]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
CHD support update.
authorToni Wilen <twilen@winuae.net>
Sun, 17 Aug 2014 17:10:11 +0000 (20:10 +0300)
committerToni Wilen <twilen@winuae.net>
Sun, 17 Aug 2014 17:10:11 +0000 (20:10 +0300)
41 files changed:
archivers/chd/astring.cpp
archivers/chd/astring.h
archivers/chd/avhuff.h [new file with mode: 0644]
archivers/chd/bitmap.h [new file with mode: 0644]
archivers/chd/bitstream.h
archivers/chd/chd.cpp
archivers/chd/chd.h
archivers/chd/chdcd.cpp [new file with mode: 0644]
archivers/chd/chdcd.h
archivers/chd/chdcdrom.cpp
archivers/chd/chdcdrom.h
archivers/chd/chdcodec.cpp
archivers/chd/chdcodec.h
archivers/chd/chdglue.cpp [new file with mode: 0644]
archivers/chd/chdtypes.h
archivers/chd/corealloc.cpp [new file with mode: 0644]
archivers/chd/corealloc.h [new file with mode: 0644]
archivers/chd/corefile.h
archivers/chd/coretmpl.h
archivers/chd/delegate.h [new file with mode: 0644]
archivers/chd/eminline.h [new file with mode: 0644]
archivers/chd/flac.cpp
archivers/chd/flac.h
archivers/chd/harddisk.cpp [new file with mode: 0644]
archivers/chd/harddisk.h [new file with mode: 0644]
archivers/chd/hashing.cpp
archivers/chd/hashing.h
archivers/chd/huffman.cpp
archivers/chd/huffman.h
archivers/chd/md5.cpp
archivers/chd/osdcomm.h [new file with mode: 0644]
archivers/chd/osdcore.h
archivers/chd/osinline.h [new file with mode: 0644]
archivers/chd/palette.h [new file with mode: 0644]
archivers/chd/sha1.cpp
archivers/chd/sha1.h
archivers/chd/windows/eivc.h [new file with mode: 0644]
archivers/chd/windows/eivcx86.h [new file with mode: 0644]
archivers/chd/windows/osinline.h [new file with mode: 0644]
archivers/chd/windows/wintime.cpp [new file with mode: 0644]
archivers/chd/windows/winwork.cpp [new file with mode: 0644]

index e74d81223044ea2e34c2b48ec9e13d6045d9b8db..e04dbd2ed799c4670a2d8cc9fd98531edfab16ca 100644 (file)
@@ -1,52 +1,23 @@
+#include "chdtypes.h"
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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>
 
+#include "astring.h"
+#include "osdcore.h"
+
 
 /***************************************************************************
     GLOBAL VARIABLES
index ca528d67c4e71bc6c85ac2fe6a9a533ec7ae4899..639591e04f25059117b4e82f9ee7a3f8bdfd6b61 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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>
+#include "osdcomm.h"
+
 
 
 //**************************************************************************
@@ -137,10 +109,10 @@ public:
        // 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; }
+       int printf(const char *format, ...) ATTR_PRINTF(2,3) { va_list ap; va_start(ap, format); int result = this->vprintf(format, ap); va_end(ap); return result; }
+       int catprintf(const char *format, ...) ATTR_PRINTF(2,3) { va_list ap; va_start(ap, format); int result = catvprintf(format, ap); va_end(ap); return result; }
+       astring &format(const char *format, ...) ATTR_PRINTF(2,3) { va_list ap; va_start(ap, format); this->vprintf(format, ap); va_end(ap); return *this; }
+       astring &catformat(const char *format, ...) ATTR_PRINTF(2,3) { 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;
@@ -179,10 +151,10 @@ private:
        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;
+       char *          m_text;
+       int             m_alloclen;
+       char            m_smallbuf[64];
+       int             m_len;
 };
 
 
diff --git a/archivers/chd/avhuff.h b/archivers/chd/avhuff.h
new file mode 100644 (file)
index 0000000..efc3c71
--- /dev/null
@@ -0,0 +1,202 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    avhuff.h
+
+    Audio/video compression and decompression helpers.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __AVHUFF_H__
+#define __AVHUFF_H__
+
+#include "osdcore.h"
+#include "coretmpl.h"
+#include "bitmap.h"
+#include "huffman.h"
+#include "flac.h"
+
+
+//**************************************************************************
+//  CONSTANTS
+//**************************************************************************
+
+#define AVHUFF_USE_FLAC     (1)
+
+
+// errors
+enum avhuff_error
+{
+       AVHERR_NONE = 0,
+       AVHERR_INVALID_DATA,
+       AVHERR_VIDEO_TOO_LARGE,
+       AVHERR_AUDIO_TOO_LARGE,
+       AVHERR_METADATA_TOO_LARGE,
+       AVHERR_OUT_OF_MEMORY,
+       AVHERR_COMPRESSION_ERROR,
+       AVHERR_TOO_MANY_CHANNELS,
+       AVHERR_INVALID_CONFIGURATION,
+       AVHERR_INVALID_PARAMETER,
+       AVHERR_BUFFER_TOO_SMALL
+};
+
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> av_codec_decompress_config
+
+// decompression configuration
+class avhuff_decompress_config
+{
+public:
+       avhuff_decompress_config()
+               : maxsamples(0),
+                       actsamples(NULL),
+                       maxmetalength(0),
+                       actmetalength(NULL),
+                       metadata(NULL)
+       {
+               memset(audio, 0, sizeof(audio));
+       }
+
+       bitmap_yuy16 video;                     // pointer to video bitmap
+       UINT32      maxsamples;                 // maximum number of samples per channel
+       UINT32 *    actsamples;                 // actual number of samples per channel
+       INT16 *     audio[16];                  // pointer to individual audio channels
+       UINT32      maxmetalength;              // maximum length of metadata
+       UINT32 *    actmetalength;              // actual length of metadata
+       UINT8 *     metadata;                   // pointer to metadata buffer
+};
+
+
+// ======================> avhuff_encoder
+
+// core state for the codec
+class avhuff_encoder
+{
+public:
+       // construction/destruction
+       avhuff_encoder();
+
+       // encode/decode
+       avhuff_error encode_data(const UINT8 *source, UINT8 *dest, UINT32 &complength);
+
+       // static helpers
+       static UINT32 raw_data_size(const UINT8 *data);
+       static UINT32 raw_data_size(UINT32 width, UINT32 height, UINT8 channels, UINT32 numsamples, UINT32 metadatasize = 0) { return 12 + channels * numsamples * 2 + width * height * 2; }
+       static avhuff_error assemble_data(dynamic_buffer &buffer, bitmap_yuy16 &bitmap, UINT8 channels, UINT32 numsamples, INT16 **samples, UINT8 *metadata = NULL, UINT32 metadatasize = 0);
+
+private:
+       // delta-RLE Huffman encoder
+       class deltarle_encoder
+       {
+       public:
+               // construction/destruction
+               deltarle_encoder()
+                       : m_rlecount(0) { }
+
+               // histogramming
+               UINT16 *rle_and_histo_bitmap(const UINT8 *source, UINT32 items_per_row, UINT32 item_advance, UINT32 row_count);
+
+               // encoding
+               void flush_rle() { m_rlecount = 0; }
+               void encode_one(bitstream_out &bitbuf, UINT16 *&rleptr);
+               huffman_error export_tree_rle(bitstream_out &bitbuf) { return m_encoder.export_tree_rle(bitbuf); }
+
+       private:
+               // internal state
+               int                         m_rlecount;
+               huffman_encoder<256 + 16>   m_encoder;
+               dynamic_array<UINT16>       m_rlebuffer;
+       };
+
+       // internal helpers
+       avhuff_error encode_audio(const UINT8 *source, int channels, int samples, UINT8 *dest, UINT8 *sizes);
+       avhuff_error encode_video(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength);
+       avhuff_error encode_video_lossless(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength);
+
+       // video encoding contexts
+       deltarle_encoder            m_ycontext;
+       deltarle_encoder            m_cbcontext;
+       deltarle_encoder            m_crcontext;
+
+       // audio encoding contexts
+       dynamic_buffer              m_audiobuffer;
+#if AVHUFF_USE_FLAC
+       flac_encoder                m_flac_encoder;
+#else
+       huffman_8bit_encoder        m_audiohi_encoder;
+       huffman_8bit_encoder        m_audiolo_encoder;
+#endif
+};
+
+
+// ======================> avhuff_decoder
+
+// core state for the codec
+class avhuff_decoder
+{
+public:
+       // construction/destruction
+       avhuff_decoder();
+
+       // configuration
+       void configure(const avhuff_decompress_config &config);
+
+       // encode/decode
+       avhuff_error decode_data(const UINT8 *source, UINT32 complength, UINT8 *dest);
+
+private:
+       // delta-RLE Huffman decoder
+       class deltarle_decoder
+       {
+       public:
+               // construction/destruction
+               deltarle_decoder()
+                       : m_rlecount(0), m_prevdata(0) { }
+
+               // general
+               void reset() { m_rlecount = m_prevdata = 0; }
+
+               // decoding
+               void flush_rle() { m_rlecount = 0; }
+               UINT32 decode_one(bitstream_in &bitbuf);
+               huffman_error import_tree_rle(bitstream_in &bitbuf) { return m_decoder.import_tree_rle(bitbuf); }
+
+       private:
+               // internal state
+               int                         m_rlecount;
+               UINT8                       m_prevdata;
+               huffman_decoder<256 + 16>   m_decoder;
+       };
+
+
+       // internal helpers
+       avhuff_error decode_audio(int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes);
+       avhuff_error decode_video(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor);
+       avhuff_error decode_video_lossless(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor);
+
+       // internal state
+       avhuff_decompress_config    m_config;
+
+       // video decoding contexts
+       deltarle_decoder            m_ycontext;
+       deltarle_decoder            m_cbcontext;
+       deltarle_decoder            m_crcontext;
+
+       // audio decoding contexts
+       huffman_8bit_decoder        m_audiohi_decoder;
+       huffman_8bit_decoder        m_audiolo_decoder;
+#if AVHUFF_USE_FLAC
+       flac_decoder                m_flac_decoder;
+#endif
+};
+
+
+#endif
diff --git a/archivers/chd/bitmap.h b/archivers/chd/bitmap.h
new file mode 100644 (file)
index 0000000..a82ec7e
--- /dev/null
@@ -0,0 +1,412 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    bitmap.h
+
+    Core bitmap routines.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __BITMAP_H__
+#define __BITMAP_H__
+
+#include "osdcore.h"
+#include "palette.h"
+#include <assert.h>
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// bitmap_format describes the various bitmap formats we use
+enum bitmap_format
+{
+       BITMAP_FORMAT_INVALID = 0,      // invalid forma
+       BITMAP_FORMAT_IND8,             // 8bpp indexed
+       BITMAP_FORMAT_IND16,            // 16bpp indexed
+       BITMAP_FORMAT_IND32,            // 32bpp indexed
+       BITMAP_FORMAT_IND64,            // 64bpp indexed
+       BITMAP_FORMAT_RGB32,            // 32bpp 8-8-8 RGB
+       BITMAP_FORMAT_ARGB32,           // 32bpp 8-8-8-8 ARGB
+       BITMAP_FORMAT_YUY16,            // 16bpp 8-8 Y/Cb, Y/Cr in sequence
+       BITMAP_FORMAT_LAST
+};
+
+
+// ======================> rectangle
+
+// rectangles describe a bitmap portion
+class rectangle
+{
+public:
+       // construction/destruction
+       rectangle()
+               : min_x(0), max_x(0), min_y(0), max_y(0) { }
+       rectangle(INT32 minx, INT32 maxx, INT32 miny, INT32 maxy)
+               : min_x(minx), max_x(maxx), min_y(miny), max_y(maxy) { }
+
+       // getters
+       INT32 left() const { return min_x; }
+       INT32 right() const { return max_x; }
+       INT32 top() const { return min_y; }
+       INT32 bottom() const { return max_y; }
+
+       // compute intersection with another rect
+       rectangle &operator&=(const rectangle &src)
+       {
+               if (src.min_x > min_x) min_x = src.min_x;
+               if (src.max_x < max_x) max_x = src.max_x;
+               if (src.min_y > min_y) min_y = src.min_y;
+               if (src.max_y < max_y) max_y = src.max_y;
+               return *this;
+       }
+
+       // compute union with another rect
+       rectangle &operator|=(const rectangle &src)
+       {
+               if (src.min_x < min_x) min_x = src.min_x;
+               if (src.max_x > max_x) max_x = src.max_x;
+               if (src.min_y < min_y) min_y = src.min_y;
+               if (src.max_y > max_y) max_y = src.max_y;
+               return *this;
+       }
+
+       // comparisons
+       bool operator==(const rectangle &rhs) const { return min_x == rhs.min_x && max_x == rhs.max_x && min_y == rhs.min_y && max_y == rhs.max_y; }
+       bool operator!=(const rectangle &rhs) const { return min_x != rhs.min_x || max_x != rhs.max_x || min_y != rhs.min_y || max_y != rhs.max_y; }
+       bool operator>(const rectangle &rhs) const { return min_x < rhs.min_x && min_y < rhs.min_y && max_x > rhs.max_x && max_y > rhs.max_y; }
+       bool operator>=(const rectangle &rhs) const { return min_x <= rhs.min_x && min_y <= rhs.min_y && max_x >= rhs.max_x && max_y >= rhs.max_y; }
+       bool operator<(const rectangle &rhs) const { return min_x >= rhs.min_x || min_y >= rhs.min_y || max_x <= rhs.max_x || max_y <= rhs.max_y; }
+       bool operator<=(const rectangle &rhs) const { return min_x > rhs.min_x || min_y > rhs.min_y || max_x < rhs.max_x || max_y < rhs.max_y; }
+
+       // other helpers
+       bool empty() const { return (min_x > max_x || min_y > max_y); }
+       bool contains(INT32 x, INT32 y) const { return (x >= min_x && x <= max_x && y >= min_y && y <= max_y); }
+       bool contains(const rectangle &rect) const { return (min_x <= rect.min_x && max_x >= rect.max_x && min_y <= rect.min_y && max_y >= rect.max_y); }
+       INT32 width() const { return max_x + 1 - min_x; }
+       INT32 height() const { return max_y + 1 - min_y; }
+       INT32 xcenter() const { return (min_x + max_x + 1) / 2; }
+       INT32 ycenter() const { return (min_y + max_y + 1) / 2; }
+
+       // setters
+       void set(INT32 minx, INT32 maxx, INT32 miny, INT32 maxy) { min_x = minx; max_x = maxx; min_y = miny; max_y = maxy; }
+       void setx(INT32 minx, INT32 maxx) { min_x = minx; max_x = maxx; }
+       void sety(INT32 miny, INT32 maxy) { min_y = miny; max_y = maxy; }
+       void set_width(INT32 width) { max_x = min_x + width - 1; }
+       void set_height(INT32 height) { max_y = min_y + height - 1; }
+       void set_origin(INT32 x, INT32 y) { max_x += x - min_x; max_y += y - min_y; min_x = x; min_y = y; }
+       void set_size(INT32 width, INT32 height) { set_width(width); set_height(height); }
+
+       // offset helpers
+       void offset(INT32 xdelta, INT32 ydelta) { min_x += xdelta; max_x += xdelta; min_y += ydelta; max_y += ydelta; }
+       void offsetx(INT32 delta) { min_x += delta; max_x += delta; }
+       void offsety(INT32 delta) { min_y += delta; max_y += delta; }
+
+       // internal state
+       INT32           min_x;          // minimum X, or left coordinate
+       INT32           max_x;          // maximum X, or right coordinate (inclusive)
+       INT32           min_y;          // minimum Y, or top coordinate
+       INT32           max_y;          // maximum Y, or bottom coordinate (inclusive)
+};
+
+
+// ======================> bitmap_t
+
+// bitmaps describe a rectangular array of pixels
+class bitmap_t
+{
+private:
+       // prevent implicit copying
+       bitmap_t(const bitmap_t &);
+       bitmap_t &operator=(const bitmap_t &);
+
+protected:
+       // construction/destruction -- subclasses only to ensure type correctness
+       bitmap_t(bitmap_format format, int bpp, int width = 0, int height = 0, int xslop = 0, int yslop = 0);
+       bitmap_t(bitmap_format format, int bpp, void *base, int width, int height, int rowpixels);
+       bitmap_t(bitmap_format format, int bpp, bitmap_t &source, const rectangle &subrect);
+       virtual ~bitmap_t();
+
+public:
+       // allocation/deallocation
+       void reset();
+
+       // getters
+       INT32 width() const { return m_width; }
+       INT32 height() const { return m_height; }
+       INT32 rowpixels() const { return m_rowpixels; }
+       INT32 rowbytes() const { return m_rowpixels * m_bpp / 8; }
+       UINT8 bpp() const { return m_bpp; }
+       bitmap_format format() const { return m_format; }
+       bool valid() const { return (m_base != NULL); }
+       palette_t *palette() const { return m_palette; }
+       const rectangle &cliprect() const { return m_cliprect; }
+
+       // allocation/sizing
+       void allocate(int width, int height, int xslop = 0, int yslop = 0);
+       void resize(int width, int height, int xslop = 0, int yslop = 0);
+
+       // operations
+       void set_palette(palette_t *palette);
+       void fill(UINT32 color) { fill(color, m_cliprect); }
+       void fill(UINT32 color, const rectangle &cliprect);
+       void plot_box(int x, int y, int width, int height, UINT32 color)
+       {
+               rectangle clip(x, x + width - 1, y, y + height - 1);
+               fill(color, clip);
+       }
+
+       // pixel access
+       template<typename _PixelType>
+       _PixelType &pixt(INT32 y, INT32 x = 0) const { return *(reinterpret_cast<_PixelType *>(m_base) + y * m_rowpixels + x); }
+       void *raw_pixptr(INT32 y, INT32 x = 0) const { return reinterpret_cast<UINT8 *>(m_base) + (y * m_rowpixels + x) * m_bpp / 8; }
+
+protected:
+       // for use by subclasses only to ensure type correctness
+       void wrap(void *base, int width, int height, int rowpixels);
+       void wrap(const bitmap_t &source, const rectangle &subrect);
+
+private:
+       // internal helpers
+       INT32 compute_rowpixels(int width, int xslop);
+       void compute_base(int xslop, int yslop);
+
+       // internal state
+       UINT8 *         m_alloc;        // pointer to allocated pixel memory
+       UINT32          m_allocbytes;   // size of our allocation
+       void *          m_base;         // pointer to pixel (0,0) (adjusted for padding)
+       INT32           m_rowpixels;    // pixels per row (including padding)
+       INT32           m_width;        // width of the bitmap
+       INT32           m_height;       // height of the bitmap
+       bitmap_format   m_format;       // format of the bitmap
+       UINT8           m_bpp;          // bits per pixel
+       palette_t *     m_palette;      // optional palette
+       rectangle       m_cliprect;     // a clipping rectangle covering the full bitmap
+};
+
+
+// ======================> bitmap8_t, bitmap16_t, bitmap32_t, bitmap64_t
+
+// 8bpp bitmaps
+class bitmap8_t : public bitmap_t
+{
+private:
+       // private helpers
+       bool valid_format(bitmap_format format) const { return (format == BITMAP_FORMAT_IND8); }
+
+protected:
+       // construction/destruction -- subclasses only
+       bitmap8_t(bitmap_format format, int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap_t(format, 8, width, height, xslop, yslop) { }
+       bitmap8_t(bitmap_format format, UINT8 *base, int width, int height, int rowpixels) : bitmap_t(format, 8, base, width, height, rowpixels) { assert(valid_format(format)); }
+       bitmap8_t(bitmap_format format, bitmap8_t &source, const rectangle &subrect) : bitmap_t(format, 8, source, subrect) { }
+
+public:
+       // getters
+       UINT8 bpp() const { return 8; }
+
+       // pixel accessors
+       typedef UINT8 pixel_t;
+       pixel_t &pix(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+       pixel_t &pix8(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+};
+
+// 16bpp bitmaps
+class bitmap16_t : public bitmap_t
+{
+private:
+       // private helpers
+       bool valid_format(bitmap_format format) const { return (format == BITMAP_FORMAT_IND16 || format == BITMAP_FORMAT_YUY16); }
+
+protected:
+       // construction/destruction -- subclasses only
+       bitmap16_t(bitmap_format format, int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap_t(format, 16, width, height, xslop, yslop) { assert(valid_format(format)); }
+       bitmap16_t(bitmap_format format, UINT16 *base, int width, int height, int rowpixels) : bitmap_t(format, 16, base, width, height, rowpixels) { assert(valid_format(format)); }
+       bitmap16_t(bitmap_format format, bitmap16_t &source, const rectangle &subrect) : bitmap_t(format, 16, source, subrect) { }
+
+public:
+       // getters
+       UINT8 bpp() const { return 16; }
+
+       // pixel accessors
+       typedef UINT16 pixel_t;
+       pixel_t &pix(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+       pixel_t &pix16(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+};
+
+// 32bpp bitmaps
+class bitmap32_t : public bitmap_t
+{
+private:
+       // private helpers
+       bool valid_format(bitmap_format format) const { return (format == BITMAP_FORMAT_IND32 || format == BITMAP_FORMAT_RGB32 || format == BITMAP_FORMAT_ARGB32); }
+
+protected:
+       // construction/destruction -- subclasses only
+       bitmap32_t(bitmap_format format, int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap_t(format, 32, width, height, xslop, yslop) { assert(valid_format(format)); }
+       bitmap32_t(bitmap_format format, UINT32 *base, int width, int height, int rowpixels) : bitmap_t(format, 32, base, width, height, rowpixels) { assert(valid_format(format)); }
+       bitmap32_t(bitmap_format format, bitmap32_t &source, const rectangle &subrect) : bitmap_t(format, 32, source, subrect) { }
+
+public:
+       // getters
+       UINT8 bpp() const { return 32; }
+
+       // pixel accessors
+       typedef UINT32 pixel_t;
+       pixel_t &pix(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+       pixel_t &pix32(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+};
+
+// 64bpp bitmaps
+class bitmap64_t : public bitmap_t
+{
+private:
+       // private helpers
+       bool valid_format(bitmap_format format) const { return (format == BITMAP_FORMAT_IND64); }
+
+protected:
+       // construction/destruction -- subclasses only
+       bitmap64_t(bitmap_format format, int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap_t(format, 64, width, height, xslop, yslop) { assert(valid_format(format)); }
+       bitmap64_t(bitmap_format format, UINT64 *base, int width, int height, int rowpixels) : bitmap_t(format, 64, base, width, height, rowpixels) { assert(valid_format(format)); }
+       bitmap64_t(bitmap_format format, bitmap64_t &source, const rectangle &subrect) : bitmap_t(format, 64, source, subrect) { }
+
+public:
+       // getters
+       UINT8 bpp() const { return 64; }
+
+       // pixel accessors
+       typedef UINT64 pixel_t;
+       pixel_t &pix(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+       pixel_t &pix64(INT32 y, INT32 x = 0) const { return pixt<pixel_t>(y, x); }
+};
+
+
+// ======================> bitmap_ind8, bitmap_ind16, bitmap_ind32, bitmap_ind64
+
+// BITMAP_FORMAT_IND8 bitmaps
+class bitmap_ind8 : public bitmap8_t
+{
+       static const bitmap_format k_bitmap_format = BITMAP_FORMAT_IND8;
+
+public:
+       // construction/destruction
+       bitmap_ind8(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap8_t(k_bitmap_format, width, height, xslop, yslop) { }
+       bitmap_ind8(UINT8 *base, int width, int height, int rowpixels) : bitmap8_t(k_bitmap_format, base, width, height, rowpixels) { }
+       bitmap_ind8(bitmap_ind8 &source, const rectangle &subrect) : bitmap8_t(k_bitmap_format, source, subrect) { }
+       void wrap(UINT8 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
+       void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+
+       // getters
+       bitmap_format format() const { return k_bitmap_format; }
+};
+
+// BITMAP_FORMAT_IND16 bitmaps
+class bitmap_ind16 : public bitmap16_t
+{
+       static const bitmap_format k_bitmap_format = BITMAP_FORMAT_IND16;
+
+public:
+       // construction/destruction
+       bitmap_ind16(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap16_t(k_bitmap_format, width, height, xslop, yslop) { }
+       bitmap_ind16(UINT16 *base, int width, int height, int rowpixels) : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
+       bitmap_ind16(bitmap_ind16 &source, const rectangle &subrect) : bitmap16_t(k_bitmap_format, source, subrect) { }
+       void wrap(UINT16 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
+       void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+
+       // getters
+       bitmap_format format() const { return k_bitmap_format; }
+};
+
+// BITMAP_FORMAT_IND32 bitmaps
+class bitmap_ind32 : public bitmap32_t
+{
+       static const bitmap_format k_bitmap_format = BITMAP_FORMAT_IND32;
+
+public:
+       // construction/destruction
+       bitmap_ind32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
+       bitmap_ind32(UINT32 *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
+       bitmap_ind32(bitmap_ind32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
+       void wrap(UINT32 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
+       void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+
+       // getters
+       bitmap_format format() const { return k_bitmap_format; }
+};
+
+// BITMAP_FORMAT_IND64 bitmaps
+class bitmap_ind64 : public bitmap64_t
+{
+       static const bitmap_format k_bitmap_format = BITMAP_FORMAT_IND64;
+
+public:
+       // construction/destruction
+       bitmap_ind64(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap64_t(k_bitmap_format, width, height, xslop, yslop) { }
+       bitmap_ind64(UINT64 *base, int width, int height, int rowpixels) : bitmap64_t(k_bitmap_format, base, width, height, rowpixels) { }
+       bitmap_ind64(bitmap_ind64 &source, const rectangle &subrect) : bitmap64_t(k_bitmap_format, source, subrect) { }
+       void wrap(UINT64 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
+       void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+
+       // getters
+       bitmap_format format() const { return k_bitmap_format; }
+};
+
+
+// ======================> bitmap_yuy16, bitmap_rgb32, bitmap_argb32
+
+// BITMAP_FORMAT_YUY16 bitmaps
+class bitmap_yuy16 : public bitmap16_t
+{
+       static const bitmap_format k_bitmap_format = BITMAP_FORMAT_YUY16;
+
+public:
+       // construction/destruction
+       bitmap_yuy16(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap16_t(k_bitmap_format, width, height, xslop, yslop) { }
+       bitmap_yuy16(UINT16 *base, int width, int height, int rowpixels) : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
+       bitmap_yuy16(bitmap_yuy16 &source, const rectangle &subrect) : bitmap16_t(k_bitmap_format, source, subrect) { }
+       void wrap(UINT16 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
+       void wrap(const bitmap_yuy16 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+
+       // getters
+       bitmap_format format() const { return k_bitmap_format; }
+};
+
+// BITMAP_FORMAT_RGB32 bitmaps
+class bitmap_rgb32 : public bitmap32_t
+{
+       static const bitmap_format k_bitmap_format = BITMAP_FORMAT_RGB32;
+
+public:
+       // construction/destruction
+       bitmap_rgb32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
+       bitmap_rgb32(UINT32 *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
+       bitmap_rgb32(bitmap_rgb32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
+       void wrap(UINT32 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
+       void wrap(const bitmap_rgb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+
+       // getters
+       bitmap_format format() const { return k_bitmap_format; }
+};
+
+// BITMAP_FORMAT_ARGB32 bitmaps
+class bitmap_argb32 : public bitmap32_t
+{
+       static const bitmap_format k_bitmap_format = BITMAP_FORMAT_ARGB32;
+
+public:
+       // construction/destruction
+       bitmap_argb32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
+       bitmap_argb32(UINT32 *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
+       bitmap_argb32(bitmap_argb32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
+       void wrap(UINT32 *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
+       void wrap(const bitmap_argb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+
+       // getters
+       bitmap_format format() const { return k_bitmap_format; }
+};
+
+
+#endif  // __BITMAP_H__
index 1c1859199d7974ed54578288a79dff750ece4552..2f36271d2960649b33ce4a766d88430b22f7b201 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
@@ -68,11 +39,11 @@ public:
 
 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
+       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
 };
 
 
@@ -92,11 +63,11 @@ public:
 
 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
+       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
 };
 
 
@@ -111,10 +82,10 @@ private:
 
 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)
+               m_bits(0),
+               m_read(reinterpret_cast<const UINT8 *>(src)),
+               m_doffset(0),
+               m_dlength(srclength)
 {
 }
 
@@ -126,6 +97,9 @@ inline bitstream_in::bitstream_in(const void *src, UINT32 srclength)
 
 inline UINT32 bitstream_in::peek(int numbits)
 {
+       if (numbits == 0)
+               return 0;
+
        // fetch data if we need more
        if (numbits > m_bits)
        {
@@ -206,10 +180,10 @@ inline UINT32 bitstream_in::flush()
 
 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)
+               m_bits(0),
+               m_write(reinterpret_cast<UINT8 *>(dest)),
+               m_doffset(0),
+               m_dlength(destlength)
 {
 }
 
@@ -234,7 +208,10 @@ inline void bitstream_out::write(UINT32 newbits, int numbits)
                }
 
        // shift the bits to the top
-       newbits <<= 32 - numbits;
+       if (numbits == 0)
+               newbits = 0;
+       else
+               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;
index 14d7d5e16bbabd7433c11b8379fa8803f18ead8d..bc0af0769106749ffc91b41d02381e3cb627d199 100644 (file)
@@ -1,12 +1,32 @@
-
 #include "chdtypes.h"
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    chd.c
+
+    MAME Compressed Hunks of Data file format
+
+***************************************************************************/
 
 #include "chd.h"
+#include "avhuff.h"
 #include "hashing.h"
+#include "flac.h"
 #include "chdcdrom.h"
 #include "coretmpl.h"
-#include "bitstream.h"
-#include "huffman.h"
+#include "chdcodec.h"
+
+#include <zlib.h>
+#include <time.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <new>
+
+
+//**************************************************************************
+//  CONSTANTS
+//**************************************************************************
 
 // standard metadata formats
 const char *HARD_DISK_METADATA_FORMAT = "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d";
@@ -15,48 +35,49 @@ const char *CDROM_TRACK_METADATA2_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%
 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 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
+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)
+       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
+       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
+       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
 //**************************************************************************
@@ -66,12 +87,12 @@ enum
 // 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
+       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
 };
 
 
@@ -79,11 +100,16 @@ struct chd_file::metadata_entry
 
 struct chd_file::metadata_hash
 {
-       UINT8                                   tag[4];                 // tag of the metadata in big-endian
-       sha1_t                                  sha1;                   // hash data
+       UINT8                   tag[4];         // tag of the metadata in big-endian
+       sha1_t                  sha1;           // hash data
 };
 
 
+
+//**************************************************************************
+//  INLINE FUNCTIONS
+//**************************************************************************
+
 //-------------------------------------------------
 //  be_read - extract a big-endian number from
 //  a byte buffer
@@ -97,6 +123,7 @@ inline UINT64 chd_file::be_read(const UINT8 *base, int numbytes)
        return result;
 }
 
+
 //-------------------------------------------------
 //  be_write - write a big-endian number to a byte
 //  buffer
@@ -112,6 +139,7 @@ inline void chd_file::be_write(UINT8 *base, UINT64 value, int numbytes)
        }
 }
 
+
 //-------------------------------------------------
 //  be_read_sha1 - fetch a sha1_t from a data
 //  stream in bigendian order
@@ -124,6 +152,18 @@ inline sha1_t chd_file::be_read_sha1(const UINT8 *base)
        return result;
 }
 
+
+//-------------------------------------------------
+//  be_write_sha1 - write a sha1_t to a data
+//  stream in bigendian order
+//-------------------------------------------------
+
+inline void chd_file::be_write_sha1(UINT8 *base, sha1_t value)
+{
+       memcpy(base, &value.m_raw[0], sizeof(value.m_raw));
+}
+
+
 //-------------------------------------------------
 //  file_read - read from the file at the given
 //  offset; on failure throw an error
@@ -136,12 +176,191 @@ inline void chd_file::file_read(UINT64 offset, void *dest, UINT32 length)
                throw CHDERR_NOT_OPEN;
 
        // seek and read
-       zfile_fseek(m_file, offset, SEEK_SET);
-       UINT32 count = zfile_fread(dest, 1, length, m_file);
+       core_fseek(m_file, offset, SEEK_SET);
+       UINT32 count = core_fread(m_file, dest, length);
+       if (count != length)
+               throw CHDERR_READ_ERROR;
+}
+
+
+//-------------------------------------------------
+//  file_write - write to the file at the given
+//  offset; on failure throw an error
+//-------------------------------------------------
+
+inline void chd_file::file_write(UINT64 offset, const void *source, UINT32 length)
+{
+       // no file = failure
+       if (m_file == NULL)
+               throw CHDERR_NOT_OPEN;
+
+       // seek and write
+       core_fseek(m_file, offset, SEEK_SET);
+       UINT32 count = core_fwrite(m_file, source, length);
+       if (count != length)
+               throw CHDERR_WRITE_ERROR;
+}
+
+
+//-------------------------------------------------
+//  file_append - append to the file at the given
+//  offset, ensuring we start at the given
+//  alignment; on failure throw an error
+//-------------------------------------------------
+
+inline UINT64 chd_file::file_append(const void *source, UINT32 length, UINT32 alignment)
+{
+       // no file = failure
+       if (m_file == NULL)
+               throw CHDERR_NOT_OPEN;
+
+       // seek to the end and align if necessary
+       core_fseek(m_file, 0, SEEK_END);
+       if (alignment != 0)
+       {
+               UINT64 offset = core_ftell(m_file);
+               UINT32 delta = offset % alignment;
+               if (delta != 0)
+               {
+                       // pad with 0's from a local buffer
+                       UINT8 buffer[1024];
+                       memset(buffer, 0, sizeof(buffer));
+                       delta = alignment - delta;
+                       while (delta != 0)
+                       {
+                               UINT32 bytes_to_write = MIN(sizeof(buffer), delta);
+                               UINT32 count = core_fwrite(m_file, buffer, bytes_to_write);
+                               if (count != bytes_to_write)
+                                       throw CHDERR_WRITE_ERROR;
+                               delta -= bytes_to_write;
+                       }
+               }
+       }
+
+       // write the real data
+       UINT64 offset = core_ftell(m_file);
+       UINT32 count = core_fwrite(m_file, source, length);
        if (count != length)
                throw CHDERR_READ_ERROR;
+       return offset;
+}
+
+
+//-------------------------------------------------
+//  bits_for_value - return the number of bits
+//  necessary to represent all numbers 0..value
+//-------------------------------------------------
+
+inline UINT8 chd_file::bits_for_value(UINT64 value)
+{
+       UINT8 result = 0;
+       while (value != 0)
+               value >>= 1, result++;
+       return result;
+}
+
+
+
+//**************************************************************************
+//  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();
+}
+
+
+//-------------------------------------------------
+//  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;
+       }
 }
 
+
 //-------------------------------------------------
 //  hunk_info - return information about this
 //  hunk
@@ -241,88 +460,275 @@ chd_error chd_file::hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32
        return CHDERR_NONE;
 }
 
+
 //-------------------------------------------------
-//  open - open an existing file for read or
-//  read/write
+//  set_raw_sha1 - set our SHA1 values
+//-------------------------------------------------
+
+void chd_file::set_raw_sha1(sha1_t rawdata)
+{
+       // create a big-endian version
+       UINT8 rawbuf[sizeof(sha1_t)];
+       be_write_sha1(rawbuf, rawdata);
+
+       // write to the header
+       UINT64 offset = (m_rawsha1_offset != 0) ? m_rawsha1_offset : m_sha1_offset;
+       assert(offset != 0);
+       file_write(offset, rawbuf, sizeof(rawbuf));
+
+       // if we have a separate rawsha1_offset, update the full sha1 as well
+       if (m_rawsha1_offset != 0)
+               metadata_update_hash();
+}
+
+
 //-------------------------------------------------
+//  set_parent_sha1 - set the parent SHA1 value
+//-------------------------------------------------
+
+void chd_file::set_parent_sha1(sha1_t parent)
+{
+       // if no file, fail
+       if (m_file == NULL)
+               throw CHDERR_INVALID_FILE;
+
+       // create a big-endian version
+       UINT8 rawbuf[sizeof(sha1_t)];
+       be_write_sha1(rawbuf, parent);
+
+       // write to the header
+       assert(m_parentsha1_offset != 0);
+       file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf));
+}
 
-chd_error chd_file::open(struct zfile *file, bool writeable, chd_file *parent)
+
+//-------------------------------------------------
+//  create - create a new file with no parent
+//  using an existing opened file handle
+//-------------------------------------------------
+
+chd_error chd_file::create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4])
 {
        // make sure we don't already have a file open
        if (m_file != NULL)
                return CHDERR_ALREADY_OPEN;
 
-       // open the file
-       m_file = file;
+       // set the header parameters
+       m_logicalbytes = logicalbytes;
+       m_hunkbytes = hunkbytes;
+       m_unitbytes = unitbytes;
+       memcpy(m_compression, compression, sizeof(m_compression));
+       m_parent = NULL;
+
+       // take ownership of the file
+       m_file = &file;
        m_owns_file = false;
-       m_parent = parent;
-       return open_common(writeable);
+       return create_common();
 }
 
+
 //-------------------------------------------------
-//  close - close a CHD file for access
+//  create - create a new file with a parent
+//  using an existing opened file handle
 //-------------------------------------------------
 
-void chd_file::close()
+chd_error chd_file::create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent)
 {
-       // reset file characteristics
-       if (m_owns_file && m_file != NULL)
-               zfile_fclose(m_file);
-       m_file = NULL;
+       // make sure we don't already have a file open
+       if (m_file != NULL)
+               return CHDERR_ALREADY_OPEN;
+
+       // set the header parameters
+       m_logicalbytes = logicalbytes;
+       m_hunkbytes = hunkbytes;
+       m_unitbytes = parent.unit_bytes();
+       memcpy(m_compression, compression, sizeof(m_compression));
+       m_parent = &parent;
+
+       // take ownership of the file
+       m_file = &file;
        m_owns_file = false;
-       m_allow_reads = false;
-       m_allow_writes = false;
+       return create_common();
+}
 
-       // 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;
+//-------------------------------------------------
+//  create - create a new file with no parent
+//  using a filename
+//-------------------------------------------------
 
-       // reset map information
-       m_mapentrybytes = 0;
-       m_rawmap.reset();
+chd_error chd_file::create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4])
+{
+       // make sure we don't already have a file open
+       if (m_file != NULL)
+               return CHDERR_ALREADY_OPEN;
 
-       // reset compression management
-       for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_decompressor); decompnum++)
+       // create the new file
+       core_file *file = NULL;
+       file_error filerr = core_fopen(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &file);
+       if (filerr != FILERR_NONE)
+               return CHDERR_FILE_NOT_FOUND;
+
+       // create the file normally, then claim the file
+       chd_error chderr = create(*file, logicalbytes, hunkbytes, unitbytes, compression);
+       m_owns_file = true;
+
+       // if an error happened, close and delete the file
+       if (chderr != CHDERR_NONE)
        {
-               delete m_decompressor[decompnum];
-               m_decompressor[decompnum] = NULL;
+               core_fclose(file);
+               osd_rmfile(filename);
        }
-       m_compressed.reset();
-
-       // reset caching
-       m_cache.reset();
-       m_cachehunk = ~0;
+       return chderr;
 }
 
 
 //-------------------------------------------------
-//  read - read a single hunk from the CHD file
+//  create - create a new file with a parent
+//  using a filename
 //-------------------------------------------------
 
-chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer)
+chd_error chd_file::create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent)
 {
-       // wrap this for clean reporting
-       try
-       {
-               // punt if no file
-               if (m_file == NULL)
-                       throw CHDERR_NOT_OPEN;
+       // make sure we don't already have a file open
+       if (m_file != NULL)
+               return CHDERR_ALREADY_OPEN;
+
+       // create the new file
+       core_file *file = NULL;
+       file_error filerr = core_fopen(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &file);
+       if (filerr != FILERR_NONE)
+               return CHDERR_FILE_NOT_FOUND;
+
+       // create the file normally, then claim the file
+       chd_error chderr = create(*file, logicalbytes, hunkbytes, compression, parent);
+       m_owns_file = true;
+
+       // if an error happened, close and delete the file
+       if (chderr != CHDERR_NONE)
+       {
+               core_fclose(file);
+               osd_rmfile(filename);
+       }
+       return chderr;
+}
+
+
+//-------------------------------------------------
+//  open - open an existing file for read or
+//  read/write
+//-------------------------------------------------
+
+chd_error chd_file::open(const TCHAR *filename, 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
+       UINT32 openflags = writeable ? (OPEN_FLAG_READ | OPEN_FLAG_WRITE) : OPEN_FLAG_READ;
+       core_file *file = NULL;
+       file_error filerr = core_fopen(filename, openflags, &file);
+       if (filerr != FILERR_NONE)
+               return CHDERR_FILE_NOT_FOUND;
+
+       // now open the CHD
+       chd_error err = open(*file, writeable, parent);
+       if (err != CHDERR_NONE)
+       {
+               core_fclose(file);
+               return err;
+       }
+
+       // we now own this file
+       m_owns_file = true;
+       return err;
+}
+
+
+//-------------------------------------------------
+//  open - open an existing file for read or
+//  read/write
+//-------------------------------------------------
+
+chd_error chd_file::open(core_file &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)
+               core_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)
@@ -441,6 +847,101 @@ chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer)
        }
 }
 
+
+//-------------------------------------------------
+//  write - write a single hunk to the CHD file
+//-------------------------------------------------
+
+chd_error chd_file::write_hunk(UINT32 hunknum, const 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;
+
+               // if not writeable, fail
+               if (!m_allow_writes)
+                       throw CHDERR_FILE_NOT_WRITEABLE;
+
+               // uncompressed writes only via this interface
+               if (compressed())
+                       throw CHDERR_FILE_NOT_WRITEABLE;
+
+               // see if we have allocated the space on disk for this hunk
+               UINT8 *rawmap = m_rawmap + hunknum * 4;
+               UINT32 rawentry = be_read(rawmap, 4);
+
+               // if not, allocate one now
+               if (rawentry == 0)
+               {
+                       // first make sure we need to allocate it
+                       bool all_zeros = true;
+                       const UINT32 *scan = reinterpret_cast<const UINT32 *>(buffer);
+                       for (UINT32 index = 0; index < m_hunkbytes / 4; index++)
+                               if (scan[index] != 0)
+                               {
+                                       all_zeros = false;
+                                       break;
+                               }
+
+                       // if it's all zeros, do nothing more
+                       if (all_zeros)
+                               return CHDERR_NONE;
+
+                       // append new data to the end of the file, aligning the first chunk
+                       rawentry = file_append(buffer, m_hunkbytes, m_hunkbytes) / m_hunkbytes;
+
+                       // write the map entry back
+                       be_write(rawmap, rawentry, 4);
+                       file_write(m_mapoffset + hunknum * 4, rawmap, 4);
+
+                       // update the cached hunk if we just wrote it
+                       if (hunknum == m_cachehunk && buffer != m_cache)
+                               memcpy(m_cache, buffer, m_hunkbytes);
+               }
+
+               // otherwise, just overwrite
+               else
+                       file_write(UINT64(rawentry) * UINT64(m_hunkbytes), buffer, m_hunkbytes);
+               return CHDERR_NONE;
+       }
+
+       // just return errors
+       catch (chd_error &err)
+       {
+               return err;
+       }
+}
+
+
+//-------------------------------------------------
+//  read_units - read the given number of units
+//  from the CHD
+//-------------------------------------------------
+
+chd_error chd_file::read_units(UINT64 unitnum, void *buffer, UINT32 count)
+{
+       return read_bytes(unitnum * UINT64(m_unitbytes), buffer, count * m_unitbytes);
+}
+
+
+//-------------------------------------------------
+//  write_units - write the given number of units
+//  to the CHD
+//-------------------------------------------------
+
+chd_error chd_file::write_units(UINT64 unitnum, const void *buffer, UINT32 count)
+{
+       return write_bytes(unitnum * UINT64(m_unitbytes), buffer, count * m_unitbytes);
+}
+
+
 //-------------------------------------------------
 //  read_bytes - read from the CHD at a byte level,
 //  using the cache to handle partial hunks
@@ -485,6 +986,51 @@ chd_error chd_file::read_bytes(UINT64 offset, void *buffer, UINT32 bytes)
 }
 
 
+//-------------------------------------------------
+//  write_bytes - write to the CHD at a byte level,
+//  using the cache to handle partial hunks
+//-------------------------------------------------
+
+chd_error chd_file::write_bytes(UINT64 offset, const void *buffer, UINT32 bytes)
+{
+       // iterate over hunks
+       UINT32 first_hunk = offset / m_hunkbytes;
+       UINT32 last_hunk = (offset + bytes - 1) / m_hunkbytes;
+       const UINT8 *source = reinterpret_cast<const 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 write directly to disk unless it's the cached hunk
+               chd_error err = CHDERR_NONE;
+               if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk)
+                       err = write_hunk(curhunk, source);
+
+               // otherwise, write from the cache
+               else
+               {
+                       if (curhunk != m_cachehunk)
+                       {
+                               err = read_hunk(curhunk, m_cache);
+                               if (err != CHDERR_NONE)
+                                       return err;
+                               m_cachehunk = curhunk;
+                       }
+                       memcpy(&m_cache[startoffs], source, endoffs + 1 - startoffs);
+                       err = write_hunk(curhunk, m_cache);
+               }
+
+               // handle errors and advance
+               if (err != CHDERR_NONE)
+                       return err;
+               source += endoffs + 1 - startoffs;
+       }
+       return CHDERR_NONE;
+}
+
+
 //-------------------------------------------------
 //  read_metadata - read the indexed metadata
 //  of the given type
@@ -590,46 +1136,310 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex
 
 
 //-------------------------------------------------
-//  guess_unitbytes - for older CHD formats, take
-//  a guess at the bytes/unit based on metadata
+//  write_metadata - write the indexed metadata
+//  of the given type
 //-------------------------------------------------
 
-UINT32 chd_file::guess_unitbytes()
+chd_error chd_file::write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen, UINT8 flags)
 {
-       // 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;
+       // wrap this for clean reporting
+       try
+       {
+               // must write at least 1 byte and no more than 16MB
+               if (inputlen < 1 || inputlen >= 16 * 1024 * 1024)
+                       return CHDERR_INVALID_PARAMETER;
 
-       // 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;
+               // find the entry if it already exists
+               metadata_entry metaentry;
+               bool finished = false;
+               if (metadata_find(metatag, metaindex, metaentry))
+               {
+                       // if the new data fits over the old data, just overwrite
+                       if (inputlen <= metaentry.length)
+                       {
+                               file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen);
 
-       // otherwise, just map 1:1 with the hunk size
-       return m_hunkbytes;
+                               // if the lengths don't match, we need to update the length in our header
+                               if (inputlen != metaentry.length)
+                               {
+                                       UINT8 length[3];
+                                       be_write(length, inputlen, 3);
+                                       file_write(metaentry.offset + 5, length, sizeof(length));
+                               }
+
+                               // indicate we did everything
+                               finished = true;
+                       }
+
+                       // if it doesn't fit, unlink the current entry
+                       else
+                               metadata_set_previous_next(metaentry.prev, metaentry.next);
+               }
+
+               // if not yet done, create a new entry and append
+               if (!finished)
+               {
+                       // now build us a new entry
+                       UINT8 raw_meta_header[METADATA_HEADER_SIZE];
+                       be_write(&raw_meta_header[0], metatag, 4);
+                       raw_meta_header[4] = flags;
+                       be_write(&raw_meta_header[5], (inputlen & 0x00ffffff) | (flags << 24), 3);
+                       be_write(&raw_meta_header[8], 0, 8);
+
+                       // append the new header, then the data
+                       UINT64 offset = file_append(raw_meta_header, sizeof(raw_meta_header));
+                       file_append(inputbuf, inputlen);
+
+                       // set the previous entry to point to us
+                       metadata_set_previous_next(metaentry.prev, offset);
+               }
+
+               // update the hash
+               metadata_update_hash();
+               return CHDERR_NONE;
+       }
+
+       // return any errors
+       catch (chd_error &err)
+       {
+               return err;
+       }
 }
 
 
 //-------------------------------------------------
-//  parse_v3_header - parse the header from a v3
-//  file and configure core parameters
+//  delete_metadata - remove the given metadata
+//  from the list
 //-------------------------------------------------
 
-void chd_file::parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1)
+chd_error chd_file::delete_metadata(chd_metadata_tag metatag, UINT32 metaindex)
 {
-       // verify header length
-       if (be_read(&rawheader[8], 4) != V3_HEADER_SIZE)
-               throw CHDERR_INVALID_FILE;
+       // wrap this for clean reporting
+       try
+       {
+               // find the entry
+               metadata_entry metaentry;
+               if (!metadata_find(metatag, metaindex, metaentry))
+                       throw CHDERR_METADATA_NOT_FOUND;
 
-       // 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);
+               // point the previous to the next, unlinking us
+               metadata_set_previous_next(metaentry.prev, metaentry.next);
+               return CHDERR_NONE;
+       }
+
+       // return any errors
+       catch (chd_error &err)
+       {
+               return err;
+       }
+}
+
+
+//-------------------------------------------------
+//  clone_all_metadata - clone the metadata from
+//  one CHD to a second
+//-------------------------------------------------
+
+chd_error chd_file::clone_all_metadata(chd_file &source)
+{
+       // wrap this for clean reporting
+       try
+       {
+               // iterate over metadata entries in the source
+               dynamic_buffer filedata;
+               metadata_entry metaentry;
+               metaentry.metatag = 0;
+               metaentry.length = 0;
+               metaentry.next = 0;
+               metaentry.flags = 0;
+               for (bool has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true))
+               {
+                       // read the metadata item
+                       filedata.resize(metaentry.length);
+                       source.file_read(metaentry.offset + METADATA_HEADER_SIZE, filedata, metaentry.length);
+
+                       // write it to the destination
+                       chd_error err = write_metadata(metaentry.metatag, (UINT32)-1, filedata, metaentry.length, metaentry.flags);
+                       if (err != CHDERR_NONE)
+                               throw err;
+               }
+               return CHDERR_NONE;
+       }
+
+       // return any errors
+       catch (chd_error &err)
+       {
+               return err;
+       }
+}
+
+
+//-------------------------------------------------
+//  compute_overall_sha1 - iterate through the
+//  metadata and compute the overall hash of the
+//  CHD file
+//-------------------------------------------------
+
+sha1_t chd_file::compute_overall_sha1(sha1_t rawsha1)
+{
+       // only works for v4 and above
+       if (m_version < 4)
+               return rawsha1;
+
+       // iterate over metadata
+       dynamic_buffer filedata;
+       dynamic_array<metadata_hash> hasharray;
+       metadata_entry metaentry;
+       for (bool has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true))
+       {
+               // if not checksumming, continue
+               if ((metaentry.flags & CHD_MDFLAGS_CHECKSUM) == 0)
+                       continue;
+
+               // allocate memory and read the data
+               filedata.resize(metaentry.length);
+               file_read(metaentry.offset + METADATA_HEADER_SIZE, filedata, metaentry.length);
+
+               // create an entry for this metadata and add it
+               metadata_hash hashentry;
+               be_write(hashentry.tag, metaentry.metatag, 4);
+               hashentry.sha1 = sha1_creator::simple(filedata, metaentry.length);
+               hasharray.append(hashentry);
+       }
+
+       // sort the array
+       if (hasharray.count() != 0)
+               qsort(&hasharray[0], hasharray.count(), sizeof(hasharray[0]), metadata_hash_compare);
+
+       // read the raw data hash from our header and start a new SHA1 with that data
+       sha1_creator overall_sha1;
+       overall_sha1.append(&rawsha1, sizeof(rawsha1));
+       if (hasharray.count() != 0)
+               overall_sha1.append(&hasharray[0], hasharray.count() * sizeof(hasharray[0]));
+       return overall_sha1.finish();
+}
+
+
+//-------------------------------------------------
+//  codec_config - set internal codec parameters
+//-------------------------------------------------
+
+chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *config)
+{
+       // wrap this for clean reporting
+       try
+       {
+               // find the codec and call its configuration
+               for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compression); codecnum++)
+                       if (m_compression[codecnum] == codec)
+                       {
+                               m_decompressor[codecnum]->configure(param, config);
+                               return CHDERR_NONE;
+                       }
+               return CHDERR_INVALID_PARAMETER;
+       }
+
+       // return any errors
+       catch (chd_error &err)
+       {
+               return err;
+       }
+}
+
+
+//-------------------------------------------------
+//  error_string - return an error string for
+//  the given CHD error
+//-------------------------------------------------
+
+const char *chd_file::error_string(chd_error err)
+{
+       switch (err)
+       {
+               case CHDERR_NONE:                       return "no error";
+               case CHDERR_NO_INTERFACE:               return "no drive interface";
+               case CHDERR_OUT_OF_MEMORY:              return "out of memory";
+               case CHDERR_NOT_OPEN:                   return "file not open";
+               case CHDERR_ALREADY_OPEN:               return "file already open";
+               case CHDERR_INVALID_FILE:               return "invalid file";
+               case CHDERR_INVALID_PARAMETER:          return "invalid parameter";
+               case CHDERR_INVALID_DATA:               return "invalid data";
+               case CHDERR_FILE_NOT_FOUND:             return "file not found";
+               case CHDERR_REQUIRES_PARENT:            return "requires parent";
+               case CHDERR_FILE_NOT_WRITEABLE:         return "file not writeable";
+               case CHDERR_READ_ERROR:                 return "read error";
+               case CHDERR_WRITE_ERROR:                return "write error";
+               case CHDERR_CODEC_ERROR:                return "codec error";
+               case CHDERR_INVALID_PARENT:             return "invalid parent";
+               case CHDERR_HUNK_OUT_OF_RANGE:          return "hunk out of range";
+               case CHDERR_DECOMPRESSION_ERROR:        return "decompression error";
+               case CHDERR_COMPRESSION_ERROR:          return "compression error";
+               case CHDERR_CANT_CREATE_FILE:           return "can't create file";
+               case CHDERR_CANT_VERIFY:                return "can't verify file";
+               case CHDERR_NOT_SUPPORTED:              return "operation not supported";
+               case CHDERR_METADATA_NOT_FOUND:         return "can't find metadata";
+               case CHDERR_INVALID_METADATA_SIZE:      return "invalid metadata size";
+               case CHDERR_UNSUPPORTED_VERSION:        return "mismatched DIFF and CHD or unsupported CHD version";
+               case CHDERR_VERIFY_INCOMPLETE:          return "incomplete verify";
+               case CHDERR_INVALID_METADATA:           return "invalid metadata";
+               case CHDERR_INVALID_STATE:              return "invalid state";
+               case CHDERR_OPERATION_PENDING:          return "operation pending";
+               case CHDERR_UNSUPPORTED_FORMAT:         return "unsupported format";
+               case CHDERR_UNKNOWN_COMPRESSION:        return "unknown compression type";
+               case CHDERR_WALKING_PARENT:             return "currently examining parent";
+               case CHDERR_COMPRESSING:                return "currently compressing";
+               default:                                return "undocumented error";
+       }
+}
+
+
+
+//**************************************************************************
+//  INTERNAL HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+//  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_OLD_METADATA_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
@@ -639,10 +1449,10 @@ void chd_file::parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1)
        // 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;
+               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;
@@ -692,10 +1502,10 @@ void chd_file::parse_v4_header(UINT8 *rawheader, sha1_t &parentsha1)
        // 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;
+               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;
@@ -764,139 +1574,209 @@ void chd_file::parse_v5_header(UINT8 *rawheader, sha1_t &parentsha1)
 
 
 //-------------------------------------------------
-//  open_common - common path when opening an
-//  existing CHD file for input
+//  compress_v5_map - compress the v5 map and
+//  write it to the end of the file
 //-------------------------------------------------
 
-chd_error chd_file::open_common(bool writeable)
+chd_error chd_file::compress_v5_map()
 {
-       // 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));
+               // first get a CRC-16 of the original rawmap
+               crc16_t mapcrc = crc16_creator::simple(m_rawmap, m_hunkcount * 12);
+
+               // create a buffer to hold the RLE data
+               dynamic_buffer compression_rle(m_hunkcount);
+               UINT8 *dest = compression_rle;
+
+               // use a huffman encoder for 16 different codes, maximum length is 8 bits
+               huffman_encoder<16, 8> encoder;
+               encoder.histo_reset();
+
+               // RLE-compress the compression type since we expect runs of the same
+               UINT32 max_self = 0;
+               UINT32 last_self = 0;
+               UINT64 max_parent = 0;
+               UINT64 last_parent = 0;
+               UINT32 max_complen = 0;
+               UINT8 lastcomp = 0;
+               int count = 0;
+               for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
+               {
+                       UINT8 curcomp = m_rawmap[hunknum * 12 + 0];
 
-               // verify the signature
-               if (memcmp(rawheader, "MComprHD", 8) != 0)
-                       throw CHDERR_INVALID_FILE;
+                       // promote self block references to more compact forms
+                       if (curcomp == COMPRESSION_SELF)
+                       {
+                               UINT32 refhunk = be_read(&m_rawmap[hunknum * 12 + 4], 6);
+                               if (refhunk == last_self)
+                                       curcomp = COMPRESSION_SELF_0;
+                               else if (refhunk == last_self + 1)
+                                       curcomp = COMPRESSION_SELF_1;
+                               else
+                                       max_self = MAX(max_self, refhunk);
+                               last_self = refhunk;
+                       }
 
-               // 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;
+                       // promote parent block references to more compact forms
+                       else if (curcomp == COMPRESSION_PARENT)
+                       {
+                               UINT32 refunit = be_read(&m_rawmap[hunknum * 12 + 4], 6);
+                               if (refunit == (UINT64(hunknum) * UINT64(m_hunkbytes)) / m_unitbytes)
+                                       curcomp = COMPRESSION_PARENT_SELF;
+                               else if (refunit == last_parent)
+                                       curcomp = COMPRESSION_PARENT_0;
+                               else if (refunit == last_parent + m_hunkbytes / m_unitbytes)
+                                       curcomp = COMPRESSION_PARENT_1;
+                               else
+                                       max_parent = MAX(max_parent, refunit);
+                               last_parent = refunit;
+                       }
 
-               // 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;
-               }
+                       // track maximum compressed length
+                       else //if (curcomp >= COMPRESSION_TYPE_0 && curcomp <= COMPRESSION_TYPE_3)
+                               max_complen = MAX(max_complen, be_read(&m_rawmap[hunknum * 12 + 1], 3));
 
-               if (writeable && !m_allow_writes)
-                       throw CHDERR_FILE_NOT_WRITEABLE;
+                       // track repeats
+                       if (curcomp == lastcomp)
+                               count++;
 
-               // 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;
+                       // if no repeat, or we're at the end, flush it
+                       if (curcomp != lastcomp || hunknum == m_hunkcount - 1)
+                       {
+                               while (count != 0)
+                               {
+                                       if (count < 3)
+                                               encoder.histo_one(*dest++ = lastcomp), count--;
+                                       else if (count <= 3+15)
+                                       {
+                                               encoder.histo_one(*dest++ = COMPRESSION_RLE_SMALL);
+                                               encoder.histo_one(*dest++ = count - 3);
+                                               count = 0;
+                                       }
+                                       else
+                                       {
+                                               int this_count = MIN(count, 3+16+255);
+                                               encoder.histo_one(*dest++ = COMPRESSION_RLE_LARGE);
+                                               encoder.histo_one(*dest++ = (this_count - 3 - 16) >> 4);
+                                               encoder.histo_one(*dest++ = (this_count - 3 - 16) & 15);
+                                               count -= this_count;
+                                       }
+                               }
+                               if (curcomp != lastcomp)
+                                       encoder.histo_one(*dest++ = lastcomp = curcomp);
+                       }
                }
-               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;
-       }
-}
+               // compute a tree and export it to the buffer
+               dynamic_buffer compressed(m_hunkcount * 6);
+               bitstream_out bitbuf(&compressed[16], compressed.count() - 16);
+               huffman_error err = encoder.compute_tree_from_histo();
+               if (err != HUFFERR_NONE)
+                       throw CHDERR_COMPRESSION_ERROR;
+               err = encoder.export_tree_rle(bitbuf);
+               if (err != HUFFERR_NONE)
+                       throw CHDERR_COMPRESSION_ERROR;
+
+               // encode the data
+               for (UINT8 *src = compression_rle; src < dest; src++)
+                       encoder.encode_one(bitbuf, *src);
+
+               // determine the number of bits we need to hold the a length
+               // and a hunk index
+               UINT8 lengthbits = bits_for_value(max_complen);
+               UINT8 selfbits = bits_for_value(max_self);
+               UINT8 parentbits = bits_for_value(max_parent);
+
+               // for each compression type, output the relevant data
+               lastcomp = 0;
+               count = 0;
+               UINT8 *src = compression_rle;
+               UINT64 firstoffs = 0;
+               for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
+               {
+                       UINT8 *rawmap = &m_rawmap[hunknum * 12];
+                       UINT32 length = be_read(&rawmap[1], 3);
+                       UINT64 offset = be_read(&rawmap[4], 6);
+                       UINT16 crc = be_read(&rawmap[10], 2);
 
-//-------------------------------------------------
-//  create_open_common - common code for handling
-//  creation and opening of a file
-//-------------------------------------------------
+                       // if no count remaining, fetch the next entry
+                       if (count == 0)
+                       {
+                               UINT8 val = *src++;
+                               if (val == COMPRESSION_RLE_SMALL)
+                                       count = 2 + *src++;
+                               else if (val == COMPRESSION_RLE_LARGE)
+                                       count = 2 + 16 + (*src++ << 4), count += *src++;
+                               else
+                                       lastcomp = val;
+                       }
+                       else
+                               count--;
 
-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;
-       }
+                       // output additional data needed for this entry
+                       switch (lastcomp)
+                       {
+                               case COMPRESSION_TYPE_0:
+                               case COMPRESSION_TYPE_1:
+                               case COMPRESSION_TYPE_2:
+                               case COMPRESSION_TYPE_3:
+                                       assert(length < (1 << lengthbits));
+                                       bitbuf.write(length, lengthbits);
+                                       bitbuf.write(crc, 16);
+                                       if (firstoffs == 0)
+                                               firstoffs = offset;
+                                       break;
 
-       // 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());
+                               case COMPRESSION_NONE:
+                                       bitbuf.write(crc, 16);
+                                       if (firstoffs == 0)
+                                               firstoffs = offset;
+                                       break;
 
-       // allocate the temporary compressed buffer and a buffer for caching
-       m_compressed.resize(m_hunkbytes);
-       m_cache.resize(m_hunkbytes);
-}
+                               case COMPRESSION_SELF:
+                                       assert(offset < (UINT64(1) << selfbits));
+                                       bitbuf.write(offset, selfbits);
+                                       break;
 
+                               case COMPRESSION_PARENT:
+                                       assert(offset < (UINT64(1) << parentbits));
+                                       bitbuf.write(offset, parentbits);
+                                       break;
 
-//-------------------------------------------------
-//  metadata_find - find a metadata entry
-//-------------------------------------------------
+                               case COMPRESSION_SELF_0:
+                               case COMPRESSION_SELF_1:
+                               case COMPRESSION_PARENT_SELF:
+                               case COMPRESSION_PARENT_0:
+                               case COMPRESSION_PARENT_1:
+                                       break;
+                       }
+               }
 
-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;
+               // write the map header
+               UINT32 complen = bitbuf.flush();
+               assert(!bitbuf.overflow());
+               be_write(&compressed[0], complen, 4);
+               be_write(&compressed[4], firstoffs, 6);
+               be_write(&compressed[10], mapcrc, 2);
+               compressed[12] = lengthbits;
+               compressed[13] = selfbits;
+               compressed[14] = parentbits;
+               compressed[15] = 0;
+
+               // write the result
+               m_mapoffset = file_append(compressed, complen + 16);
+
+               // then write the map offset
+               UINT8 rawbuf[sizeof(UINT64)];
+               be_write(rawbuf, m_mapoffset, 8);
+               file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf));
+               return CHDERR_NONE;
        }
-
-       // loop until we run out of options
-       while (metaentry.offset != 0)
+       catch (chd_error &err)
        {
-               // 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;
+               return err;
        }
-
-       // if we get here, we didn't find it
-       return false;
 }
 
 
@@ -1017,100 +1897,854 @@ void chd_file::decompress_v5_map()
                throw CHDERR_DECOMPRESSION_ERROR;
 }
 
+
 //-------------------------------------------------
-//  sha1 - return our SHA1 value
+//  create_common - command path when creating a
+//  new CHD file
 //-------------------------------------------------
 
-sha1_t chd_file::sha1()
+chd_error chd_file::create_common()
 {
+       // wrap in try for proper error handling
        try
        {
-               // read the big-endian version
-               UINT8 rawbuf[sizeof(sha1_t)];
-               file_read(m_sha1_offset, rawbuf, sizeof(rawbuf));
-               return be_read_sha1(rawbuf);
+               m_version = HEADER_VERSION;
+               m_metaoffset = 0;
+
+               // if we have a parent, it must be V3 or later
+               if (m_parent != NULL && m_parent->version() < 3)
+                       throw CHDERR_UNSUPPORTED_VERSION;
+
+               // must be an even number of units per hunk
+               if (m_hunkbytes % m_unitbytes != 0)
+                       throw CHDERR_INVALID_PARAMETER;
+               if (m_parent != NULL && m_unitbytes != m_parent->unit_bytes())
+                       throw CHDERR_INVALID_PARAMETER;
+
+               // verify the compression types
+               bool found_zero = false;
+               for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compression); codecnum++)
+               {
+                       // once we hit an empty slot, all later slots must be empty as well
+                       if (m_compression[codecnum] == CHD_CODEC_NONE)
+                               found_zero = true;
+                       else if (found_zero)
+                               throw CHDERR_INVALID_PARAMETER;
+                       else if (!chd_codec_list::codec_exists(m_compression[codecnum]))
+                               throw CHDERR_UNKNOWN_COMPRESSION;
+               }
+
+               // create our V5 header
+               UINT8 rawheader[V5_HEADER_SIZE];
+               memcpy(&rawheader[0], "MComprHD", 8);
+               be_write(&rawheader[8], V5_HEADER_SIZE, 4);
+               be_write(&rawheader[12], m_version, 4);
+               be_write(&rawheader[16], m_compression[0], 4);
+               be_write(&rawheader[20], m_compression[1], 4);
+               be_write(&rawheader[24], m_compression[2], 4);
+               be_write(&rawheader[28], m_compression[3], 4);
+               be_write(&rawheader[32], m_logicalbytes, 8);
+               be_write(&rawheader[40], compressed() ? 0 : V5_HEADER_SIZE, 8);
+               be_write(&rawheader[48], m_metaoffset, 8);
+               be_write(&rawheader[56], m_hunkbytes, 4);
+               be_write(&rawheader[60], m_unitbytes, 4);
+               be_write_sha1(&rawheader[64], sha1_t::null);
+               be_write_sha1(&rawheader[84], sha1_t::null);
+               be_write_sha1(&rawheader[104], (m_parent != NULL) ? m_parent->sha1() : sha1_t::null);
+
+               // write the resulting header
+               file_write(0, rawheader, sizeof(rawheader));
+
+               // parse it back out to set up fields appropriately
+               sha1_t parentsha1;
+               parse_v5_header(rawheader, parentsha1);
+
+               // writes are obviously permitted; reads only if uncompressed
+               m_allow_writes = true;
+               m_allow_reads = !compressed();
+
+               // write out the map (if not compressed)
+               if (!compressed())
+               {
+                       UINT32 mapsize = m_mapentrybytes * m_hunkcount;
+                       UINT8 buffer[4096] = { 0 };
+                       UINT64 offset = m_mapoffset;
+                       while (mapsize != 0)
+                       {
+                               UINT32 bytes_to_write = MIN(mapsize, sizeof(buffer));
+                               file_write(offset, buffer, bytes_to_write);
+                               offset += bytes_to_write;
+                               mapsize -= bytes_to_write;
+                       }
+               }
+
+               // finish opening the file
+               create_open_common();
        }
-       catch (chd_error &)
+
+       // handle errors by closing ourself
+       catch (chd_error &err)
        {
-               // on failure, return NULL
-               return sha1_t::null;
+               close();
+               return err;
        }
+       catch (...)
+       {
+               close();
+               throw;
+       }
+       return CHDERR_NONE;
 }
 
 
 //-------------------------------------------------
-//  raw_sha1 - return our raw SHA1 value
+//  open_common - common path when opening an
+//  existing CHD file for input
 //-------------------------------------------------
 
-sha1_t chd_file::raw_sha1()
+chd_error chd_file::open_common(bool writeable)
 {
+       // wrap in try for proper error handling
        try
        {
-               // determine offset within the file for data-only
-               if (m_rawsha1_offset == 0)
+               // 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 big-endian version
-               UINT8 rawbuf[sizeof(sha1_t)];
-               file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf));
-               return be_read_sha1(rawbuf);
+               // 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;
        }
-       catch (chd_error &)
+
+       // handle errors by closing ourself
+       catch (chd_error &err)
        {
-               // on failure, return NULL
-               return sha1_t::null;
+               close();
+               return err;
        }
 }
 
 
 //-------------------------------------------------
-//  parent_sha1 - return our parent's SHA1 value
+//  create_open_common - common code for handling
+//  creation and opening of a file
 //-------------------------------------------------
 
-sha1_t chd_file::parent_sha1()
+void chd_file::create_open_common()
 {
-       try
+       // verify the compression types and initialize the codecs
+       for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_compression); decompnum++)
        {
-               // determine offset within the file
-               if (m_parentsha1_offset == 0)
-                       throw CHDERR_UNSUPPORTED_VERSION;
+               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 big-endian version
-               UINT8 rawbuf[sizeof(sha1_t)];
-               file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf));
-               return be_read_sha1(rawbuf);
+       // 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);
+}
+
+
+//-------------------------------------------------
+//  verify_proper_compression_append - verify that
+//  the given hunk is a proper candidate for
+//  appending to a compressed CHD
+//-------------------------------------------------
+
+void chd_file::verify_proper_compression_append(UINT32 hunknum)
+{
+       // 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;
+
+       // if not writeable, fail
+       if (!m_allow_writes)
+               throw CHDERR_FILE_NOT_WRITEABLE;
+
+       // compressed writes only via this interface
+       if (!compressed())
+               throw CHDERR_FILE_NOT_WRITEABLE;
+
+       // only permitted to write new blocks
+       UINT8 *rawmap = &m_rawmap[hunknum * 12];
+       if (rawmap[0] != 0xff)
+               throw CHDERR_COMPRESSION_ERROR;
+
+       // if this isn't the first block, only permitted to write immediately
+       // after the previous one
+       if (hunknum != 0 && rawmap[-12] == 0xff)
+               throw CHDERR_COMPRESSION_ERROR;
+}
+
+
+//-------------------------------------------------
+//  hunk_write_compressed - write a hunk to a
+//  compressed CHD, discovering the best
+//  technique
+//-------------------------------------------------
+
+void chd_file::hunk_write_compressed(UINT32 hunknum, INT8 compression, const UINT8 *compressed, UINT32 complength, crc16_t crc16)
+{
+       // verify that we are appending properly to a compressed file
+       verify_proper_compression_append(hunknum);
+
+       // write the final result
+       UINT64 offset = file_append(compressed, complength);
+
+       // update the map entry
+       UINT8 *rawmap = &m_rawmap[hunknum * 12];
+       rawmap[0] = (compression == -1) ? COMPRESSION_NONE : compression;
+       be_write(&rawmap[1], complength, 3);
+       be_write(&rawmap[4], offset, 6);
+       be_write(&rawmap[10], crc16, 2);
+}
+
+
+//-------------------------------------------------
+//  hunk_copy_from_self - mark a hunk as being a
+//  copy of another hunk in the same CHD
+//-------------------------------------------------
+
+void chd_file::hunk_copy_from_self(UINT32 hunknum, UINT32 otherhunk)
+{
+       // verify that we are appending properly to a compressed file
+       verify_proper_compression_append(hunknum);
+
+       // only permitted to reference prior hunks
+       if (otherhunk >= hunknum)
+               throw CHDERR_INVALID_PARAMETER;
+
+       // update the map entry
+       UINT8 *rawmap = &m_rawmap[hunknum * 12];
+       rawmap[0] = COMPRESSION_SELF;
+       be_write(&rawmap[1], 0, 3);
+       be_write(&rawmap[4], otherhunk, 6);
+       be_write(&rawmap[10], 0, 2);
+}
+
+
+//-------------------------------------------------
+//  hunk_copy_from_parent - mark a hunk as being a
+//  copy of a hunk from a parent CHD
+//-------------------------------------------------
+
+void chd_file::hunk_copy_from_parent(UINT32 hunknum, UINT64 parentunit)
+{
+       // verify that we are appending properly to a compressed file
+       verify_proper_compression_append(hunknum);
+
+       // update the map entry
+       UINT8 *rawmap = &m_rawmap[hunknum * 12];
+       rawmap[0] = COMPRESSION_PARENT;
+       be_write(&rawmap[1], 0, 3);
+       be_write(&rawmap[4], parentunit, 6);
+       be_write(&rawmap[10], 0, 2);
+}
+
+
+//-------------------------------------------------
+//  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;
        }
-       catch (chd_error &)
+       else
        {
-               // on failure, return NULL
-               return sha1_t::null;
+               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;
+}
+
+
+//-------------------------------------------------
+//  metadata_set_previous_next - set the 'next'
+//  offset of a piece of metadata
+//-------------------------------------------------
+
+void chd_file::metadata_set_previous_next(UINT64 prevoffset, UINT64 nextoffset)
+{
+       UINT64 offset = 0;
+
+       // if we were the first entry, make the next entry the first
+       if (prevoffset == 0)
+       {
+               offset = m_metaoffset_offset;
+               m_metaoffset = nextoffset;
        }
+
+       // otherwise, update the link in the previous header
+       else
+               offset = prevoffset + 8;
+
+       // create a big-endian version
+       UINT8 rawbuf[sizeof(UINT64)];
+       be_write(rawbuf, nextoffset, 8);
+
+       // write to the header and update our local copy
+       file_write(offset, rawbuf, sizeof(rawbuf));
+}
+
+
+//-------------------------------------------------
+//  metadata_update_hash - compute the SHA1
+//  hash of all metadata that requests it
+//-------------------------------------------------
+
+void chd_file::metadata_update_hash()
+{
+       // only works for V4 and above, and only for compressed CHDs
+       if (m_version < 4 || !compressed())
+               return;
+
+       // compute the new overall hash
+       sha1_t fullsha1 = compute_overall_sha1(raw_sha1());
+
+       // create a big-endian version
+       UINT8 rawbuf[sizeof(sha1_t)];
+       be_write_sha1(&rawbuf[0], fullsha1);
+
+       // write to the header
+       file_write(m_sha1_offset, rawbuf, sizeof(rawbuf));
+}
+
+
+//-------------------------------------------------
+//  metadata_hash_compare - compare two hash
+//  entries
+//-------------------------------------------------
+
+int CLIB_DECL chd_file::metadata_hash_compare(const void *elem1, const void *elem2)
+{
+       return memcmp(elem1, elem2, sizeof(metadata_hash));
 }
 
+
+
 //**************************************************************************
-//  CHD FILE MANAGEMENT
+//  CHD COMPRESSOR
 //**************************************************************************
 
 //-------------------------------------------------
-//  chd_file - constructor
+//  chd_file_compressor - constructor
 //-------------------------------------------------
 
-chd_file::chd_file()
-       : m_file(NULL),
-      m_owns_file(false)
+chd_file_compressor::chd_file_compressor()
+       : m_walking_parent(false),
+               m_total_in(0),
+               m_total_out(0),
+               m_read_queue(NULL),
+               m_read_queue_offset(0),
+               m_read_done_offset(0),
+               m_read_error(false),
+               m_work_queue(NULL),
+               m_write_hunk(0)
+{
+       // zap arrays
+       memset(m_codecs, 0, sizeof(m_codecs));
+
+       // allocate work queues
+       m_read_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_IO);
+       m_work_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_MULTI);
+}
+
+
+//-------------------------------------------------
+//  ~chd_file_compressor - destructor
+//-------------------------------------------------
+
+chd_file_compressor::~chd_file_compressor()
+{
+       // free the work queues
+       osd_work_queue_free(m_read_queue);
+       osd_work_queue_free(m_work_queue);
+
+       // delete allocated arrays
+       for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_codecs); codecnum++)
+               delete m_codecs[codecnum];
+}
+
+
+//-------------------------------------------------
+//  compress_begin - initiate compression
+//-------------------------------------------------
+
+void chd_file_compressor::compress_begin()
 {
        // reset state
-       memset(m_decompressor, 0, sizeof(m_decompressor));
-       close();
+       m_walking_parent = (m_parent != NULL);
+       m_total_in = 0;
+       m_total_out = 0;
+       m_compsha1.reset();
+
+       // reset our maps
+       m_parent_map.reset();
+       m_current_map.reset();
+
+       // reset read state
+       m_read_queue_offset = 0;
+       m_read_done_offset = 0;
+       m_read_error = false;
+
+       // reset work item state
+       m_work_buffer.resize_and_clear(hunk_bytes() * (WORK_BUFFER_HUNKS + 1));
+       m_compressed_buffer.resize(hunk_bytes() * WORK_BUFFER_HUNKS);
+       for (int itemnum = 0; itemnum < WORK_BUFFER_HUNKS; itemnum++)
+       {
+               work_item &item = m_work_item[itemnum];
+               item.m_compressor = this;
+               item.m_data = m_work_buffer + hunk_bytes() * itemnum;
+               item.m_compressed = m_compressed_buffer + hunk_bytes() * itemnum;
+               item.m_hash.resize(hunk_bytes() / unit_bytes());
+       }
+
+       // initialize codec instances
+       for (int instance = 0; instance < ARRAY_LENGTH(m_codecs); instance++)
+       {
+               delete m_codecs[instance];
+               m_codecs[instance] = new chd_compressor_group(*this, m_compression);
+       }
+
+       // reset write state
+       m_write_hunk = 0;
 }
 
 
 //-------------------------------------------------
-//  ~chd_file - destructor
+//  compress_continue - continue compression
 //-------------------------------------------------
 
-chd_file::~chd_file()
+chd_error chd_file_compressor::compress_continue(double &progress, double &ratio)
 {
-       // close any open files
-       close();
+       // if we got an error, return an error
+       if (m_read_error)
+               return CHDERR_READ_ERROR;
+
+       // if done reading, queue some more
+       while (m_read_queue_offset < m_logicalbytes && osd_work_queue_items(m_read_queue) < 2)
+       {
+               // see if we have enough free work items to read the next half of a buffer
+               UINT32 startitem = m_read_queue_offset / hunk_bytes();
+               UINT32 enditem = startitem + WORK_BUFFER_HUNKS / 2;
+               UINT32 curitem;
+               for (curitem = startitem; curitem < enditem; curitem++)
+                       if (m_work_item[curitem % WORK_BUFFER_HUNKS].m_status != WS_READY)
+                               break;
+
+               // if it's not all clear, defer
+               if (curitem != enditem)
+                       break;
+
+               // if we're walking the parent, we want one more item to have cleared so we
+               // can read an extra hunk there
+               if (m_walking_parent && m_work_item[curitem % WORK_BUFFER_HUNKS].m_status != WS_READY)
+                       break;
+
+               // queue the next read
+               for (curitem = startitem; curitem < enditem; curitem++)
+                       m_work_item[curitem % WORK_BUFFER_HUNKS].m_status = WS_READING;
+               osd_work_item_queue(m_read_queue, async_read_static, this, WORK_ITEM_FLAG_AUTO_RELEASE);
+               m_read_queue_offset += WORK_BUFFER_HUNKS * hunk_bytes() / 2;
+       }
+
+       // flush out any finished items
+       while (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status == WS_COMPLETE)
+       {
+               work_item &item = m_work_item[m_write_hunk % WORK_BUFFER_HUNKS];
+
+               // free any OSD work item
+               if (item.m_osd != NULL)
+                       osd_work_item_release(item.m_osd);
+               item.m_osd = NULL;
+
+               // for parent walking, just add to the hashmap
+               if (m_walking_parent)
+               {
+                       UINT32 uph = hunk_bytes() / unit_bytes();
+                       UINT32 units = uph;
+                       if (item.m_hunknum == hunk_count() - 1 || !compressed())
+                               units = 1;
+                       for (UINT32 unit = 0; unit < units; unit++)
+                               if (m_parent_map.find(item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1) == hashmap::NOT_FOUND)
+                                       m_parent_map.add(item.m_hunknum * uph + unit, item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1);
+               }
+
+               // if we're uncompressed, use regular writes
+               else if (!compressed())
+               {
+                       chd_error err = write_hunk(item.m_hunknum, item.m_data);
+                       if (err != CHDERR_NONE)
+                               return err;
+
+                       // writes of all-0 data don't actually take space, so see if we count this
+                       chd_codec_type codec = CHD_CODEC_NONE;
+                       UINT32 complen;
+                       hunk_info(item.m_hunknum, codec, complen);
+                       if (codec == CHD_CODEC_NONE)
+                               m_total_out += m_hunkbytes;
+               }
+
+               // for compressing, process the result
+               else do
+               {
+                       // first see if the hunk is in the parent or self maps
+                       UINT64 selfhunk = m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1);
+                       if (selfhunk != hashmap::NOT_FOUND)
+                       {
+                               hunk_copy_from_self(item.m_hunknum, selfhunk);
+                               break;
+                       }
+
+                       // if not, see if it's in the parent map
+                       if (m_parent != NULL)
+                       {
+                               UINT64 parentunit = m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1);
+                               if (parentunit != hashmap::NOT_FOUND)
+                               {
+                                       hunk_copy_from_parent(item.m_hunknum, parentunit);
+                                       break;
+                               }
+                       }
+
+                       // otherwise, append it compressed and add to the self map
+                       hunk_write_compressed(item.m_hunknum, item.m_compression, item.m_compressed, item.m_complen, item.m_hash[0].m_crc16);
+                       m_total_out += item.m_complen;
+                       m_current_map.add(item.m_hunknum, item.m_hash[0].m_crc16, item.m_hash[0].m_sha1);
+               } while (0);
+
+               // reset the item and advance
+               item.m_status = WS_READY;
+               m_write_hunk++;
+
+               // if we hit the end, finalize
+               if (m_write_hunk == m_hunkcount)
+               {
+                       // if this is just walking the parent, reset and get ready for compression
+                       if (m_walking_parent)
+                       {
+                               m_walking_parent = false;
+                               m_read_queue_offset = m_read_done_offset = 0;
+                               m_write_hunk = 0;
+                               for (int itemnum = 0; itemnum < WORK_BUFFER_HUNKS; itemnum++)
+                                       m_work_item[itemnum].m_status = WS_READY;
+                       }
+
+                       // wait for all reads to finish and if we're compressed, write the final SHA1 and map
+                       else
+                       {
+                               osd_work_queue_wait(m_read_queue, 30 * osd_ticks_per_second());
+                               if (!compressed())
+                                       return CHDERR_NONE;
+                               set_raw_sha1(m_compsha1.finish());
+                               return compress_v5_map();
+                       }
+               }
+       }
+
+       // update progress and ratio
+       if (m_walking_parent)
+               progress = double(m_read_done_offset) / double(logical_bytes());
+       else
+               progress = double(m_write_hunk) / double(m_hunkcount);
+       ratio = (m_total_in == 0) ? 1.0 : double(m_total_out) / double(m_total_in);
+
+       // if we're waiting for work, wait
+       while (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE && m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd != NULL)
+               osd_work_item_wait(m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd, osd_ticks_per_second());
+
+       return m_walking_parent ? CHDERR_WALKING_PARENT : CHDERR_COMPRESSING;
+}
+
+
+//-------------------------------------------------
+//  async_walk_parent - handle asynchronous parent
+//  walking operations
+//-------------------------------------------------
+
+void *chd_file_compressor::async_walk_parent_static(void *param, int threadid)
+{
+       work_item *item = reinterpret_cast<work_item *>(param);
+       item->m_compressor->async_walk_parent(*item);
+       return NULL;
+}
+
+void chd_file_compressor::async_walk_parent(work_item &item)
+{
+       // compute CRC-16 and SHA-1 hashes for each unit, unless we're the last one or we're uncompressed
+       UINT32 units = hunk_bytes() / unit_bytes();
+       if (item.m_hunknum == m_hunkcount - 1 || !compressed())
+               units = 1;
+       for (UINT32 unit = 0; unit < units; unit++)
+       {
+               item.m_hash[unit].m_crc16 = crc16_creator::simple(item.m_data + unit * unit_bytes(), hunk_bytes());
+               item.m_hash[unit].m_sha1 = sha1_creator::simple(item.m_data + unit * unit_bytes(), hunk_bytes());
+       }
+       item.m_status = WS_COMPLETE;
+}
+
+
+//-------------------------------------------------
+//  async_compress_hunk - handle asynchronous
+//  hunk compression
+//-------------------------------------------------
+
+void *chd_file_compressor::async_compress_hunk_static(void *param, int threadid)
+{
+       work_item *item = reinterpret_cast<work_item *>(param);
+       item->m_compressor->async_compress_hunk(*item, threadid);
+       return NULL;
+}
+
+void chd_file_compressor::async_compress_hunk(work_item &item, int threadid)
+{
+       // use our thread's codec
+       assert(threadid < ARRAY_LENGTH(m_codecs));
+       item.m_codecs = m_codecs[threadid];
+
+       // compute CRC-16 and SHA-1 hashes
+       item.m_hash[0].m_crc16 = crc16_creator::simple(item.m_data, hunk_bytes());
+       item.m_hash[0].m_sha1 = sha1_creator::simple(item.m_data, hunk_bytes());
+
+       // find the best compression scheme, unless we already have a self or parent match
+       // (note we may miss a self match from blocks not yet added, but this just results in extra work)
+       if (m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND &&
+               m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND)
+               item.m_compression = item.m_codecs->find_best_compressor(item.m_data, item.m_compressed, item.m_complen);
+
+       // mark us complete
+       item.m_status = WS_COMPLETE;
+}
+
+
+//-------------------------------------------------
+//  async_read - handle asynchronous source file
+//  reading
+//-------------------------------------------------
+
+void *chd_file_compressor::async_read_static(void *param, int threadid)
+{
+       reinterpret_cast<chd_file_compressor *>(param)->async_read();
+       return NULL;
+}
+
+void chd_file_compressor::async_read()
+{
+       // if in the error or complete state, stop
+       if (m_read_error)
+               return;
+
+       // determine parameters for the read
+       UINT32 work_buffer_bytes = WORK_BUFFER_HUNKS * hunk_bytes();
+       UINT32 numbytes = work_buffer_bytes / 2;
+       if (m_read_done_offset + numbytes > logical_bytes())
+               numbytes = logical_bytes() - m_read_done_offset;
+
+       // catch any exceptions coming out of here
+       try
+       {
+               // do the read
+               UINT8 *dest = m_work_buffer + (m_read_done_offset % work_buffer_bytes);
+               assert(dest == m_work_buffer || dest == m_work_buffer + work_buffer_bytes/2);
+               UINT64 end_offset = m_read_done_offset + numbytes;
+
+               // if walking the parent, read in hunks from the parent CHD
+               if (m_walking_parent)
+               {
+                       UINT8 *curdest = dest;
+                       for (UINT64 curoffs = m_read_done_offset; curoffs < end_offset + 1; curoffs += hunk_bytes())
+                       {
+                               m_parent->read_hunk(curoffs / hunk_bytes(), curdest);
+                               curdest += hunk_bytes();
+                       }
+               }
+
+               // otherwise, call the virtual function
+               else
+                       read_data(dest, m_read_done_offset, numbytes);
+
+               // spawn off work for each hunk
+               for (UINT64 curoffs = m_read_done_offset; curoffs < end_offset; curoffs += hunk_bytes())
+               {
+                       UINT32 hunknum = curoffs / hunk_bytes();
+                       work_item &item = m_work_item[hunknum % WORK_BUFFER_HUNKS];
+                       assert(item.m_status == WS_READING);
+                       item.m_status = WS_QUEUED;
+                       item.m_hunknum = hunknum;
+                       item.m_osd = osd_work_item_queue(m_work_queue, m_walking_parent ? async_walk_parent_static : async_compress_hunk_static, &item, 0);
+               }
+
+               // continue the running SHA-1
+               if (!m_walking_parent)
+               {
+                       if (compressed())
+                               m_compsha1.append(dest, numbytes);
+                       m_total_in += numbytes;
+               }
+
+               // advance the read pointer
+               m_read_done_offset += numbytes;
+       }
+       catch (std::exception& ex)
+       {
+               fprintf(stderr, "exception occured: %s\n", ex.what());
+               m_read_error = true;
+       }
+}
+
+
+
+//**************************************************************************
+//  CHD COMPRESSOR HASHMAP
+//**************************************************************************
+
+//-------------------------------------------------
+//  hashmap - constructor
+//-------------------------------------------------
+
+chd_file_compressor::hashmap::hashmap()
+       : m_block_list(new entry_block(NULL))
+{
+       // initialize the map to empty
+       memset(m_map, 0, sizeof(m_map));
+}
+
+
+//-------------------------------------------------
+//  ~hashmap - destructor
+//-------------------------------------------------
+
+chd_file_compressor::hashmap::~hashmap()
+{
+       reset();
+       delete m_block_list;
+}
+
+
+//-------------------------------------------------
+//  reset - reset the state of the map
+//-------------------------------------------------
+
+void chd_file_compressor::hashmap::reset()
+{
+       // delete all the blocks
+       while (m_block_list->m_next != NULL)
+       {
+               entry_block *block = m_block_list;
+               m_block_list = block->m_next;
+               delete block;
+       }
+       m_block_list->m_nextalloc = 0;
+
+       // reset the hash
+       memset(m_map, 0, sizeof(m_map));
+}
+
+
+//-------------------------------------------------
+//  find - find an item in the CRC map
+//-------------------------------------------------
+
+UINT64 chd_file_compressor::hashmap::find(crc16_t crc16, sha1_t sha1)
+{
+       // look up the entry in the map
+       for (entry_t *entry = m_map[crc16]; entry != NULL; entry = entry->m_next)
+               if (entry->m_sha1 == sha1)
+                       return entry->m_itemnum;
+       return NOT_FOUND;
+}
+
+
+//-------------------------------------------------
+//  add - add an item to the CRC map
+//-------------------------------------------------
+
+void chd_file_compressor::hashmap::add(UINT64 itemnum, crc16_t crc16, sha1_t sha1)
+{
+       // add to the appropriate map
+       if (m_block_list->m_nextalloc == ARRAY_LENGTH(m_block_list->m_array))
+               m_block_list = new entry_block(m_block_list);
+       entry_t *entry = &m_block_list->m_array[m_block_list->m_nextalloc++];
+       entry->m_itemnum = itemnum;
+       entry->m_sha1 = sha1;
+       entry->m_next = m_map[crc16];
+       m_map[crc16] = entry;
 }
index c548a34bff554e04240a07c534f590e8c118283e..d19a23ade58d29f20c6bc7fdd2084cc9c59918b9 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
@@ -45,6 +16,7 @@
 #include "osdcore.h"
 #include "coretmpl.h"
 #include "astring.h"
+#include "bitmap.h"
 #include "corefile.h"
 #include "hashing.h"
 #include "chdcodec.h"
 //**************************************************************************
 
 // 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
+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;
@@ -232,7 +204,7 @@ const chd_metadata_tag CHDMETATAG_WILDCARD = 0;
 const UINT32 CHDMETAINDEX_APPEND = ~0;
 
 // metadata flags
-const UINT8 CHD_MDFLAGS_CHECKSUM = 0x01;               // indicates data is checksummed
+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');
@@ -253,7 +225,8 @@ 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');
+const chd_metadata_tag GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T');
+const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C', 'H', 'G', 'D');
 extern const char *GDROM_TRACK_METADATA_FORMAT;
 
 // standard A/V metadata
@@ -329,6 +302,9 @@ public:
        chd_file();
        virtual ~chd_file();
 
+       // operators
+       operator core_file *() { return m_file; }
+
        // getters
        bool opened() const { return (m_file != NULL); }
        UINT32 version() const { return m_version; }
@@ -345,23 +321,41 @@ public:
        sha1_t parent_sha1();
        chd_error hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes);
 
+       // setters
+       void set_raw_sha1(sha1_t rawdata);
+       void set_parent_sha1(sha1_t parent);
+
+       // file create
+       chd_error create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]);
+       chd_error create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]);
+       chd_error create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent);
+       chd_error create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent);
+
        // 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);
+       chd_error open(const TCHAR *filename, bool writeable = false, chd_file *parent = NULL);
+       chd_error open(core_file &file, bool writeable = false, chd_file *parent = NULL);
 
        // file close
        void close();
 
        // read/write
        chd_error read_hunk(UINT32 hunknum, void *buffer);
+       chd_error write_hunk(UINT32 hunknum, const void *buffer);
        chd_error read_units(UINT64 unitnum, void *buffer, UINT32 count = 1);
+       chd_error write_units(UINT64 unitnum, const void *buffer, UINT32 count = 1);
        chd_error read_bytes(UINT64 offset, void *buffer, UINT32 bytes);
+       chd_error write_bytes(UINT64 offset, const 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);
+       chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen, UINT8 flags = CHD_MDFLAGS_CHECKSUM);
+       chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const astring &input, UINT8 flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input.cstr(), input.len() + 1, flags); }
+       chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const dynamic_buffer &input, UINT8 flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input, input.count(), flags); }
+       chd_error delete_metadata(chd_metadata_tag metatag, UINT32 metaindex);
+       chd_error clone_all_metadata(chd_file &source);
 
        // hashing helper
        sha1_t compute_overall_sha1(sha1_t rawsha1);
@@ -403,45 +397,184 @@ private:
        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);
+       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_file *             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?
+       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
+       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
+       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
+       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?
+       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
+
+// ======================> chd_file_compressor
+
+// class for creating a new compressed CHD
+class chd_file_compressor : public chd_file
+{
+public:
+       // construction/destruction
+       chd_file_compressor();
+       virtual ~chd_file_compressor();
+
+       // compression management
+       void compress_begin();
+       chd_error compress_continue(double &progress, double &ratio);
+
+protected:
+       // required override: read more data
+       virtual UINT32 read_data(void *dest, UINT64 offset, UINT32 length) = 0;
+
+private:
+       // hash map for looking up values
+       class hashmap
+       {
+       public:
+               // construction/destruction
+               hashmap();
+               ~hashmap();
+
+               // operations
+               void reset();
+               UINT64 find(crc16_t crc16, sha1_t sha1);
+               void add(UINT64 itemnum, crc16_t crc16, sha1_t sha1);
+
+               // constants
+               static const UINT64 NOT_FOUND = ~UINT64(0);
+       private:
+               // internal entry
+               struct entry_t
+               {
+                       entry_t *           m_next;             // next entry in list
+                       UINT64              m_itemnum;          // item number
+                       sha1_t              m_sha1;             // SHA-1 of the block
+               };
+
+               // block of entries
+               struct entry_block
+               {
+                       entry_block(entry_block *prev)
+                               : m_next(prev), m_nextalloc(0) { }
+
+                       entry_block *       m_next;             // next block in list
+                       UINT32              m_nextalloc;        // next to be allocated
+                       entry_t             m_array[16384];     // array of entries
+               };
+
+               // internal state
+               entry_t *           m_map[65536];           // map, hashed by CRC-16
+               entry_block *       m_block_list;           // list of allocated blocks
+       };
+
+       // status of a given work item
+       enum work_status
+       {
+               WS_READY = 0,
+               WS_READING,
+               WS_QUEUED,
+               WS_COMPLETE
+       };
+
+       // a CRC-16/SHA-1 pair
+       struct hash_pair
+       {
+               crc16_t             m_crc16;            // calculated CRC-16
+               sha1_t              m_sha1;             // calculated SHA-1
+       };
+
+       // a single work item
+       struct work_item
+       {
+               work_item()
+                       : m_osd(NULL)
+                       , m_compressor(NULL)
+                       , m_status(WS_READY)
+                       , m_data(NULL)
+                       , m_compressed(NULL)
+                       , m_complen(0)
+                       , m_compression(0)
+                       , m_codecs(NULL)
+               { }
+
+               osd_work_item *     m_osd;              // OSD work item running on this block
+               chd_file_compressor *m_compressor;      // pointer back to the compressor
+               volatile work_status m_status;          // current status of this item
+               UINT32              m_hunknum;          // number of the hunk we're working on
+               UINT8 *             m_data;             // pointer to the data we are working on
+               UINT8 *             m_compressed;       // pointer to the compressed data
+               UINT32              m_complen;          // compressed data length
+               INT8                m_compression;      // type of compression used
+               chd_compressor_group *m_codecs;         // codec instance
+               dynamic_array<hash_pair> m_hash;        // array of hashes
+       };
+
+       // internal helpers
+       static void *async_walk_parent_static(void *param, int threadid);
+       void async_walk_parent(work_item &item);
+       static void *async_compress_hunk_static(void *param, int threadid);
+       void async_compress_hunk(work_item &item, int threadid);
+       static void *async_read_static(void *param, int threadid);
+       void async_read();
+
+       // current compression status
+       bool                    m_walking_parent;   // are we building the parent map?
+       UINT64                  m_total_in;         // total bytes in
+       UINT64                  m_total_out;        // total bytes out
+       sha1_creator            m_compsha1;         // running SHA-1 on raw data
+
+       // hash lookup maps
+       hashmap                 m_parent_map;       // hash map for parent
+       hashmap                 m_current_map;      // hash map for current
+
+       // read I/O thread
+       osd_work_queue *        m_read_queue;       // work queue for reading
+       UINT64                  m_read_queue_offset;// next offset to enqueue
+       UINT64                  m_read_done_offset; // next offset that will complete
+       bool                    m_read_error;       // error during reading?
+
+       // work item thread
+       static const int WORK_BUFFER_HUNKS = 256;
+       osd_work_queue *        m_work_queue;       // queue for doing work on other threads
+       dynamic_buffer          m_work_buffer;      // buffer containing hunk data to work on
+       dynamic_buffer          m_compressed_buffer;// buffer containing compressed data
+       work_item               m_work_item[WORK_BUFFER_HUNKS]; // status of each hunk
+       chd_compressor_group *  m_codecs[WORK_MAX_THREADS]; // codecs to use
+
+       // output state
+       UINT32                  m_write_hunk;       // next hunk to write
+};
+
+
+#endif // __CHD_H__
diff --git a/archivers/chd/chdcd.cpp b/archivers/chd/chdcd.cpp
new file mode 100644 (file)
index 0000000..8690cc6
--- /dev/null
@@ -0,0 +1,1180 @@
+/***************************************************************************
+
+    TOC parser for CHD compression frontend
+    Handles CDRDAO .toc, CDRWIN .cue, Nero .nrg, and Sega GDROM .gdi
+
+    Copyright Nicola Salmoria and the MAME Team.
+    Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#include <ctype.h>
+#include <stdlib.h>
+#include "osdcore.h"
+#include "chd.h"
+#include "chdcd.h"
+#include "corefile.h"
+
+
+
+/***************************************************************************
+    CONSTANTS & DEFINES
+***************************************************************************/
+
+#define TOKENIZE i = tokenize( linebuffer, i, sizeof(linebuffer), token, sizeof(token) );
+
+
+
+/***************************************************************************
+    GLOBAL VARIABLES
+***************************************************************************/
+
+static char linebuffer[512];
+
+
+
+/***************************************************************************
+    IMPLEMENTATION
+***************************************************************************/
+
+static astring get_file_path(astring &path)
+{
+       int pos = path.rchr( 0, '\\');
+       if (pos!=-1) {
+               path = path.substr(0,pos+1);
+       } else {
+               pos = path.rchr( 0, '/');
+               path = path.substr(0,pos+1);
+       }
+       return path;
+}
+/*-------------------------------------------------
+    get_file_size - get the size of a file
+-------------------------------------------------*/
+
+static UINT64 get_file_size(const char *filename)
+{
+       osd_file *file;
+       UINT64 filesize = 0;
+       file_error filerr;
+
+       filerr = osd_open(filename, OPEN_FLAG_READ, &file, &filesize);
+       if (filerr == FILERR_NONE)
+               osd_close(file);
+
+       return filesize;
+}
+
+
+/*-------------------------------------------------
+    tokenize - get a token from the line buffer
+-------------------------------------------------*/
+
+static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
+{
+       int j = 0;
+       int singlequote = 0;
+       int doublequote = 0;
+
+       while ((i < linebuffersize) && isspace((UINT8)linebuffer[i]))
+       {
+               i++;
+       }
+
+       while ((i < linebuffersize) && (j < tokensize))
+       {
+               if (!singlequote && linebuffer[i] == '"' )
+               {
+                       doublequote = !doublequote;
+               }
+               else if (!doublequote && linebuffer[i] == '\'')
+               {
+                       singlequote = !singlequote;
+               }
+               else if (!singlequote && !doublequote && isspace((UINT8)linebuffer[i]))
+               {
+                       break;
+               }
+               else
+               {
+                       token[j] = linebuffer[i];
+                       j++;
+               }
+
+               i++;
+       }
+
+       token[j] = '\0';
+
+       return i;
+}
+
+
+/*-------------------------------------------------
+    msf_to_frames - convert m:s:f into a number of frames
+-------------------------------------------------*/
+
+static int msf_to_frames( char *token )
+{
+       int m = 0;
+       int s = 0;
+       int f = 0;
+
+       if( sscanf( token, "%d:%d:%d", &m, &s, &f ) == 1 )
+       {
+               f = m;
+       }
+       else
+       {
+               /* convert to just frames */
+               s += (m * 60);
+               f += (s * 75);
+       }
+
+       return f;
+}
+
+/*-------------------------------------------------
+    parse_wav_sample - takes a .WAV file, verifies
+    that the file is 16 bits, and returns the
+    length in bytes of the data and the offset in
+    bytes to where the data starts in the file.
+-------------------------------------------------*/
+static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs)
+{
+       unsigned long offset = 0;
+       UINT32 length, rate, filesize;
+       UINT16 bits, temp16;
+       char buf[32];
+       osd_file *file;
+       file_error filerr;
+       UINT64 fsize = 0;
+       UINT32 actual;
+
+       filerr = osd_open(filename, OPEN_FLAG_READ, &file, &fsize);
+       if (filerr != FILERR_NONE)
+       {
+               printf("ERROR: could not open (%s)\n", filename);
+               return 0;
+       }
+
+       /* read the core header and make sure it's a WAVE file */
+       osd_read(file, buf, 0, 4, &actual);
+       offset += actual;
+       if (offset < 4)
+       {
+               osd_close(file);
+               printf("ERROR: unexpected RIFF offset %lu (%s)\n", offset, filename);
+               return 0;
+       }
+       if (memcmp(&buf[0], "RIFF", 4) != 0)
+       {
+               osd_close(file);
+               printf("ERROR: could not find RIFF header (%s)\n", filename);
+               return 0;
+       }
+
+       /* get the total size */
+       osd_read(file, &filesize, offset, 4, &actual);
+       offset += actual;
+       if (offset < 8)
+       {
+               osd_close(file);
+               printf("ERROR: unexpected size offset %lu (%s)\n", offset, filename);
+               return 0;
+       }
+       filesize = LITTLE_ENDIANIZE_INT32(filesize);
+
+       /* read the RIFF file type and make sure it's a WAVE file */
+       osd_read(file, buf, offset, 4, &actual);
+       offset += actual;
+       if (offset < 12)
+       {
+               osd_close(file);
+               printf("ERROR: unexpected WAVE offset %lu (%s)\n", offset, filename);
+               return 0;
+       }
+       if (memcmp(&buf[0], "WAVE", 4) != 0)
+       {
+               osd_close(file);
+               printf("ERROR: could not find WAVE header (%s)\n", filename);
+               return 0;
+       }
+
+       /* seek until we find a format tag */
+       while (1)
+       {
+               osd_read(file, buf, offset, 4, &actual);
+               offset += actual;
+               osd_read(file, &length, offset, 4, &actual);
+               offset += actual;
+               length = LITTLE_ENDIANIZE_INT32(length);
+               if (memcmp(&buf[0], "fmt ", 4) == 0)
+                       break;
+
+               /* seek to the next block */
+               offset += length;
+               if (offset >= filesize)
+               {
+                       osd_close(file);
+                       printf("ERROR: could not find fmt tag (%s)\n", filename);
+                       return 0;
+               }
+       }
+
+       /* read the format -- make sure it is PCM */
+       osd_read(file, &temp16, offset, 2, &actual);
+       offset += actual;
+       temp16 = LITTLE_ENDIANIZE_INT16(temp16);
+       if (temp16 != 1)
+       {
+               osd_close(file);
+               printf("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, filename);
+               return 0;
+       }
+
+       /* number of channels -- only stereo is supported */
+       osd_read(file, &temp16, offset, 2, &actual);
+       offset += actual;
+       temp16 = LITTLE_ENDIANIZE_INT16(temp16);
+       if (temp16 != 2)
+       {
+               osd_close(file);
+               printf("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, filename);
+               return 0;
+       }
+
+       /* sample rate */
+       osd_read(file, &rate, offset, 4, &actual);
+       offset += actual;
+       rate = LITTLE_ENDIANIZE_INT32(rate);
+       if (rate != 44100)
+       {
+               osd_close(file);
+               printf("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, filename);
+               return 0;
+       }
+
+       /* bytes/second and block alignment are ignored */
+       osd_read(file, buf, offset, 6, &actual);
+       offset += actual;
+
+       /* bits/sample */
+       osd_read(file, &bits, offset, 2, &actual);
+       offset += actual;
+       bits = LITTLE_ENDIANIZE_INT16(bits);
+       if (bits != 16)
+       {
+               osd_close(file);
+               printf("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, filename);
+               return 0;
+       }
+
+       /* seek past any extra data */
+       offset += length - 16;
+
+       /* seek until we find a data tag */
+       while (1)
+       {
+               osd_read(file, buf, offset, 4, &actual);
+               offset += actual;
+               osd_read(file, &length, offset, 4, &actual);
+               offset += actual;
+               length = LITTLE_ENDIANIZE_INT32(length);
+               if (memcmp(&buf[0], "data", 4) == 0)
+                       break;
+
+               /* seek to the next block */
+               offset += length;
+               if (offset >= filesize)
+               {
+                       osd_close(file);
+                       printf("ERROR: could not find data tag (%s)\n", filename);
+                       return 0;
+               }
+       }
+
+       osd_close(file);
+
+       /* if there was a 0 length data block, we're done */
+       if (length == 0)
+       {
+               printf("ERROR: empty data block (%s)\n", filename);
+               return 0;
+       }
+
+       *dataoffs = offset;
+
+       return length;
+}
+
+UINT16 read_uint16(FILE *infile)
+{
+       UINT16 res = 0;
+       unsigned char buffer[2];
+
+       fread(buffer, 2, 1, infile);
+
+       res = buffer[1] | buffer[0]<<8;
+
+       return res;
+}
+
+UINT32 read_uint32(FILE *infile)
+{
+       UINT32 res = 0;
+       unsigned char buffer[4];
+
+       fread(buffer, 4, 1, infile);
+
+       res = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24;
+
+       return res;
+}
+
+UINT64 read_uint64(FILE *infile)
+{
+       UINT64 res0 = U64(0), res1 = U64(0);
+       UINT64 res;
+       unsigned char buffer[8];
+
+       fread(buffer, 8, 1, infile);
+
+       res0 = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24;
+       res1 = buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24;
+
+       res = res0<<32 | res1;
+
+       return res;
+}
+
+/*-------------------------------------------------
+    chdcd_parse_nero - parse a Nero .NRG file
+-------------------------------------------------*/
+
+chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
+{
+       FILE *infile;
+       unsigned char buffer[12];
+       UINT32 chain_offs, chunk_size;
+       int done = 0;
+
+       astring path = astring(tocfname);
+
+       infile = fopen(tocfname, "rb");
+       path = get_file_path(path);
+
+       if (infile == (FILE *)NULL)
+       {
+               return CHDERR_FILE_NOT_FOUND;
+       }
+
+       /* clear structures */
+       memset(&outtoc, 0, sizeof(outtoc));
+       outinfo.reset();
+
+       // seek to 12 bytes before the end
+       fseek(infile, -12, SEEK_END);
+       fread(buffer, 12, 1, infile);
+
+       if (memcmp(buffer, "NER5", 4))
+       {
+               printf("ERROR: Not a Nero 5.5 or later image!\n");
+               fclose(infile);
+               return CHDERR_UNSUPPORTED_VERSION;
+       }
+
+       chain_offs = buffer[11] | (buffer[10]<<8) | (buffer[9]<<16) | (buffer[8]<<24);
+
+       if ((buffer[7] != 0) || (buffer[6] != 0) || (buffer[5] != 0) || (buffer[4] != 0))
+       {
+               printf("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it.");
+               fclose(infile);
+               return CHDERR_UNSUPPORTED_FORMAT;
+       }
+
+//  printf("NER5 detected, chain offset: %x\n", chain_offs);
+
+       while (!done)
+       {
+               UINT32 offset;
+               UINT8 start, end;
+               int track;
+
+               fseek(infile, chain_offs, SEEK_SET);
+               fread(buffer, 8, 1, infile);
+
+               chunk_size = (buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24);
+
+//      printf("Chunk type: %c%c%c%c, size %x\n", buffer[0], buffer[1], buffer[2], buffer[3], chunk_size);
+
+               // we want the DAOX chunk, which has the TOC information
+               if (!memcmp(buffer, "DAOX", 4))
+               {
+                       // skip second chunk size and UPC code
+                       fseek(infile, 20, SEEK_CUR);
+
+                       fread(&start, 1, 1, infile);
+                       fread(&end, 1, 1, infile);
+
+//          printf("Start track %d  End track: %d\n", start, end);
+
+                       outtoc.numtrks = (end-start) + 1;
+
+                       offset = 0;
+                       for (track = start; track <= end; track++)
+                       {
+                               UINT32 size, mode;
+                               UINT64 index0, index1, track_end;
+
+                               fseek(infile, 12, SEEK_CUR);    // skip ISRC code
+                               size = read_uint16(infile);
+                               mode = read_uint16(infile);
+                               fseek(infile, 2, SEEK_CUR);
+                               index0 = read_uint64(infile);
+                               index1 = read_uint64(infile);
+                               track_end = read_uint64(infile);
+
+//              printf("Track %d: sector size %d mode %x index0 %llx index1 %llx track_end %llx (pregap %d sectors, length %d sectors)\n", track, size, mode, index0, index1, track_end, (UINT32)(index1-index0)/size, (UINT32)(track_end-index1)/size);
+                               outinfo.track[track-1].fname.cpy(tocfname);
+                               outinfo.track[track-1].offset = offset + (UINT32)(index1-index0);
+                               outinfo.track[track-1].idx0offs = 0;
+                               outinfo.track[track-1].idx1offs = 0;
+
+                               switch (mode)
+                               {
+                                       case 0x0000:    // 2048 byte data
+                                               outtoc.tracks[track-1].trktype = CD_TRACK_MODE1;
+                                               outinfo.track[track-1].swap = false;
+                                               break;
+
+                                       case 0x0300:    // Mode 2 Form 1
+                                               printf("ERROR: Mode 2 Form 1 tracks not supported\n");
+                                               fclose(infile);
+                                               return CHDERR_UNSUPPORTED_FORMAT;
+
+                                       case 0x0500:    // raw data
+                                               printf("ERROR: Raw data tracks not supported\n");
+                                               fclose(infile);
+                                               return CHDERR_UNSUPPORTED_FORMAT;
+
+                                       case 0x0600:    // 2352 byte mode 2 raw
+                                               outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW;
+                                               outinfo.track[track-1].swap = false;
+                                               break;
+
+                                       case 0x0700:    // 2352 byte audio
+                                               outtoc.tracks[track-1].trktype = CD_TRACK_AUDIO;
+                                               outinfo.track[track-1].swap = true;
+                                               break;
+
+                                       case 0x0f00:    // raw data with sub-channel
+                                               printf("ERROR: Raw data tracks with sub-channel not supported\n");
+                                               fclose(infile);
+                                               return CHDERR_UNSUPPORTED_FORMAT;
+
+                                       case 0x1000:    // audio with sub-channel
+                                               printf("ERROR: Audio tracks with sub-channel not supported\n");
+                                               fclose(infile);
+                                               return CHDERR_UNSUPPORTED_FORMAT;
+
+                                       case 0x1100:    // raw Mode 2 Form 1 with sub-channel
+                                               printf("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n");
+                                               fclose(infile);
+                                               return CHDERR_UNSUPPORTED_FORMAT;
+
+                                       default:
+                                               printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode);
+                                               fclose(infile);
+                                               return CHDERR_UNSUPPORTED_FORMAT;
+                               }
+
+                               outtoc.tracks[track-1].datasize = size;
+
+                               outtoc.tracks[track-1].subtype = CD_SUB_NONE;
+                               outtoc.tracks[track-1].subsize = 0;
+
+                               outtoc.tracks[track-1].pregap = (UINT32)(index1-index0)/size;
+                               outtoc.tracks[track-1].frames = (UINT32)(track_end-index1)/size;
+                               outtoc.tracks[track-1].postgap = 0;
+                               outtoc.tracks[track-1].pgtype = 0;
+                               outtoc.tracks[track-1].pgsub = CD_SUB_NONE;
+                               outtoc.tracks[track-1].pgdatasize = 0;
+                               outtoc.tracks[track-1].pgsubsize = 0;
+                               outtoc.tracks[track-1].padframes = 0;
+
+                               offset += (UINT32)track_end-index1;
+                       }
+               }
+
+               if (!memcmp(buffer, "END!", 4))
+               {
+                       done = 1;
+               }
+               else
+               {
+                       chain_offs += chunk_size + 8;
+               }
+       }
+
+       fclose(infile);
+
+       return CHDERR_NONE;
+}
+
+/*-------------------------------------------------
+    chdcd_parse_iso - parse a .ISO file
+-------------------------------------------------*/
+
+chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
+{
+       FILE *infile;
+       astring path = astring(tocfname);
+
+       infile = fopen(tocfname, "rb");
+       path = get_file_path(path);
+
+       if (infile == (FILE *)NULL)
+       {
+               return CHDERR_FILE_NOT_FOUND;
+       }
+
+       /* clear structures */
+       memset(&outtoc, 0, sizeof(outtoc));
+       outinfo.reset();
+
+       fseek(infile, 0, SEEK_END);
+       long size = ftell(infile);
+       fclose(infile);
+
+
+       outtoc.numtrks = 1;
+
+       outinfo.track[0].fname = tocfname;
+       outinfo.track[0].offset = 0;
+       outinfo.track[0].idx0offs = 0;
+       outinfo.track[0].idx1offs = 0;
+
+       if ((size % 2048)==0 ) {
+               outtoc.tracks[0].trktype = CD_TRACK_MODE1;
+               outtoc.tracks[0].frames = size / 2048;
+               outtoc.tracks[0].datasize = 2048;
+               outinfo.track[0].swap = false;
+       } else if ((size % 2352)==0 ) {
+               // 2352 byte mode 2 raw
+               outtoc.tracks[0].trktype = CD_TRACK_MODE2_RAW;
+               outtoc.tracks[0].frames = size / 2352;
+               outtoc.tracks[0].datasize = 2352;
+               outinfo.track[0].swap = false;
+       } else {
+               printf("ERROR: Unrecognized track type\n");
+               return CHDERR_UNSUPPORTED_FORMAT;
+       }
+
+       outtoc.tracks[0].subtype = CD_SUB_NONE;
+       outtoc.tracks[0].subsize = 0;
+
+       outtoc.tracks[0].pregap = 0;
+
+       outtoc.tracks[0].postgap = 0;
+       outtoc.tracks[0].pgtype = 0;
+       outtoc.tracks[0].pgsub = CD_SUB_NONE;
+       outtoc.tracks[0].pgdatasize = 0;
+       outtoc.tracks[0].pgsubsize = 0;
+       outtoc.tracks[0].padframes = 0;
+
+
+       return CHDERR_NONE;
+}
+
+/*-------------------------------------------------
+    chdcd_parse_gdi - parse a Sega GD-ROM rip
+-------------------------------------------------*/
+
+static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
+{
+       FILE *infile;
+       int i, numtracks;
+
+       astring path = astring(tocfname);
+
+       infile = fopen(tocfname, "rt");
+       path = get_file_path(path);
+
+       if (infile == (FILE *)NULL)
+       {
+               return CHDERR_FILE_NOT_FOUND;
+       }
+
+       /* clear structures */
+       memset(&outtoc, 0, sizeof(outtoc));
+       outinfo.reset();
+
+       outtoc.flags = CD_FLAG_GDROM;
+
+       fgets(linebuffer,511,infile);
+       numtracks=atoi(linebuffer);
+
+       for(i=0;i<numtracks;++i)
+       {
+               char *tok;
+               int trknum;
+               int trksize,trktype;
+               int sz;
+
+               fgets(linebuffer,511,infile);
+
+               tok=strtok(linebuffer," ");
+
+               trknum=atoi(tok)-1;
+
+               outinfo.track[trknum].swap=false;
+               outinfo.track[trknum].offset=0;
+
+               outtoc.tracks[trknum].datasize = 0;
+               outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+               outtoc.tracks[trknum].subsize = 0;
+               outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
+
+               tok=strtok(NULL," ");
+               outtoc.tracks[trknum].physframeofs=atoi(tok);
+
+               tok=strtok(NULL," ");
+               trktype=atoi(tok);
+
+               tok=strtok(NULL," ");
+               trksize=atoi(tok);
+
+               if(trktype==4 && trksize==2352)
+               {
+                       outtoc.tracks[trknum].trktype=CD_TRACK_MODE1_RAW;
+                       outtoc.tracks[trknum].datasize=2352;
+               }
+               if(trktype==4 && trksize==2048)
+               {
+                       outtoc.tracks[trknum].trktype=CD_TRACK_MODE1;
+                       outtoc.tracks[trknum].datasize=2048;
+               }
+               if(trktype==0)
+               {
+                       outtoc.tracks[trknum].trktype=CD_TRACK_AUDIO;
+                       outtoc.tracks[trknum].datasize=2352;
+                       outinfo.track[trknum].swap = true;
+               }
+
+               astring name;
+
+               tok=strtok(NULL," ");
+               name = tok;
+               if (tok[0]=='"') {
+                       do {
+                               tok=strtok(NULL," ");
+                               if (tok!=NULL) {
+                                       name += " ";
+                                       name += tok;
+                               }
+                       } while(tok!=NULL && (strrchr(tok,'"')-tok !=(strlen(tok)-1)));
+                       name = name.delchr('"');
+               }
+               outinfo.track[trknum].fname.cpy(path).cat(name);
+
+               sz = get_file_size(outinfo.track[trknum].fname);
+
+               outtoc.tracks[trknum].frames = sz/trksize;
+               outtoc.tracks[trknum].padframes = 0;
+
+               if (trknum != 0)
+               {
+                       int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
+                       outtoc.tracks[trknum-1].frames += dif;
+                       outtoc.tracks[trknum-1].padframes = dif;
+               }
+       }
+
+       #if 0
+       for(i=0; i < numtracks; i++)
+       {
+               printf("%s %d %d %d (true %d)\n", outinfo.track[i].fname.cstr(), outtoc.tracks[i].frames, outtoc.tracks[i].padframes, outtoc.tracks[i].physframeofs, outtoc.tracks[i].frames - outtoc.tracks[i].padframes);
+       }
+       #endif
+
+       /* close the input TOC */
+       fclose(infile);
+
+       /* store the number of tracks found */
+       outtoc.numtrks = numtracks;
+
+       return CHDERR_NONE;
+}
+
+/*-------------------------------------------------
+    chdcd_parse_cue - parse a CDRWin format CUE file
+-------------------------------------------------*/
+
+chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
+{
+       FILE *infile;
+       int i, trknum;
+       static char token[512];
+       astring lastfname;
+       UINT32 wavlen, wavoffs;
+       astring path = astring(tocfname);
+
+       infile = fopen(tocfname, "rt");
+       path = get_file_path(path);
+       if (infile == (FILE *)NULL)
+       {
+               return CHDERR_FILE_NOT_FOUND;
+       }
+
+       /* clear structures */
+       memset(&outtoc, 0, sizeof(outtoc));
+       outinfo.reset();
+
+       trknum = -1;
+       wavoffs = wavlen = 0;
+
+       while (!feof(infile))
+       {
+               /* get the next line */
+               fgets(linebuffer, 511, infile);
+
+               /* if EOF didn't hit, keep going */
+               if (!feof(infile))
+               {
+                       i = 0;
+
+                       TOKENIZE
+
+                       if (!strcmp(token, "FILE"))
+                       {
+                               /* found the data file for a track */
+                               TOKENIZE
+
+                               /* keep the filename */
+                               lastfname.cpy(path).cat(token);
+
+                               /* get the file type */
+                               TOKENIZE
+
+                               if (!strcmp(token, "BINARY"))
+                               {
+                                       outinfo.track[trknum+1].swap = false;
+                               }
+                               else if (!strcmp(token, "MOTOROLA"))
+                               {
+                                       outinfo.track[trknum+1].swap = true;
+                               }
+                               else if (!strcmp(token, "WAVE"))
+                               {
+                                       wavlen = parse_wav_sample(lastfname, &wavoffs);
+                                       if (!wavlen)
+                                       {
+                                               printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.cstr());
+                                               return CHDERR_INVALID_DATA;
+                                       }
+                               }
+                               else
+                               {
+                                       printf("ERROR: Unhandled track type %s\n", token);
+                                       return CHDERR_UNSUPPORTED_FORMAT;
+                               }
+                       }
+                       else if (!strcmp(token, "TRACK"))
+                       {
+                               /* get the track number */
+                               TOKENIZE
+                               trknum = strtoul(token, NULL, 10) - 1;
+
+                               /* next token on the line is the track type */
+                               TOKENIZE
+
+                               if (wavlen != 0)
+                               {
+                                       outtoc.tracks[trknum].trktype = CD_TRACK_AUDIO;
+                                       outtoc.tracks[trknum].frames = wavlen/2352;
+                                       outinfo.track[trknum].offset = wavoffs;
+                                       wavoffs = wavlen = 0;
+                               }
+                               else
+                               {
+                                       outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
+                                       outtoc.tracks[trknum].datasize = 0;
+                                       outinfo.track[trknum].offset = 0;
+                               }
+                               outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+                               outtoc.tracks[trknum].subsize = 0;
+                               outtoc.tracks[trknum].pregap = 0;
+                               outtoc.tracks[trknum].padframes = 0;
+                               outinfo.track[trknum].idx0offs = -1;
+                               outinfo.track[trknum].idx1offs = 0;
+
+                               outinfo.track[trknum].fname.cpy(lastfname); // default filename to the last one
+
+//              printf("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname.cstr(), outinfo.track[trknum].offset);
+
+                               cdrom_convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+                               if (outtoc.tracks[trknum].datasize == 0)
+                               {
+                                       printf("ERROR: Unknown track type [%s].  Contact MAMEDEV.\n", token);
+                                       return CHDERR_UNSUPPORTED_FORMAT;
+                               }
+
+                               /* next (optional) token on the line is the subcode type */
+                               TOKENIZE
+
+                               cdrom_convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+                       }
+                       else if (!strcmp(token, "INDEX"))   /* only in bin/cue files */
+                       {
+                               int idx, frames;
+
+                               /* get index number */
+                               TOKENIZE
+                               idx = strtoul(token, NULL, 10);
+
+                               /* get index */
+                               TOKENIZE
+                               frames = msf_to_frames( token );
+
+                               if (idx == 0)
+                               {
+                                       outinfo.track[trknum].idx0offs = frames;
+                               }
+                               else if (idx == 1)
+                               {
+                                       outinfo.track[trknum].idx1offs = frames;
+                                       if ((outtoc.tracks[trknum].pregap == 0) && (outinfo.track[trknum].idx0offs != -1))
+                                       {
+                                               outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx0offs;
+                                               outtoc.tracks[trknum].pgtype = outtoc.tracks[trknum].trktype;
+                                               switch (outtoc.tracks[trknum].pgtype)
+                                               {
+                                                       case CD_TRACK_MODE1:
+                                                       case CD_TRACK_MODE2_FORM1:
+                                                               outtoc.tracks[trknum].pgdatasize = 2048;
+                                                               break;
+
+                                                       case CD_TRACK_MODE1_RAW:
+                                                       case CD_TRACK_MODE2_RAW:
+                                                       case CD_TRACK_AUDIO:
+                                                               outtoc.tracks[trknum].pgdatasize = 2352;
+                                                               break;
+
+                                                       case CD_TRACK_MODE2:
+                                                       case CD_TRACK_MODE2_FORM_MIX:
+                                                               outtoc.tracks[trknum].pgdatasize = 2336;
+                                                               break;
+
+                                                       case CD_TRACK_MODE2_FORM2:
+                                                               outtoc.tracks[trknum].pgdatasize = 2324;
+                                                               break;
+                                               }
+                                       }
+                                       else    // pregap sectors not in file, but we're always using idx0ofs for track length calc now
+                                       {
+                                               outinfo.track[trknum].idx0offs = frames;
+                                       }
+                               }
+                       }
+                       else if (!strcmp(token, "PREGAP"))
+                       {
+                               int frames;
+
+                               /* get index */
+                               TOKENIZE
+                               frames = msf_to_frames( token );
+
+                               outtoc.tracks[trknum].pregap = frames;
+                       }
+                       else if (!strcmp(token, "POSTGAP"))
+                       {
+                               int frames;
+
+                               /* get index */
+                               TOKENIZE
+                               frames = msf_to_frames( token );
+
+                               outtoc.tracks[trknum].postgap = frames;
+                       }
+               }
+       }
+
+       /* close the input CUE */
+       fclose(infile);
+
+       /* store the number of tracks found */
+       outtoc.numtrks = trknum + 1;
+
+       /* now go over the files again and set the lengths */
+       for (trknum = 0; trknum < outtoc.numtrks; trknum++)
+       {
+               UINT64 tlen = 0;
+
+               // this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian
+               if (outtoc.tracks[trknum].trktype == CD_TRACK_AUDIO)
+               {
+                       outinfo.track[trknum].swap = true;
+               }
+
+               // don't do this for .WAV tracks, we already have their length and offset filled out
+               if (outinfo.track[trknum].offset == 0)
+               {
+                       // is this the last track?
+                       if (trknum == (outtoc.numtrks-1))
+                       {
+                               /* if we have the same filename as the last track, do it that way */
+                               if (trknum != 0 && outinfo.track[trknum].fname == outinfo.track[trknum-1].fname)
+                               {
+                                       tlen = get_file_size(outinfo.track[trknum].fname.cstr());
+                                       if (tlen == 0)
+                                       {
+                                               printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.cstr());
+                                               return CHDERR_FILE_NOT_FOUND;
+                                       }
+                                       outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
+                                       outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+                               }
+                               else    /* data files are different */
+                               {
+                                       tlen = get_file_size(outinfo.track[trknum].fname);
+                                       if (tlen == 0)
+                                       {
+                                               printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.cstr());
+                                               return CHDERR_FILE_NOT_FOUND;
+                                       }
+                                       tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+                                       outtoc.tracks[trknum].frames = tlen;
+                                       outinfo.track[trknum].offset = 0;
+                               }
+                       }
+                       else
+                       {
+                               /* if we have the same filename as the next track, do it that way */
+                               if (outinfo.track[trknum].fname == outinfo.track[trknum+1].fname)
+                               {
+                                       outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx0offs - outinfo.track[trknum].idx0offs;
+
+                                       if (trknum == 0)    // track 0 offset is 0
+                                       {
+                                               outinfo.track[trknum].offset = 0;
+                                       }
+                                       else
+                                       {
+                                               outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
+                                       }
+
+                                       if (!outtoc.tracks[trknum].frames)
+                                       {
+                                               printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
+                                               return CHDERR_INVALID_DATA;
+                                       }
+                               }
+                               else    /* data files are different */
+                               {
+                                       tlen = get_file_size(outinfo.track[trknum].fname);
+                                       if (tlen == 0)
+                                       {
+                                               printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.cstr());
+                                               return CHDERR_FILE_NOT_FOUND;
+                                       }
+                                       tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+                                       outtoc.tracks[trknum].frames = tlen;
+                                       outinfo.track[trknum].offset = 0;
+                               }
+                       }
+               }
+               //printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc.tracks[trknum].frames, outinfo.track[trknum].offset);
+       }
+
+       return CHDERR_NONE;
+}
+
+/*-------------------------------------------------
+    chdcd_parse_toc - parse a CDRDAO format TOC file
+-------------------------------------------------*/
+
+chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
+{
+       FILE *infile;
+       int i, trknum;
+       static char token[512];
+       char tocftemp[512];
+
+       strcpy(tocftemp, tocfname);
+       for (i = 0; i < strlen(tocfname); i++)
+       {
+               tocftemp[i] = tolower(tocftemp[i]);
+       }
+
+       if (strstr(tocftemp,".gdi"))
+       {
+               return chdcd_parse_gdi(tocfname, outtoc, outinfo);
+       }
+
+       if (strstr(tocftemp,".cue"))
+       {
+               return chdcd_parse_cue(tocfname, outtoc, outinfo);
+       }
+
+       if (strstr(tocftemp,".nrg"))
+       {
+               return chdcd_parse_nero(tocfname, outtoc, outinfo);
+       }
+
+       if (strstr(tocftemp,".iso") || strstr(tocftemp,".cdr"))
+       {
+               return chdcd_parse_iso(tocfname, outtoc, outinfo);
+       }
+
+       astring path = astring(tocfname);
+
+       infile = fopen(tocfname, "rt");
+       path = get_file_path(path);
+
+       if (infile == (FILE *)NULL)
+       {
+               return CHDERR_FILE_NOT_FOUND;
+       }
+
+       /* clear structures */
+       memset(&outtoc, 0, sizeof(outtoc));
+       outinfo.reset();
+
+       trknum = -1;
+
+       while (!feof(infile))
+       {
+               /* get the next line */
+               fgets(linebuffer, 511, infile);
+
+               /* if EOF didn't hit, keep going */
+               if (!feof(infile))
+               {
+                       i = 0;
+
+                       TOKENIZE
+
+                       if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE")))
+                       {
+                               int f;
+
+                               /* found the data file for a track */
+                               TOKENIZE
+
+                               /* keep the filename */
+                               outinfo.track[trknum].fname.cpy(path).cat(token);
+
+                               /* get either the offset or the length */
+                               TOKENIZE
+
+                               if (!strcmp(token, "SWAP"))
+                               {
+                                       TOKENIZE
+
+                                       outinfo.track[trknum].swap = true;
+                               }
+                               else
+                               {
+                                       outinfo.track[trknum].swap = false;
+                               }
+
+                               if (token[0] == '#')
+                               {
+                                       /* it's a decimal offset, use it */
+                                       f = strtoul(&token[1], NULL, 10);
+                               }
+                               else if (isdigit((UINT8)token[0]))
+                               {
+                                       /* convert the time to an offset */
+                                       f = msf_to_frames( token );
+
+                                       f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+                               }
+                               else
+                               {
+                                       f = 0;
+                               }
+
+                               outinfo.track[trknum].offset = f;
+
+                               TOKENIZE
+
+                               if (isdigit((UINT8)token[0]))
+                               {
+                                       // this could be the length or an offset from the previous field.
+                                       f = msf_to_frames( token );
+
+                                       TOKENIZE
+
+                                       if (isdigit((UINT8)token[0]))
+                                       {
+                                               // it was an offset.
+                                               f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+
+                                               outinfo.track[trknum].offset += f;
+
+                                               // this is the length.
+                                               f = msf_to_frames( token );
+                                       }
+                               }
+                               else if( trknum == 0 && outinfo.track[trknum].offset != 0 )
+                               {
+                                       /* the 1st track might have a length with no offset */
+                                       f = outinfo.track[trknum].offset / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+                                       outinfo.track[trknum].offset = 0;
+                               }
+                               else
+                               {
+                                       /* guesstimate the track length? */
+                                       f = 0;
+                               }
+
+                               outtoc.tracks[trknum].frames = f;
+                       }
+                       else if (!strcmp(token, "TRACK"))
+                       {
+                               trknum++;
+
+                               /* next token on the line is the track type */
+                               TOKENIZE
+
+                               outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
+                               outtoc.tracks[trknum].datasize = 0;
+                               outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+                               outtoc.tracks[trknum].subsize = 0;
+                               outtoc.tracks[trknum].padframes = 0;
+
+                               cdrom_convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+                               if (outtoc.tracks[trknum].datasize == 0)
+                               {
+                                       printf("ERROR: Unknown track type [%s].  Contact MAMEDEV.\n", token);
+                                       return CHDERR_UNSUPPORTED_FORMAT;
+                               }
+
+                               /* next (optional) token on the line is the subcode type */
+                               TOKENIZE
+
+                               cdrom_convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+                       }
+                       else if (!strcmp(token, "START"))
+                       {
+                               int frames;
+
+                               /* get index */
+                               TOKENIZE
+                               frames = msf_to_frames( token );
+
+                               outtoc.tracks[trknum].pregap = frames;
+                       }
+               }
+       }
+
+       /* close the input TOC */
+       fclose(infile);
+
+       /* store the number of tracks found */
+       outtoc.numtrks = trknum + 1;
+
+       return CHDERR_NONE;
+}
index ac8b1831502c17cdbb78a991468e284702e45d16..e428ad590515f9aefc0f1b0ebb32c5414220a10e 100644 (file)
@@ -19,9 +19,9 @@ 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
+       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;
 };
@@ -34,6 +34,6 @@ struct chdcd_track_input_info
 };
 
 
-chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo);
+chd_error chdcd_parse_toc(const TCHAR *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo);
 
-#endif /* __CHDCD_H__ */
+#endif  /* __CHDCD_H__ */
index a6549ea834f571ff91341b00a053d863c3a4155f..e4de237d271811202d08bc691cff85b67cd5cb0c 100644 (file)
@@ -1,42 +1,12 @@
 #include "chdtypes.h"
-
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     cdrom.c
 
     Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this
 
-****************************************************************************
-
-    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:
     DEBUGGING
 ***************************************************************************/
 
-#define VERBOSE        (0)
+#define VERBOSE (0)
 #if VERBOSE
 #define LOG(x) do { if (VERBOSE) logerror x; } while (0)
-void CLIB_DECL logerror(const char *text,...);
+void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
 #else
 #define LOG(x)
 #endif
@@ -71,18 +41,18 @@ void CLIB_DECL logerror(const char *text,...);
     CONSTANTS
 ***************************************************************************/
 
-const int SYNC_OFFSET = 0x000;         // offset within sector
-const int SYNC_NUM_BYTES = 12;         // 12 bytes
+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 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_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
+const int ECC_Q_NUM_BYTES = 52;     // 2 lots of 52
+const int ECC_Q_COMP = 43;          // 43 bytes each
 
 
 
@@ -92,10 +62,10 @@ const int ECC_Q_COMP = 43;                  // 43 bytes each
 
 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 */
+       chd_file *          chd;                /* CHD file */
+       cdrom_toc           cdtoc;              /* TOC for the CD */
+       chdcd_track_input_info track_info;      /* track info */
+       core_file *         fhandle[CD_MAX_TRACKS];/* file handle */
 };
 
 
@@ -126,18 +96,48 @@ INLINE UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 &trac
        return physlba;
 }
 
+/*-------------------------------------------------
+    logical_to_chd_lba - find the CHD LBA
+    and the track number
+-------------------------------------------------*/
+
+INLINE UINT32 logical_to_chd_lba(cdrom_file *file, UINT32 loglba, UINT32 &tracknum)
+{
+       UINT32 chdlba, physlba;
+       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 (loglba < file->cdtoc.tracks[track + 1].logframeofs)
+               {
+                       // is this a no-pregap-data track?  compensate for the logical offset pointing to the "wrong" sector.
+                       if ((file->cdtoc.tracks[track].pgdatasize == 0) && (loglba > file->cdtoc.tracks[track].pregap))
+                       {
+                               loglba -= file->cdtoc.tracks[track].pregap;
+                       }
+
+                       // convert to physical and proceed
+                       physlba = file->cdtoc.tracks[track].physframeofs + (loglba - file->cdtoc.tracks[track].logframeofs);
+                       chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs;
+                       tracknum = track;
+                       return chdlba;
+               }
+       }
+
+       return loglba;
+}
 
 
 /***************************************************************************
     BASE FUNCTIONALITY
 ***************************************************************************/
-
 #if 0
-cdrom_file *cdrom_open(const char *inputfile)
+cdrom_file *cdrom_open(const TCHAR *inputfile)
 {
        int i;
        cdrom_file *file;
-       UINT32 physofs;
+       UINT32 physofs, logofs;
 
        /* allocate memory for the CD-ROM file */
        file = new cdrom_file();
@@ -169,35 +169,51 @@ cdrom_file *cdrom_open(const char *inputfile)
                }
        }
        /* 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;
+          pads tracks out with extra frames to fit 4-frame size boundries
+       */
+       physofs = logofs = 0;
        for (i = 0; i < file->cdtoc.numtrks; i++)
        {
                file->cdtoc.tracks[i].physframeofs = physofs;
                file->cdtoc.tracks[i].chdframeofs = 0;
+               file->cdtoc.tracks[i].logframeofs = logofs;
 
-               physofs += file->cdtoc.tracks[i].frames;
+               // if the pregap sectors aren't in the track, add them to the track's logical length
+               if (file->cdtoc.tracks[i].pgdatasize == 0)
+               {
+                       logofs += file->cdtoc.tracks[i].pregap;
+               }
 
-               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));
+               // postgap adds to the track length
+               logofs += file->cdtoc.tracks[i].postgap;
+
+               physofs += file->cdtoc.tracks[i].frames;
+               logofs  += file->cdtoc.tracks[i].frames;
+
+/*      printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %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].pregap,
+            file->cdtoc.tracks[i].pgtype,
+            file->cdtoc.tracks[i].pgdatasize,
+            file->cdtoc.tracks[i].postgap,
+            file->cdtoc.tracks[i].logframeofs,
+            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].logframeofs = logofs;
        file->cdtoc.tracks[i].chdframeofs = 0;
 
        return file;
 }
 #endif
-
 /*-------------------------------------------------
     cdrom_open - "open" a CD-ROM file from an
     already-opened CHD file
@@ -207,7 +223,7 @@ cdrom_file *cdrom_open(chd_file *chd)
 {
        int i;
        cdrom_file *file;
-       UINT32 physofs, chdofs;
+       UINT32 physofs, chdofs, logofs;
        chd_error err;
 
        /* punt if no CHD */
@@ -239,31 +255,48 @@ cdrom_file *cdrom_open(chd_file *chd)
        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;
+          pads tracks out with extra frames to fit 4-frame size boundries
+       */
+       physofs = chdofs = logofs = 0;
        for (i = 0; i < file->cdtoc.numtrks; i++)
        {
                file->cdtoc.tracks[i].physframeofs = physofs;
                file->cdtoc.tracks[i].chdframeofs = chdofs;
+               file->cdtoc.tracks[i].logframeofs = logofs;
+
+               // if the pregap sectors aren't in the track, add them to the track's logical length
+               if (file->cdtoc.tracks[i].pgdatasize == 0)
+               {
+                       logofs += file->cdtoc.tracks[i].pregap;
+               }
+
+               // postgap counts against the next track
+               logofs += file->cdtoc.tracks[i].postgap;
 
                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));
+               logofs  += file->cdtoc.tracks[i].frames;
+
+/*      printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %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].pregap,
+            file->cdtoc.tracks[i].pgtype,
+            file->cdtoc.tracks[i].pgdatasize,
+            file->cdtoc.tracks[i].postgap,
+            file->cdtoc.tracks[i].logframeofs,
+            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].logframeofs = logofs;
        file->cdtoc.tracks[i].chdframeofs = chdofs;
 
        return file;
@@ -283,7 +316,7 @@ void cdrom_close(cdrom_file *file)
        {
                for (int i = 0; i < file->cdtoc.numtrks; i++)
                {
-                       zfile_fclose(file->fhandle[i]);
+                       core_fclose(file->fhandle[i]);
                }
        }
 
@@ -296,24 +329,46 @@ void cdrom_close(cdrom_file *file)
     CORE READ ACCESS
 ***************************************************************************/
 
-chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length)
+chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 lbasector, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length)
 {
+       chd_error result = CHDERR_NONE;
+       bool needswap = false;
+
+       // if this is pregap info that isn't actually in the file, just return blank data
+       if ((file->cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < (file->cdtoc.tracks[tracknum].logframeofs + file->cdtoc.tracks[tracknum].pregap)))
+       {
+//      printf("PG missing sector: LBA %d, trklog %d\n", lbasector, file->cdtoc.tracks[tracknum].logframeofs);
+               memset(dest, 0, length);
+               return result;
+       }
+
        // 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];
+       {
+               result = file->chd->read_bytes(UINT64(chdsector) * UINT64(CD_FRAME_SIZE) + startoffs, dest, length);
+               /* swap CDDA in the case of LE GDROMs */
+               if ((file->cdtoc.flags & CD_FLAG_GDROMLE) && (file->cdtoc.tracks[tracknum].trktype == CD_TRACK_AUDIO))
+                       needswap = true;
+       }
+       else
+       {
+               // 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;
 
-       UINT64 sourcefileoffset = file->track_info.track[tracknum].offset;
-       int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize;
+               sourcefileoffset += chdsector * bytespersector + startoffs;
 
-       sourcefileoffset += chdsector * bytespersector + startoffs;
+               //  printf("Reading sector %d from track %d at offset %lld\n", chdsector, tracknum, sourcefileoffset);
 
-       core_fseek(srcfile, sourcefileoffset, SEEK_SET);
-       core_fread(srcfile, dest, length);
+               core_fseek(srcfile, sourcefileoffset, SEEK_SET);
+               core_fread(srcfile, dest, length);
+
+               needswap = file->track_info.track[tracknum].swap;
+       }
 
-       if (file->track_info.track[tracknum].swap)
+       if (needswap)
        {
                UINT8 *buffer = (UINT8 *)dest - startoffs;
                for (int swapindex = startoffs; swapindex < 2352; swapindex += 2 )
@@ -323,8 +378,7 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UI
                        buffer[ swapindex + 1 ] = swaptemp;
                }
        }
-#endif
-       return CHDERR_NONE;
+       return result;
 }
 
 
@@ -333,27 +387,37 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UI
     from a CD-ROM
 -------------------------------------------------*/
 
-UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype)
+UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype, bool phys)
 {
        if (file == NULL)
                return 0;
 
        // compute CHD sector and tracknumber
        UINT32 tracknum = 0;
-       UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+       UINT32 chdsector;
+
+       if (phys)
+       {
+               chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+       }
+       else
+       {
+               chdsector = logical_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);
+               return (read_partial_sector(file, buffer, lbasector, 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 (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2048) == CHDERR_NONE);
                }
 
                /* return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data */
@@ -367,21 +431,21 @@ UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32
                        bufptr[12] = msf>>16;
                        bufptr[13] = msf>>8;
                        bufptr[14] = msf&0xff;
-                       bufptr[15] = 1; // mode 1
+                       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 (read_partial_sector(file, bufptr+16, lbasector, 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 (read_partial_sector(file, buffer, lbasector, 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);
+                       return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2336) == CHDERR_NONE);
                }
 
                LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype));
@@ -395,19 +459,29 @@ UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32
     a sector
 -------------------------------------------------*/
 
-UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer)
+UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer, bool phys)
 {
        if (file == NULL)
                return ~0;
 
        // compute CHD sector and tracknumber
        UINT32 tracknum = 0;
-       UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+       UINT32 chdsector;
+
+       if (phys)
+       {
+               chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+       }
+       else
+       {
+               chdsector = logical_to_chd_lba(file, lbasector, tracknum);
+       }
+
        if (file->cdtoc.tracks[tracknum].subsize == 0)
-               return 1;
+               return 0;
 
        // read the data
-       chd_error err = read_partial_sector(file, buffer, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize);
+       chd_error err = read_partial_sector(file, buffer, lbasector, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize);
        return (err == CHDERR_NONE);
 }
 
@@ -430,7 +504,8 @@ UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame)
                return ~0;
 
        /* convert to a CHD sector offset and get track information */
-       physical_to_chd_lba(file, frame, track);
+       logical_to_chd_lba(file, frame, track);
+
        return track;
 }
 
@@ -449,10 +524,25 @@ UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track)
        if (track == 0xaa)
                track = file->cdtoc.numtrks;
 
-       return file->cdtoc.tracks[track].physframeofs;
+       return file->cdtoc.tracks[track].logframeofs;
 }
 
+/*-------------------------------------------------
+    cdrom_get_track_start_phys - get the
+    physical frame number that a track starts at
+-------------------------------------------------*/
+
+UINT32 cdrom_get_track_start_phys(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
@@ -484,10 +574,10 @@ int cdrom_get_adr_control(cdrom_file *file, int track)
 
        if (track == 0xaa || file->cdtoc.tracks[track].trktype == CD_TRACK_AUDIO)
        {
-               return 0x10;    // audio track, subchannel is position
+               return 0x10;    // audio track, subchannel is position
        }
 
-       return 0x14;    // data track, subchannel is position
+       return 0x14;    // data track, subchannel is position
 }
 
 
@@ -679,15 +769,15 @@ 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";
+               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";
        }
 }
 
@@ -701,9 +791,9 @@ 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";
+               case CD_SUB_NORMAL:             return "RW";
+               case CD_SUB_RAW:                return "RW_RAW";
+               default:                        return "NONE";
        }
 }
 
@@ -724,6 +814,8 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
        chd_error err;
        int i;
 
+       toc->flags = 0;
+
        /* start with no tracks */
        for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++)
        {
@@ -739,7 +831,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
                {
                        /* parse the metadata */
                        type[0] = subtype[0] = 0;
-            pgtype[0] = pgsub[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)
@@ -750,36 +842,42 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
                {
                        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;
-                }
-            }
+                       {
+                               /* 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_OLD_METADATA_TAG, toc->numtrks, metadata);
+                               if (err == CHDERR_NONE)
+                                       /* legacy GDROM track was detected */
+                                       toc->flags |= CD_FLAG_GDROMLE;
+                               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];
+                                       toc->flags |= CD_FLAG_GDROM;
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
                }
 
                /* extract the track type and determine the data size */
@@ -796,7 +894,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
 
                /* set the frames and extra frames data */
                track->frames = frames;
-        track->padframes = padframes;
+               track->padframes = padframes;
                int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING;
                track->extraframes = padded * CD_TRACK_PADDING - frames;
 
@@ -806,18 +904,25 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
                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);
+               if (track->pregap > 0)
+               {
+                       if (pgtype[0] == 'V')
+                       {
+                               cdrom_convert_type_string_to_pregap_info(&pgtype[1], track);
+                       }
+
+                       cdrom_convert_subtype_string_to_pregap_info(pgsub, track);
+               }
 
-        /* set the postgap info */
-        track->postgap = postgap;
+               /* 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);
+       printf("toc->numtrks = %d?!\n", toc->numtrks);
 
        /* look for old-style metadata */
        dynamic_buffer oldmetadata;
@@ -868,7 +973,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
 /*-------------------------------------------------
     cdrom_write_metadata - write metadata
 -------------------------------------------------*/
-#if 0
+
 chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
 {
        chd_error err;
@@ -877,31 +982,42 @@ chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
        /* 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);
-        }
+               astring metadata;
+               if (!(toc->flags & CD_FLAG_GDROM))
+               {
+                       char submode[32];
+
+                       if (toc->tracks[i].pgdatasize > 0)
+                       {
+                               strcpy(&submode[1], cdrom_get_type_string(toc->tracks[i].pgtype));
+                               submode[0] = 'V';   // indicate valid submode
+                       }
+                       else
+                       {
+                               strcpy(submode, cdrom_get_type_string(toc->tracks[i].pgtype));
+                       }
+
+                       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,
+                                       submode, 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
@@ -1134,11 +1250,11 @@ void ecc_compute_bytes(const UINT8 *sector, const UINT16 *row, int rowlen, UINT8
        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];
+               val1 ^= ecc_source_byte(sector, row[component]);
+               val2 ^= ecc_source_byte(sector, row[component]);
+               val1 = ecclow[val1];
+       }
+       val1 = ecchigh[ecclow[val1] ^ val2];
        val2 ^= val1;
 }
 
@@ -1154,16 +1270,16 @@ bool ecc_verify(const UINT8 *sector)
        for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++)
        {
                UINT8 val1, val2;
-           ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, 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++)
+       for (int byte = 0; byte < ECC_Q_NUM_BYTES; byte++)
        {
                UINT8 val1, val2;
-           ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, 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;
        }
@@ -1180,11 +1296,11 @@ 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]);
+               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]);
+       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]);
 }
 
 
index dab39178c01da5f660148f3e3fb8c537cacaef85..c0115701e35d9d6bde4877c7394bcfaa52801d67 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
 // 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_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_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))
+#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 */
+       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 */
+       CD_SUB_NORMAL = 0,          /* "cooked" 96 bytes per sector */
+       CD_SUB_RAW,                 /* raw uninterleaved 96 bytes per sector */
+       CD_SUB_NONE                 /* no subcode data stored */
 };
 
-#define        CD_FLAG_GDROM   0x00000001      // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
+#define CD_FLAG_GDROM   0x00000001  // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
+#define CD_FLAG_GDROMLE 0x00000002  // legacy GD-ROM, with little-endian CDDA data
 
 /***************************************************************************
     TYPE DEFINITIONS
@@ -95,32 +67,33 @@ 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 */
+       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 */
+       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 */
+       /* fields used in MAME/MESS only */
+       UINT32 logframeofs; /* logical frame of actual track data - offset by pregap size if pregap not physically present */
+       UINT32 physframeofs; /* physical frame of actual track data in CHD data */
+       UINT32 chdframeofs; /* frame number this track starts at on the CHD */
 };
 
 
 struct cdrom_toc
 {
-       UINT32 numtrks;         /* number of tracks */
-       UINT32 flags;           /* see FLAG_ above */
+       UINT32 numtrks;     /* number of tracks */
+       UINT32 flags;       /* see FLAG_ above */
        cdrom_track_info tracks[CD_MAX_TRACKS];
 };
 
@@ -137,13 +110,13 @@ 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);
+UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype, bool phys=false);
+UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer, bool phys=false);
 
 /* handy utilities */
 UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame);
 UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track);
+UINT32 cdrom_get_track_start_phys(cdrom_file *file, UINT32 track);
 
 /* TOC utilities */
 int cdrom_get_last_track(cdrom_file *file);
@@ -187,8 +160,8 @@ INLINE UINT32 lba_to_msf(UINT32 lba)
        f = lba % 75;
 
        return ((m / 10) << 20) | ((m % 10) << 16) |
-              ((s / 10) << 12) | ((s % 10) <<  8) |
-              ((f / 10) <<  4) | ((f % 10) <<  0);
+                       ((s / 10) << 12) | ((s % 10) <<  8) |
+                       ((f / 10) <<  4) | ((f % 10) <<  0);
 }
 
 // segacd needs it like this.. investigate
@@ -208,4 +181,4 @@ INLINE UINT32 lba_to_msf_alt(int lba)
 
 
 
-#endif // __CDROM_H__
+#endif  // __CDROM_H__
index f12344622b8a9f26f755f6f0e68117f67143ca0f..fdc7c1cb5e8b9291a28c0fb07896ded0f408dd8c 100644 (file)
@@ -1,49 +1,19 @@
 #include "chdtypes.h"
-
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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 "avhuff.h"
 #include "flac.h"
 #include "chdcdrom.h"
-#include "huffman.h"
 #include <zlib.h>
 #include "7z/LzmaEnc.h"
 #include "7z/LzmaDec.h"
@@ -81,7 +51,7 @@ private:
        static void fast_free(voidpf opaque, voidpf address);
 
        static const int MAX_ZLIB_ALLOCS = 64;
-       UINT32 *                                m_allocptr[MAX_ZLIB_ALLOCS];
+       UINT32 *                m_allocptr[MAX_ZLIB_ALLOCS];
 };
 
 
@@ -100,8 +70,8 @@ public:
 
 private:
        // internal state
-       z_stream                                m_deflater;
-       chd_zlib_allocator              m_allocator;
+       z_stream                m_deflater;
+       chd_zlib_allocator      m_allocator;
 };
 
 
@@ -120,8 +90,8 @@ public:
 
 private:
        // internal state
-       z_stream                                m_inflater;
-       chd_zlib_allocator              m_allocator;
+       z_stream                m_inflater;
+       chd_zlib_allocator      m_allocator;
 };
 
 
@@ -141,7 +111,7 @@ private:
        static void fast_free(void *p, void *address);
 
        static const int MAX_LZMA_ALLOCS = 64;
-       UINT32 *                                m_allocptr[MAX_LZMA_ALLOCS];
+       UINT32 *                m_allocptr[MAX_LZMA_ALLOCS];
 };
 
 
@@ -163,8 +133,8 @@ public:
 
 private:
        // internal state
-       CLzmaEncProps                   m_props;
-       chd_lzma_allocator              m_allocator;
+       CLzmaEncProps           m_props;
+       chd_lzma_allocator      m_allocator;
 };
 
 
@@ -183,12 +153,11 @@ public:
 
 private:
        // internal state
-       CLzmaProps                              m_props;
-       CLzmaDec                                m_decoder;
-       chd_lzma_allocator              m_allocator;
+       CLzmaDec                m_decoder;
+       chd_lzma_allocator      m_allocator;
 };
 
-
+#if 0
 // ======================> chd_huffman_compressor
 
 // Huffman compressor
@@ -203,9 +172,9 @@ public:
 
 private:
        // internal state
-       huffman_8bit_encoder    m_encoder;
+       huffman_8bit_encoder    m_encoder;
 };
-
+#endif
 
 // ======================> chd_huffman_decompressor
 
@@ -221,10 +190,9 @@ public:
 
 private:
        // internal state
-       huffman_8bit_decoder    m_decoder;
+       huffman_8bit_decoder    m_decoder;
 };
 
-
 // ======================> chd_flac_compressor
 
 // FLAC compressor
@@ -242,11 +210,10 @@ public:
 
 private:
        // internal state
-       bool                    m_big_endian;
-       flac_encoder    m_encoder;
+       bool            m_big_endian;
+       flac_encoder    m_encoder;
 };
 
-
 // ======================> chd_flac_decompressor
 
 // FLAC decompressor
@@ -261,8 +228,8 @@ public:
 
 private:
        // internal state
-       bool                    m_big_endian;
-       flac_decoder    m_decoder;
+       bool            m_big_endian;
+       flac_decoder    m_decoder;
 };
 
 
@@ -284,11 +251,11 @@ public:
 
 private:
        // internal state
-       bool                            m_swap_endian;
-       flac_encoder            m_encoder;
-       z_stream                        m_deflater;
-       chd_zlib_allocator      m_allocator;
-       dynamic_buffer          m_buffer;
+       bool                m_swap_endian;
+       flac_encoder        m_encoder;
+       z_stream            m_deflater;
+       chd_zlib_allocator  m_allocator;
+       dynamic_buffer      m_buffer;
 };
 
 
@@ -307,11 +274,11 @@ public:
 
 private:
        // internal state
-       bool                            m_swap_endian;
-       flac_decoder            m_decoder;
-       z_stream                        m_inflater;
-       chd_zlib_allocator      m_allocator;
-       dynamic_buffer          m_buffer;
+       bool                m_swap_endian;
+       flac_decoder        m_decoder;
+       z_stream            m_inflater;
+       chd_zlib_allocator  m_allocator;
+       dynamic_buffer      m_buffer;
 };
 
 
@@ -324,9 +291,9 @@ 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)
+                       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)
@@ -378,9 +345,9 @@ public:
 
 private:
        // internal state
-       _BaseCompressor         m_base_compressor;
-       _SubcodeCompressor      m_subcode_compressor;
-       dynamic_buffer          m_buffer;
+       _BaseCompressor     m_base_compressor;
+       _SubcodeCompressor  m_subcode_compressor;
+       dynamic_buffer      m_buffer;
 };
 
 
@@ -393,9 +360,9 @@ 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)
+                       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)
@@ -438,9 +405,9 @@ public:
 
 private:
        // internal state
-       _BaseDecompressor       m_base_decompressor;
+       _BaseDecompressor   m_base_decompressor;
        _SubcodeDecompressor m_subcode_decompressor;
-       dynamic_buffer          m_buffer;
+       dynamic_buffer      m_buffer;
 };
 
 
@@ -462,8 +429,8 @@ private:
        void postinit();
 
        // internal state
-       avhuff_encoder                          m_encoder;
-       bool                                            m_postinit;
+       avhuff_encoder              m_encoder;
+       bool                        m_postinit;
 };
 
 
@@ -482,7 +449,7 @@ public:
 
 private:
        // internal state
-       avhuff_decoder                          m_decoder;
+       avhuff_decoder              m_decoder;
 };
 #endif
 
@@ -495,18 +462,21 @@ private:
 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> },
-
+       { 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> },
+#if 0
+       { 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> },
+#endif
        // 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> },
+       { 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> },
 
+#if 0
        // 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_AVHUFF,     false,  "A/V Huffman",          &chd_codec_list::construct_compressor<chd_avhuff_compressor>,   &chd_codec_list::construct_decompressor<chd_avhuff_decompressor> },
+#endif
 };
 
 
@@ -521,8 +491,8 @@ const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] =
 
 chd_codec::chd_codec(chd_file &chd, UINT32 hunkbytes, bool lossy)
        : m_chd(chd),
-         m_hunkbytes(hunkbytes),
-         m_lossy(lossy)
+               m_hunkbytes(hunkbytes),
+               m_lossy(lossy)
 {
 }
 
@@ -647,9 +617,9 @@ const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type t
 
 chd_compressor_group::chd_compressor_group(chd_file &chd, UINT32 compressor_list[4])
        : m_hunkbytes(chd.hunk_bytes()),
-         m_compress_test(m_hunkbytes)
+               m_compress_test(m_hunkbytes)
 #if CHDCODEC_VERIFY_COMPRESSION
-         ,m_decompressed(m_hunkbytes)
+               ,m_decompressed(m_hunkbytes)
 #endif
 {
        // verify the compression types and initialize the codecs
@@ -791,9 +761,6 @@ void chd_zlib_allocator::install(z_stream &stream)
 
 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
@@ -823,7 +790,6 @@ voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size)
        // set the low bit of the size so we don't match next time
        *ptr = size | 1;
        return ptr + 1;
-#endif
 }
 
 
@@ -834,9 +800,6 @@ voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size)
 
 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
@@ -848,7 +811,6 @@ void chd_zlib_allocator::fast_free(voidpf opaque, voidpf address)
                        *ptr &= ~1;
                        return;
                }
-#endif
 }
 
 
@@ -865,7 +827,7 @@ chd_zlib_compressor::chd_zlib_compressor(chd_file &chd, UINT32 hunkbytes, bool l
        : chd_compressor(chd, hunkbytes, lossy)
 {
        // initialize the deflater
-       m_deflater.next_in = (Bytef *)this;     // bogus, but that's ok
+       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);
@@ -930,8 +892,7 @@ chd_zlib_decompressor::chd_zlib_decompressor(chd_file &chd, UINT32 hunkbytes, bo
        : 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.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);
@@ -1217,7 +1178,7 @@ void chd_lzma_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *
 }
 
 
-
+#if 0
 //**************************************************************************
 //  HUFFMAN COMPRESSOR
 //**************************************************************************
@@ -1239,16 +1200,12 @@ chd_huffman_compressor::chd_huffman_compressor(chd_file &chd, UINT32 hunkbytes,
 
 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
 }
-
+#endif
 
 
 //**************************************************************************
@@ -1277,7 +1234,6 @@ void chd_huffman_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT
 }
 
 
-
 //**************************************************************************
 //  FLAC COMPRESSOR
 //**************************************************************************
@@ -1417,7 +1373,7 @@ void chd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *
 
 chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy)
        : chd_compressor(chd, hunkbytes, lossy),
-         m_buffer(hunkbytes)
+               m_buffer(hunkbytes)
 {
 #if 0
        // make sure the CHD's hunk size is an even multiple of the frame size
@@ -1436,7 +1392,7 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes,
        m_encoder.set_strip_metadata(true);
 
        // initialize the deflater
-       m_deflater.next_in = (Bytef *)this;     // bogus, but that's ok
+       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);
@@ -1446,6 +1402,8 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes,
                throw std::bad_alloc();
        else if (zerr != Z_OK)
                throw CHDERR_CODEC_ERROR;
+#else
+       throw CHDERR_CODEC_ERROR;
 #endif
 }
 
@@ -1524,7 +1482,6 @@ UINT32 chd_cd_flac_compressor::blocksize(UINT32 bytes)
 }
 
 
-
 //**************************************************************************
 //  CD FLAC DECOMPRESSOR
 //**************************************************************************
@@ -1535,7 +1492,7 @@ UINT32 chd_cd_flac_compressor::blocksize(UINT32 bytes)
 
 chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy)
        : chd_decompressor(chd, hunkbytes, lossy),
-         m_buffer(hunkbytes)
+               m_buffer(hunkbytes)
 {
        // make sure the CHD's hunk size is an even multiple of the frame size
        if (hunkbytes % CD_FRAME_SIZE != 0)
@@ -1547,7 +1504,7 @@ chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, UINT32 hunkbyt
        m_swap_endian = (native_endian == 1);
 
        // init the inflater
-       m_inflater.next_in = (Bytef *)this;     // bogus, but that's ok
+       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);
@@ -1624,7 +1581,7 @@ void chd_cd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT
 
 chd_avhuff_compressor::chd_avhuff_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy)
        : chd_compressor(chd, hunkbytes, lossy),
-         m_postinit(false)
+               m_postinit(false)
 {
        try
        {
@@ -1697,7 +1654,6 @@ void chd_avhuff_compressor::postinit()
 }
 
 
-
 //**************************************************************************
 //  AVHUFF DECOMPRESSOR
 //**************************************************************************
@@ -1719,7 +1675,6 @@ chd_avhuff_decompressor::chd_avhuff_decompressor(chd_file &chd, UINT32 hunkbytes
 
 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)
@@ -1734,6 +1689,7 @@ void chd_avhuff_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8
        }
 }
 
+
 //-------------------------------------------------
 //  config - codec-specific configuration for the
 //  A/V codec
@@ -1749,4 +1705,4 @@ void chd_avhuff_decompressor::configure(int param, void *config)
        else
                throw CHDERR_INVALID_PARAMETER;
 }
-#endif
\ No newline at end of file
+#endif
index a8d6f5382e8269ee5c995092c0b630a2789e37da..144fba8641f7b683c3ce76f1ac52e8066415a7d8 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
@@ -52,7 +23,7 @@
 //  MACROS
 //**************************************************************************
 
-#define CHD_MAKE_TAG(a,b,c,d)          (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+#define CHD_MAKE_TAG(a,b,c,d)       (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
 
 
 
@@ -90,9 +61,9 @@ public:
 
 private:
        // internal state
-       chd_file &                      m_chd;
-       UINT32                          m_hunkbytes;
-       bool                            m_lossy;
+       chd_file &          m_chd;
+       UINT32              m_hunkbytes;
+       bool                m_lossy;
 };
 
 
@@ -144,11 +115,11 @@ 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);
+               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
@@ -180,12 +151,12 @@ public:
 
 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
+       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
+       chd_decompressor *      m_decompressor[4];  // array of active codecs
+       dynamic_buffer          m_decompressed;     // verification buffer
 #endif
 };
 
@@ -196,21 +167,21 @@ private:
 //**************************************************************************
 
 // currently-defined codecs
-const chd_codec_type CHD_CODEC_NONE            = 0;
+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');
+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');
+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');
+const chd_codec_type CHD_CODEC_AVHUFF       = CHD_MAKE_TAG('a','v','h','u');
 
 // A/V codec configuration parameters
 enum
diff --git a/archivers/chd/chdglue.cpp b/archivers/chd/chdglue.cpp
new file mode 100644 (file)
index 0000000..2bf8af8
--- /dev/null
@@ -0,0 +1,88 @@
+
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include "zfile.h"
+#include "fsdb.h"
+#include "threaddep/thread.h"
+
+#include "chdtypes.h"
+#include "corefile.h"
+
+int core_fseek(core_file *file, INT64 offset, int whence)
+{
+       return zfile_fseek(file, offset, whence);
+}
+file_error core_fopen(const TCHAR *filename, UINT32 openflags, core_file **file)
+{
+       TCHAR *mode;
+
+       if (openflags & OPEN_FLAG_CREATE)
+               mode = _T("wb");
+       else if (openflags & OPEN_FLAG_WRITE)
+               mode = _T("rb+");
+       else
+               mode = _T("rb");
+       struct zfile *z = zfile_fopen(filename, mode);
+       *file = z;
+       return z == NULL ? FILERR_NOT_FOUND : FILERR_NONE;
+}
+void core_fclose(core_file *file)
+{
+       zfile_fclose(file);
+}
+UINT32 core_fread(core_file *file, void *buffer, UINT32 length)
+{
+       return zfile_fread(buffer, 1, length, file);
+}
+UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length)
+{
+       return zfile_fwrite(buffer, 1, length, file);
+}
+UINT64 core_ftell(core_file *file)
+{
+       return zfile_ftell(file);
+}
+
+
+file_error osd_rmfile(const TCHAR *filename)
+{
+       my_rmdir(filename);
+       return FILERR_NONE;
+}
+
+void osd_break_into_debugger(const char *message)
+{
+       write_log(message);
+}
+
+void *osd_malloc(size_t size)
+{
+       return malloc(size);
+}
+void *osd_malloc_array(size_t size)
+{
+       return malloc(size);
+}
+void osd_free(void *ptr)
+{
+       free(ptr);
+}
+osd_lock *osd_lock_alloc(void)
+{
+       uae_sem_t *t = xmalloc(uae_sem_t, 1);
+       uae_sem_init(t, 0, 1);
+       return (osd_lock*)t;
+}
+void osd_lock_free(osd_lock *lock)
+{
+       uae_sem_destroy((uae_sem_t*)lock);
+}
+void osd_lock_release(osd_lock *lock)
+{
+       uae_sem_post((uae_sem_t*)lock);
+}
+void osd_lock_acquire(osd_lock *lock)
+{
+       uae_sem_wait((uae_sem_t*)lock);
+}
\ No newline at end of file
index cad86c91900c0746e9066e7ffa39ab60428d147a..941caf5ea31f498b9076da82476c503da6893f1e 100644 (file)
@@ -9,6 +9,8 @@
 #include "zfile.h"
 #endif
 
+#define NO_MEM_TRACKING
+
 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
 #define DECL_NORETURN                  __declspec(noreturn)
 #else
@@ -27,8 +29,7 @@
 #define ARRAY_LENGTH(x)                (sizeof(x) / sizeof(x[0]))
 
 #define CLIB_DECL __cdecl
-
-#define FLAC__NO_DLL
+#define FLAC_API_EXPORTS
 
 /* Macros for normalizing data into big or little endian formats */
 #define FLIPENDIAN_INT16(x)    (((((UINT16) (x)) >> 8) | ((x) << 8)) & 0xffff)
diff --git a/archivers/chd/corealloc.cpp b/archivers/chd/corealloc.cpp
new file mode 100644 (file)
index 0000000..9c7229e
--- /dev/null
@@ -0,0 +1,385 @@
+#include "chdtypes.h"
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    corealloc.c
+
+    Memory allocation helpers for the helper library.
+
+***************************************************************************/
+
+#include "corealloc.h"
+#include "osdcore.h"
+
+
+//**************************************************************************
+//  DEBUGGING
+//**************************************************************************
+
+#define LOG_ALLOCS      (0)
+
+// define this to initialize allocated memory to a fixed non-0 value
+#ifdef MAME_DEBUG
+#define INITIALIZE_ALLOCATED_MEMORY
+#endif
+
+// define this to zap memory to a fixed non-0 value before freeing
+//#define OVERWRITE_FREED_MEMORY
+
+
+
+//**************************************************************************
+//  CONSTANTS
+//**************************************************************************
+
+// number of memory_entries to allocate in a block
+const int memory_block_alloc_chunk = 256;
+
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// this struct is allocated in pools to track memory allocations
+// it must be a POD type!!
+class memory_entry
+{
+public:
+       // internal state
+       memory_entry *      m_next;             // link to the next entry
+       memory_entry *      m_prev;             // link to the previous entry
+       size_t              m_size;             // size of the allocation (not including this header)
+       void *              m_base;             // base of the allocation
+       const char *        m_file;             // file the allocation was made from
+       int                 m_line;             // line number within that file
+       UINT64              m_id;               // unique id
+       bool                m_array;            // array?
+
+       // hashing prime number
+       static const int    k_hash_prime = 6151;
+
+       // global state
+       static UINT64       s_curid;            // current ID
+       static osd_lock *   s_lock;             // lock for managing the list
+       static bool         s_lock_alloc;       // set to true temporarily during lock allocation
+       static bool         s_tracking;         // set to true when tracking is live
+       static memory_entry *s_hash[k_hash_prime];// hash table based on pointer
+       static memory_entry *s_freehead;        // pointer to the head of the free list
+
+       // static helpers
+       static memory_entry *allocate(size_t size, void *base, const char *file, int line, bool array);
+       static memory_entry *find(void *ptr);
+       static void release(memory_entry *entry, const char *file, int line);
+       static void report_unfreed(UINT64 start);
+
+private:
+       static void acquire_lock();
+       static void release_lock();
+};
+
+
+
+//**************************************************************************
+//  GLOBALS
+//**************************************************************************
+
+// dummy zeromem object
+const zeromem_t zeromem = { };
+
+// globals for memory_entry
+UINT64 memory_entry::s_curid = 1;
+osd_lock *memory_entry::s_lock = NULL;
+bool memory_entry::s_lock_alloc = false;
+bool memory_entry::s_tracking = false;
+memory_entry *memory_entry::s_hash[memory_entry::k_hash_prime] = { NULL };
+memory_entry *memory_entry::s_freehead = NULL;
+
+
+
+//**************************************************************************
+//  GLOBAL HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+//  malloc_file_line - allocate memory with file
+//  and line number information
+//-------------------------------------------------
+
+void *malloc_file_line(size_t size, const char *file, int line, bool array, bool throw_on_fail, bool clear)
+{
+       // allocate the memory and fail if we can't
+       void *result = array ? osd_malloc_array(size) : osd_malloc(size);
+       if (result == NULL)
+       {
+               fprintf(stderr, "Failed to allocate %d bytes (%s:%d)\n", UINT32(size), file, line);
+               osd_break_into_debugger("Failed to allocate RAM");
+               if (throw_on_fail)
+                       throw std::bad_alloc();
+               return NULL;
+       }
+
+       // zap the memory if requested
+       if (clear)
+               memset(result, 0, size);
+       else
+       {
+#if !__has_feature(memory_sanitizer) && defined(INITIALIZE_ALLOCATED_MEMORY) && !defined(MAME_DEBUG_FAST)
+               memset(result, 0xdd, size);
+#endif
+       }
+
+       // add a new entry
+       memory_entry::allocate(size, result, file, line, array);
+
+       return result;
+}
+
+
+//-------------------------------------------------
+//  free_file_line - free memory with file
+//  and line number information
+//-------------------------------------------------
+
+void free_file_line(void *memory, const char *file, int line, bool array)
+{
+       // find the memory entry
+       memory_entry *entry = memory_entry::find(memory);
+
+       // warn about untracked frees
+       if (entry == NULL)
+       {
+               fprintf(stderr, "Error: attempt to free untracked memory %p in %s(%d)!\n", memory, file, line);
+               osd_break_into_debugger("Error: attempt to free untracked memory");
+               return;
+       }
+
+       // warn about mismatched arrays
+       if (!array && entry->m_array)
+       {
+               fprintf(stderr, "Error: attempt to free array %p with global_free in %s(%d)!\n", memory, file, line);
+               osd_break_into_debugger("Error: attempt to free array with global_free");
+       }
+       if (array && !entry->m_array)
+       {
+#ifndef __INTEL_COMPILER // todo: fix this properly, it appears some optimization the compiler applies breaks our logic here
+               fprintf(stderr, "Error: attempt to free single object %p with global_free_array in %s(%d)!\n", memory, file, line);
+               osd_break_into_debugger("Error: attempt to free single object with global_free_array");
+#endif
+       }
+
+#ifdef OVERWRITE_FREED_MEMORY
+       // clear memory to a bogus value
+       memset(memory, 0xfc, entry->m_size);
+#endif
+
+       // free the entry and the memory
+       memory_entry::release(entry, file, line);
+       osd_free(memory);
+}
+
+
+//-------------------------------------------------
+//  track_memory - enables or disables the memory
+//  tracking
+//-------------------------------------------------
+
+void track_memory(bool track)
+{
+       memory_entry::s_tracking = track;
+}
+
+
+//-------------------------------------------------
+//  next_memory_id - return the ID of the next
+//  allocated block
+//-------------------------------------------------
+
+UINT64 next_memory_id()
+{
+       return memory_entry::s_curid;
+}
+
+
+//-------------------------------------------------
+//  dump_unfreed_mem - called from the exit path
+//  of any code that wants to check for unfreed
+//  memory
+//-------------------------------------------------
+
+void dump_unfreed_mem(UINT64 start)
+{
+       memory_entry::report_unfreed(start);
+}
+
+
+
+//**************************************************************************
+//  MEMORY ENTRY
+//**************************************************************************
+
+//-------------------------------------------------
+//  acquire_lock - acquire the memory entry lock,
+//  creating a new one if needed
+//-------------------------------------------------
+
+void memory_entry::acquire_lock()
+{
+       // allocate a lock on first usage
+       // note that osd_lock_alloc() may re-enter this path, so protect against recursion!
+       if (s_lock == NULL)
+       {
+               if (s_lock_alloc)
+                       return;
+               s_lock_alloc = true;
+               s_lock = osd_lock_alloc();
+               s_lock_alloc = false;
+       }
+       osd_lock_acquire(s_lock);
+}
+
+
+//-------------------------------------------------
+//  release_lock - release the memory entry lock
+//-------------------------------------------------
+
+void memory_entry::release_lock()
+{
+       osd_lock_release(s_lock);
+}
+
+
+//-------------------------------------------------
+//  allocate - allocate a new memory entry
+//-------------------------------------------------
+
+memory_entry *memory_entry::allocate(size_t size, void *base, const char *file, int line, bool array)
+{
+       acquire_lock();
+
+       // if we're out of free entries, allocate a new chunk
+       if (s_freehead == NULL)
+       {
+               // create a new chunk, and fail if we can't
+               memory_entry *entry = reinterpret_cast<memory_entry *>(osd_malloc_array(memory_block_alloc_chunk * sizeof(memory_entry)));
+               if (entry == NULL)
+               {
+                       release_lock();
+                       return NULL;
+               }
+
+               // add all the entries to the list
+               for (int entrynum = 0; entrynum < memory_block_alloc_chunk; entrynum++)
+               {
+                       entry->m_next = s_freehead;
+                       s_freehead = entry++;
+               }
+       }
+
+       // grab a free entry
+       memory_entry *entry = s_freehead;
+       s_freehead = entry->m_next;
+
+       // populate it
+       entry->m_size = size;
+       entry->m_base = base;
+       entry->m_file = s_tracking ? file : NULL;
+       entry->m_line = s_tracking ? line : 0;
+       entry->m_id = s_curid++;
+       entry->m_array = array;
+       if (LOG_ALLOCS)
+               fprintf(stderr, "#%06d, alloc %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line);
+
+       // add it to the alloc list
+       int hashval = reinterpret_cast<FPTR>(base) % k_hash_prime;
+       entry->m_next = s_hash[hashval];
+       if (entry->m_next != NULL)
+               entry->m_next->m_prev = entry;
+       entry->m_prev = NULL;
+       s_hash[hashval] = entry;
+
+       release_lock();
+       return entry;
+}
+
+
+//-------------------------------------------------
+//  find - find a memory entry
+//-------------------------------------------------
+
+memory_entry *memory_entry::find(void *ptr)
+{
+       // NULL maps to nothing
+       if (ptr == NULL)
+               return NULL;
+
+       // scan the list under the lock
+       acquire_lock();
+
+       int hashval = reinterpret_cast<FPTR>(ptr) % k_hash_prime;
+       memory_entry *entry;
+       for (entry = s_hash[hashval]; entry != NULL; entry = entry->m_next)
+               if (entry->m_base == ptr)
+                       break;
+
+       release_lock();
+       return entry;
+}
+
+
+//-------------------------------------------------
+//  release - release a memory entry
+//-------------------------------------------------
+
+void memory_entry::release(memory_entry *entry, const char *file, int line)
+{
+       acquire_lock();
+
+       if (LOG_ALLOCS)
+               fprintf(stderr, "#%06d, release %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), file, line);
+
+       // remove ourselves from the alloc list
+       int hashval = reinterpret_cast<FPTR>(entry->m_base) % k_hash_prime;
+       if (entry->m_prev != NULL)
+               entry->m_prev->m_next = entry->m_next;
+       else
+               s_hash[hashval] = entry->m_next;
+       if (entry->m_next != NULL)
+               entry->m_next->m_prev = entry->m_prev;
+
+       // add ourself to the free list
+       entry->m_next = s_freehead;
+       s_freehead = entry;
+
+       release_lock();
+}
+
+
+//-------------------------------------------------
+//  report_unfreed - print a list of unfreed
+//  memory to the target file
+//-------------------------------------------------
+
+void memory_entry::report_unfreed(UINT64 start)
+{
+       acquire_lock();
+
+       // check for leaked memory
+       UINT32 total = 0;
+
+       for (int hashnum = 0; hashnum < k_hash_prime; hashnum++)
+               for (memory_entry *entry = s_hash[hashnum]; entry != NULL; entry = entry->m_next)
+                       if (entry->m_file != NULL && entry->m_id >= start)
+                       {
+                               if (total == 0)
+                                       fprintf(stderr, "--- memory leak warning ---\n");
+                               total += entry->m_size;
+                               fprintf(stderr, "#%06d, nofree %d bytes (%s:%d)\n", (UINT32)entry->m_id, static_cast<UINT32>(entry->m_size), entry->m_file, (int)entry->m_line);
+                       }
+
+       release_lock();
+
+       if (total > 0)
+               fprintf(stderr, "a total of %u bytes were not freed\n", total);
+}
diff --git a/archivers/chd/corealloc.h b/archivers/chd/corealloc.h
new file mode 100644 (file)
index 0000000..5abddda
--- /dev/null
@@ -0,0 +1,112 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    corealloc.h
+
+    Memory allocation helpers for the helper library.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __COREALLOC_H__
+#define __COREALLOC_H__
+
+#include <stdlib.h>
+#include <new>
+#include "osdcore.h"
+
+
+//**************************************************************************
+//  MACROS
+//**************************************************************************
+
+// global allocation helpers -- use these instead of new and delete
+#define global_alloc(_type)                         new(__FILE__, __LINE__) _type
+#define global_alloc_clear(_type)                   new(__FILE__, __LINE__, zeromem) _type
+#define global_alloc_array(_type, _num)             new(__FILE__, __LINE__) _type[_num]
+#define global_alloc_array_clear(_type, _num)       new(__FILE__, __LINE__, zeromem) _type[_num]
+#define global_free(_ptr)                           do { delete _ptr; } while (0)
+#define global_free_array(_ptr)                     do { delete[] _ptr; } while (0)
+
+
+
+//**************************************************************************
+//  FUNCTION PROTOTYPES
+//**************************************************************************
+
+// allocate memory with file and line number information
+void *malloc_file_line(size_t size, const char *file, int line, bool array, bool throw_on_fail, bool clear);
+
+// free memory with file and line number information
+void free_file_line(void *memory, const char *file, int line, bool array);
+inline void free_file_line(const void *memory, const char *file, int line, bool array) { free_file_line(const_cast<void *>(memory), file, line, array); }
+
+// called from the exit path of any code that wants to check for unfreed memory
+void track_memory(bool track);
+UINT64 next_memory_id();
+void dump_unfreed_mem(UINT64 start = 0);
+
+
+
+//**************************************************************************
+//  INLINE FUNCTIONS
+//**************************************************************************
+
+// zeromem_t is a dummy class used to tell new to zero memory after allocation
+class zeromem_t { };
+
+#if 0
+#ifndef NO_MEM_TRACKING
+
+// standard new/delete operators (try to avoid using)
+ATTR_FORCE_INLINE inline void *operator new(std::size_t size) throw (std::bad_alloc) { return malloc_file_line(size, NULL, 0, false, true, false); }
+ATTR_FORCE_INLINE inline void *operator new[](std::size_t size) throw (std::bad_alloc) { return malloc_file_line(size, NULL, 0, true, true, false); }
+ATTR_FORCE_INLINE inline void operator delete(void *ptr) throw() { if (ptr != NULL) free_file_line(ptr, NULL, 0, false); }
+ATTR_FORCE_INLINE inline void operator delete[](void *ptr) throw() { if (ptr != NULL) free_file_line(ptr, NULL, 0, true); }
+
+#endif
+#endif
+
+// file/line new/delete operators
+ATTR_FORCE_INLINE inline void *operator new(std::size_t size, const char *file, int line) throw (std::bad_alloc) { return malloc_file_line(size, file, line, false, true, false); }
+ATTR_FORCE_INLINE inline void *operator new[](std::size_t size, const char *file, int line) throw (std::bad_alloc) { return malloc_file_line(size, file, line, true, true, false); }
+ATTR_FORCE_INLINE inline void operator delete(void *ptr, const char *file, int line) { if (ptr != NULL) free_file_line(ptr, file, line, false); }
+ATTR_FORCE_INLINE inline void operator delete[](void *ptr, const char *file, int line) { if (ptr != NULL) free_file_line(ptr, file, line, true); }
+
+// file/line new/delete operators with zeroing
+ATTR_FORCE_INLINE inline void *operator new(std::size_t size, const char *file, int line, const zeromem_t &) throw (std::bad_alloc) { return malloc_file_line(size, file, line, false, true, true); }
+ATTR_FORCE_INLINE inline void *operator new[](std::size_t size, const char *file, int line, const zeromem_t &) throw (std::bad_alloc) { return malloc_file_line(size, file, line, true, true, true); }
+ATTR_FORCE_INLINE inline void operator delete(void *ptr, const char *file, int line, const zeromem_t &) { if (ptr != NULL) free_file_line(ptr, file, line, false); }
+ATTR_FORCE_INLINE inline void operator delete[](void *ptr, const char *file, int line, const zeromem_t &) { if (ptr != NULL) free_file_line(ptr, file, line, true); }
+
+
+
+//**************************************************************************
+//  GLOBAL VARIABLES
+//**************************************************************************
+
+// dummy objects to pass to the specialized new variants
+extern const zeromem_t zeromem;
+
+
+
+//**************************************************************************
+//  ADDDITIONAL MACROS
+//**************************************************************************
+
+#ifndef NO_MEM_TRACKING
+// re-route classic malloc-style allocations
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+#define malloc(x)       malloc_file_line(x, __FILE__, __LINE__, true, false, false)
+#define calloc(x,y)     __error_use_auto_alloc_clear_or_global_alloc_clear_instead__
+#define realloc(x,y)    __error_realloc_is_dangerous__
+#define free(x)         free_file_line(x, __FILE__, __LINE__, true)
+#endif
+
+#endif  /* __COREALLOC_H__ */
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..87ca8e2b187c9ef9f77dc45587fc964ee4bd77f2 100644 (file)
@@ -0,0 +1,132 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    corefile.h
+
+    Core file I/O interface functions and definitions.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __COREFILE_H__
+#define __COREFILE_H__
+
+#include <stdarg.h>
+#include "osdcore.h"
+#include "astring.h"
+#include "coretmpl.h"
+
+
+
+/***************************************************************************
+    ADDITIONAL OPEN FLAGS
+***************************************************************************/
+
+#define OPEN_FLAG_NO_BOM        0x0100      /* don't output BOM */
+
+#define FCOMPRESS_NONE          0           /* no compression */
+#define FCOMPRESS_MIN           1           /* minimal compression */
+#define FCOMPRESS_MEDIUM        6           /* standard compression */
+#define FCOMPRESS_MAX           9           /* maximum compression */
+
+
+
+/***************************************************************************
+    TYPE DEFINITIONS
+***************************************************************************/
+
+#define core_file zfile
+//struct core_file;
+
+
+
+/***************************************************************************
+    FUNCTION PROTOTYPES
+***************************************************************************/
+
+
+/* ----- file open/close ----- */
+
+/* open a file with the specified filename */
+file_error core_fopen(const TCHAR *filename, UINT32 openflags, core_file **file);
+
+/* open a RAM-based "file" using the given data and length (read-only) */
+file_error core_fopen_ram(const void *data, size_t length, UINT32 openflags, core_file **file);
+
+/* open a RAM-based "file" using the given data and length (read-only), copying the data */
+file_error core_fopen_ram_copy(const void *data, size_t length, UINT32 openflags, core_file **file);
+
+/* close an open file */
+void core_fclose(core_file *file);
+
+/* enable/disable streaming file compression via zlib; level is 0 to disable compression, or up to 9 for max compression */
+file_error core_fcompress(core_file *file, int level);
+
+
+
+/* ----- file positioning ----- */
+
+/* adjust the file pointer within the file */
+int core_fseek(core_file *file, INT64 offset, int whence);
+
+/* return the current file pointer */
+UINT64 core_ftell(core_file *file);
+
+/* return true if we are at the EOF */
+int core_feof(core_file *file);
+
+/* return the total size of the file */
+UINT64 core_fsize(core_file *file);
+
+
+
+/* ----- file read ----- */
+
+/* standard binary read from a file */
+UINT32 core_fread(core_file *file, void *buffer, UINT32 length);
+
+/* read one character from the file */
+int core_fgetc(core_file *file);
+
+/* put back one character from the file */
+int core_ungetc(int c, core_file *file);
+
+/* read a full line of text from the file */
+char *core_fgets(char *s, int n, core_file *file);
+
+/* get a pointer to a buffer that holds the full file data in RAM */
+/* this function may cause the full file data to be read */
+const void *core_fbuffer(core_file *file);
+
+/* open a file with the specified filename, read it into memory, and return a pointer */
+file_error core_fload(const TCHAR *filename, void **data, UINT32 *length);
+file_error core_fload(const TCHAR *filename, dynamic_buffer &data);
+
+
+
+/* ----- file write ----- */
+
+/* standard binary write to a file */
+UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length);
+
+/* write a line of text to the file */
+int core_fputs(core_file *f, const TCHAR *s);
+
+/* printf-style text write to a file */
+int core_vfprintf(core_file *f, const TCHAR *fmt, va_list va);
+int CLIB_DECL core_fprintf(core_file *f, const TCHAR *fmt, ...) ATTR_PRINTF(2,3);
+
+
+
+/* ----- filename utilities ----- */
+
+/* extract the base part of a filename (remove extensions and paths) */
+astring &core_filename_extract_base(astring &result, const TCHAR *name, bool strip_extension = false);
+
+/* true if the given filename ends with a particular extension */
+int core_filename_ends_with(const TCHAR *filename, const TCHAR *extension);
+
+
+#endif  /* __COREFILE_H__ */
index 30d181edae676743fbcc42a37010bc4f6800dc8c..11c43da031e748c30fcea88f5659fb2ae13647f5 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
 
 #include <assert.h>
 #include "osdcore.h"
+#include "corealloc.h"
+
+// TEMPORARY helper to catch is_pod assertions in the debugger
+#if 0
+#undef assert
+#define assert(x) do { if (!(x)) { fprintf(stderr, "Assert: %s\n", #x); osd_break_into_debugger("Assertion failed"); } } while (0)
+#endif
+
+
+// ======================> auto_pointer
+
+// an object that transparently wraps a pointer and auto-frees it upon destruction
+template<class _ElementType>
+class auto_pointer
+{
+private:
+       // we don't support deep copying
+       auto_pointer(const auto_pointer &);
+       auto_pointer &operator=(const auto_pointer &);
+
+public:
+       // construction/destruction
+       auto_pointer(_ElementType *value = NULL)
+               : m_ptr(value) { }
+       virtual ~auto_pointer() { reset(); }
+
+       // operators
+       operator _ElementType *() const { return m_ptr; }
+       _ElementType &operator*() const { assert(m_ptr != NULL); return *m_ptr; }
+       _ElementType *operator->() const { return m_ptr; }
+
+       // simple getters
+       _ElementType *get() const { return m_ptr; }
+
+       // core operations
+       void reset(_ElementType *ptr = NULL) { if (m_ptr != ptr) { global_free(m_ptr); m_ptr = ptr; } }
+
+private:
+       // internal state
+       _ElementType *  m_ptr;          // pointer we are tracking
+};
 
 
 // ======================> dynamic_array
@@ -59,10 +71,10 @@ private:
 
 public:
        // construction/destruction
-       dynamic_array(int initial = 0)
+       dynamic_array(int initial = 0, int clearvalue = -1)
                : m_array(NULL),
-                 m_count(0),
-                 m_allocated(0) { if (initial != 0) expand_internal(initial); m_count = initial; }
+                       m_count(0),
+                       m_allocated(0) { if (initial != 0) expand_internal(initial); m_count = initial; if (clearvalue != -1) clear(clearvalue); }
        virtual ~dynamic_array() { reset(); }
 
        // operators
@@ -73,37 +85,337 @@ public:
 
        // simple getters
        int count() const { return m_count; }
+       int bytes() const { return m_count * sizeof(_ElementType); }
+
+       // core operations
+       _ElementType &append() { if (m_count == m_allocated) expand_and_keep_internal((m_allocated == 0) ? 16 : (m_allocated << 1)); return m_array[m_count++]; }
+       const _ElementType &append(const _ElementType &element) { return (append() = element); }
+       void reset() { global_free_array(m_array); m_array = NULL; m_count = m_allocated = 0; }
+       void resize(int count) { if (count > m_allocated) expand_internal(count); m_count = count; }
+       void resize_keep(int count) { if (count > m_allocated) expand_and_keep_internal(count); m_count = count; }
+       void clear(UINT8 data = 0) { clear_internal(0, m_count, data); }
 
-       // 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; }
+       // compound operations
+       void resize_and_clear(int count, UINT8 data = 0) { resize(count); clear(data); }
+       void resize_keep_and_clear_new(int count, UINT8 data = 0) { int oldcount = m_count; resize_keep(count); if (oldcount < m_count) clear_internal(oldcount, m_count - oldcount, data); }
 
 private:
        // internal helpers
-       void expand_internal(int count, bool keepdata = true)
+       void expand_internal(int count)
        {
-               // allocate a new array, copy the old one, and proceed
+               global_free_array(m_array);
+               m_array = global_alloc_array(_ElementType, count);
                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;
        }
 
+       void expand_and_keep_internal(int count)
+       {
+               _ElementType *oldarray = m_array;
+               int oldcount = m_count;
+               m_array = global_alloc_array(_ElementType, count);
+               m_allocated = count;
+               for (int index = 0; index < oldcount; index++)
+                       m_array[index] = oldarray[index];
+               global_free_array(oldarray);
+       }
+
+#ifdef __GNUC__
+       void clear_internal(UINT32 start, UINT32 count, UINT8 data) { assert(__is_pod(_ElementType)); memset(&m_array[start], data, count * sizeof(*m_array)); }
+#else
+       void clear_internal(UINT32 start, UINT32 count, UINT8 data) { memset(&m_array[start], data, count * sizeof(*m_array)); }
+#endif
+
        // 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
+       _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
 };
 
+typedef dynamic_array<UINT8> dynamic_buffer;
 
-// ======================> dynamic_buffer
 
-typedef dynamic_array<UINT8> dynamic_buffer;
+// ======================> simple_list
+
+// a simple_list is a singly-linked list whose 'next' pointer is owned
+// by the object
+template<class _ElementType>
+class simple_list
+{
+       // we don't support deep copying
+       simple_list(const simple_list &);
+       simple_list &operator=(const simple_list &);
+
+public:
+       // construction/destruction
+       simple_list()
+               : m_head(NULL),
+                       m_tail(NULL),
+                       m_count(0) { }
+
+       virtual ~simple_list() { reset(); }
+
+       // simple getters
+       _ElementType *first() const { return m_head; }
+       _ElementType *last() const { return m_tail; }
+       int count() const { return m_count; }
+
+       // remove (free) all objects in the list, leaving an empty list
+       void reset()
+       {
+               while (m_head != NULL)
+                       remove(*m_head);
+       }
+
+       // add the given object to the head of the list
+       _ElementType &prepend(_ElementType &object)
+       {
+               object.m_next = m_head;
+               m_head = &object;
+               if (m_tail == NULL)
+                       m_tail = m_head;
+               m_count++;
+               return object;
+       }
+
+       // add the given list to the head of the list
+       void prepend_list(simple_list<_ElementType> &list)
+       {
+               int count = list.count();
+               if (count == 0)
+                       return;
+               _ElementType *tail = list.last();
+               _ElementType *head = list.detach_all();
+               tail->m_next = m_head;
+               m_head = head;
+               if (m_tail == NULL)
+                       m_tail = tail;
+               m_count += count;
+       }
+
+       // add the given object to the tail of the list
+       _ElementType &append(_ElementType &object)
+       {
+               object.m_next = NULL;
+               if (m_tail != NULL)
+                       m_tail = m_tail->m_next = &object;
+               else
+                       m_tail = m_head = &object;
+               m_count++;
+               return object;
+       }
+
+       // add the given list to the tail of the list
+       void append_list(simple_list<_ElementType> &list)
+       {
+               int count = list.count();
+               if (count == 0)
+                       return;
+               _ElementType *tail = list.last();
+               _ElementType *head = list.detach_all();
+               if (m_tail != NULL)
+                       m_tail->m_next = head;
+               else
+                       m_head = head;
+               m_tail = tail;
+               m_count += count;
+       }
+
+       // insert the given object after a particular object (NULL means prepend)
+       _ElementType &insert_after(_ElementType &object, _ElementType *insert_after)
+       {
+               if (insert_after == NULL)
+                       return prepend(object);
+               object.m_next = insert_after->m_next;
+               insert_after->m_next = &object;
+               if (m_tail == insert_after)
+                       m_tail = &object;
+               m_count++;
+               return object;
+       }
+
+       // insert the given object before a particular object (NULL means append)
+       _ElementType &insert_before(_ElementType &object, _ElementType *insert_before)
+       {
+               if (insert_before == NULL)
+                       return append(object);
+               for (_ElementType **curptr = &m_head; *curptr != NULL; curptr = &(*curptr)->m_next)
+                       if (*curptr == insert_before)
+                       {
+                               object.m_next = insert_before;
+                               *curptr = &object;
+                               if (m_head == insert_before)
+                                       m_head = &object;
+                               m_count++;
+                               return object;
+                       }
+               return object;
+       }
+
+       // replace an item in the list at the same location, and remove it
+       _ElementType &replace_and_remove(_ElementType &object, _ElementType &toreplace)
+       {
+               _ElementType *prev = NULL;
+               for (_ElementType *cur = m_head; cur != NULL; prev = cur, cur = cur->m_next)
+                       if (cur == &toreplace)
+                       {
+                               if (prev != NULL)
+                                       prev->m_next = &object;
+                               else
+                                       m_head = &object;
+                               if (m_tail == &toreplace)
+                                       m_tail = &object;
+                               object.m_next = toreplace.m_next;
+                               global_free(&toreplace);
+                               return object;
+                       }
+               return append(object);
+       }
+
+       // detach the head item from the list, but don't free its memory
+       _ElementType *detach_head()
+       {
+               _ElementType *result = m_head;
+               if (result != NULL)
+               {
+                       m_head = result->m_next;
+                       m_count--;
+                       if (m_head == NULL)
+                               m_tail = NULL;
+               }
+               return result;
+       }
+
+       // detach the given item from the list, but don't free its memory
+       _ElementType &detach(_ElementType &object)
+       {
+               _ElementType *prev = NULL;
+               for (_ElementType *cur = m_head; cur != NULL; prev = cur, cur = cur->m_next)
+                       if (cur == &object)
+                       {
+                               if (prev != NULL)
+                                       prev->m_next = object.m_next;
+                               else
+                                       m_head = object.m_next;
+                               if (m_tail == &object)
+                                       m_tail = prev;
+                               m_count--;
+                               return object;
+                       }
+               return object;
+       }
+
+       // deatch the entire list, returning the head, but don't free memory
+       _ElementType *detach_all()
+       {
+               _ElementType *result = m_head;
+               m_head = m_tail = NULL;
+               m_count = 0;
+               return result;
+       }
+
+       // remove the given object and free its memory
+       void remove(_ElementType &object)
+       {
+               global_free(&detach(object));
+       }
+
+       // find an object by index in the list
+       _ElementType *find(int index) const
+       {
+               for (_ElementType *cur = m_head; cur != NULL; cur = cur->m_next)
+                       if (index-- == 0)
+                               return cur;
+               return NULL;
+       }
+
+       // return the index of the given object in the list
+       int indexof(const _ElementType &object) const
+       {
+               int index = 0;
+               for (_ElementType *cur = m_head; cur != NULL; cur = cur->m_next)
+               {
+                       if (cur == &object)
+                               return index;
+                       index++;
+               }
+               return -1;
+       }
+
+private:
+       // internal state
+       _ElementType *  m_head;         // head of the singly-linked list
+       _ElementType *  m_tail;         // tail of the singly-linked list
+       int             m_count;        // number of objects in the list
+};
+
 
+// ======================> simple_list_wrapper
+
+// a simple_list_wrapper wraps an existing object with a next pointer so it
+// can live in a simple_list without requiring the object to have a next
+// pointer
+template<class _ObjectType>
+class simple_list_wrapper
+{
+public:
+       template<class U> friend class simple_list;
+
+       // construction/destruction
+       simple_list_wrapper(_ObjectType *object)
+               : m_next(NULL),
+                       m_object(object) { }
+
+       // operators
+       operator _ObjectType *() { return m_object; }
+       operator _ObjectType *() const { return m_object; }
+       _ObjectType *operator *() { return m_object; }
+       _ObjectType *operator *() const { return m_object; }
+
+       // getters
+       simple_list_wrapper *next() const { return m_next; }
+       _ObjectType *object() const { return m_object; }
+
+private:
+       // internal state
+       simple_list_wrapper *   m_next;
+       _ObjectType *           m_object;
+};
+
+
+// ======================> fixed_allocator
+
+// a fixed_allocator is a simple class that maintains a free pool of objects
+template<class _ItemType>
+class fixed_allocator
+{
+       // we don't support deep copying
+       fixed_allocator(const fixed_allocator &);
+       fixed_allocator &operator=(const fixed_allocator &);
+
+public:
+       // construction/destruction
+       fixed_allocator() { }
+
+       // allocate a new item, either by recycling an old one, or by allocating a new one
+       _ItemType *alloc()
+       {
+               _ItemType *result = m_freelist.detach_head();
+               if (result == NULL)
+                       result = global_alloc(_ItemType);
+               return result;
+       }
+
+       // reclaim an item by adding it to the free list
+       void reclaim(_ItemType *item) { if (item != NULL) m_freelist.append(*item); }
+       void reclaim(_ItemType &item) { m_freelist.append(item); }
+
+       // reclaim all items from a list
+       void reclaim_all(simple_list<_ItemType> &list) { m_freelist.append_list(list); }
+
+private:
+       // internal state
+       simple_list<_ItemType>  m_freelist;     // list of free objects
+};
 
 
 #endif
diff --git a/archivers/chd/delegate.h b/archivers/chd/delegate.h
new file mode 100644 (file)
index 0000000..7336007
--- /dev/null
@@ -0,0 +1,943 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    delegate.h
+
+    Templates and classes to enable delegates for callbacks.
+
+****************************************************************************
+
+    There are many implementations of delegate-like functionality for
+    C++ code, but none of them is a perfect drop-in fit for use in MAME.
+    In order to be useful in MAME, we need the following properties:
+
+        * No significant overhead; we want to use these for memory
+          accessors, and memory accessor overhead is already the dominant
+          performance aspect for most drivers.
+
+        * Existing static functions need to be bound with an additional
+          pointer parameter as the first argument. All existing
+          implementations that allow static function binding assume the
+          same signature as the member functions.
+
+        * We must be able to bind the function separately from the
+          object. This is to allow configurations to bind functions
+          before the objects are created.
+
+    Thus, the implementations below are based on existing works but are
+    really a new implementation that is specific to MAME.
+
+    --------------------------------------------------------------------
+
+    The "compatible" version of delegates is based on an implementation
+    from Sergey Ryazanov, found here:
+
+        http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx
+
+    These delegates essentially generate a templated static stub function
+    for each target function. The static function takes the first
+    parameter, uses it as the object pointer, and calls through the
+    member function. For static functions, the stub is compatible with
+    the signature of a static function, so we just set the stub directly.
+
+    Pros:
+        * should work with any modern compiler
+        * static bindings are just as fast as direct calls
+
+    Cons:
+        * lots of little stub functions generated
+        * double-hops on member function calls means more overhead
+        * calling through stub functions repackages parameters
+
+    --------------------------------------------------------------------
+
+    The "internal" version of delegates makes use of the internal
+    structure of member function pointers in order to convert them at
+    binding time into simple static function pointers. This only works
+    on platforms where object->func(p1, p2) is equivalent in calling
+    convention to func(object, p1, p2).
+
+    Most of the information on how this works comes from Don Clugston
+    in this article:
+
+        http://www.codeproject.com/KB/cpp/FastDelegate.aspx
+
+    Pros:
+        * as fast as a standard function call in static and member cases
+        * no stub functions or double-hops needed
+
+    Cons:
+        * requires internal knowledge of the member function pointer
+        * only works for GCC (for now; MSVC info is also readily available)
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __DELEGATE_H__
+#define __DELEGATE_H__
+
+// standard C++ includes
+#include <assert.h>
+#include <exception>
+#include <typeinfo>
+
+
+//**************************************************************************
+//  MACROS
+//**************************************************************************
+
+// types of delegates supported
+#define DELEGATE_TYPE_COMPATIBLE 0
+#define DELEGATE_TYPE_INTERNAL 1
+
+// select which one we will be using
+#if defined(__GNUC__)
+       /* does not work in versions over 4.7.x of 32bit MINGW  */
+       #if ((defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)))
+               #define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
+       #elif defined(SDLMAME_EMSCRIPTEN)
+               #define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
+       #else
+               #define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL
+       #endif
+#else
+#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
+#endif
+
+
+
+//**************************************************************************
+//  HELPER CLASSES
+//**************************************************************************
+
+// generic function type
+typedef void (*delegate_generic_function)();
+
+
+// ======================> generic_class
+
+// define a dummy generic class that is just straight single-inheritance
+#ifdef _MSC_VER
+class __single_inheritance generic_class;
+class delegate_generic_class { };
+#else
+class delegate_generic_class;
+#endif
+
+
+// ======================> delegate_late_bind
+
+// simple polymorphic class that must be mixed into any object that is late-bound
+class delegate_late_bind
+{
+public:
+       delegate_late_bind() { }
+       virtual ~delegate_late_bind() { }
+};
+
+
+// ======================> binding_type_exception
+
+// exception that is thrown when a bind fails the dynamic_cast
+class binding_type_exception : public std::exception
+{
+public:
+       binding_type_exception(const std::type_info &target_type, const std::type_info &actual_type)
+               : m_target_type(target_type), m_actual_type(actual_type) { }
+       const std::type_info &m_target_type;
+       const std::type_info &m_actual_type;
+};
+
+
+// ======================> delegate_traits
+
+// delegate_traits is a meta-template that is used to provide a static function pointer
+// and member function pointer of the appropriate type and number of parameters; we use
+// partial template specialization to support fewer parameters by defaulting the later
+// parameters to the special type _noparam
+
+// dummy class used to indicate a non-existant parameter
+class _noparam { };
+
+// specialization for 12 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type, typename _P11Type, typename _P12Type>
+struct delegate_traits
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type);
+};
+
+// specialization for 11 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type, typename _P11Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type);
+};
+
+// specialization for 10 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type);
+};
+
+// specialization for 9 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type);
+};
+
+// specialization for 8 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type);
+};
+
+// specialization for 7 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type);
+};
+
+// specialization for 6 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type);
+};
+
+// specialization for 5 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type, _P5Type);
+};
+
+// specialization for 4 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type, _P4Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type, _P4Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type, _P4Type);
+};
+
+// specialization for 3 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _P3Type, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type, _P3Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type, _P3Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type, _P3Type);
+};
+
+// specialization for 2 parameters
+template<typename _ClassType, typename _ReturnType, typename _P1Type, typename _P2Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _P2Type, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type, _P2Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type, _P2Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type, _P2Type);
+};
+
+// specialization for 1 parameter
+template<typename _ClassType, typename _ReturnType, typename _P1Type>
+struct delegate_traits<_ClassType, _ReturnType, _P1Type, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *, _P1Type);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &, _P1Type);
+       typedef _ReturnType (_ClassType::*member_func_type)(_P1Type);
+};
+
+// specialization for no parameters
+template<typename _ClassType, typename _ReturnType>
+struct delegate_traits<_ClassType, _ReturnType, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam, _noparam>
+{
+       typedef _ReturnType (*static_func_type)(_ClassType *);
+       typedef _ReturnType (*static_ref_func_type)(_ClassType &);
+       typedef _ReturnType (_ClassType::*member_func_type)();
+};
+
+
+
+//**************************************************************************
+//  DELEGATE MEMBER FUNCTION POINTER WRAPPERS
+//**************************************************************************
+
+#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
+
+// ======================> delegate_mfp
+
+// delegate_mfp is a class that wraps a generic member function pointer
+// in a static buffer, and can effectively recast itself back for later use;
+// it hides some of the gross details involved in copying artibtrary member
+// function pointers around
+class delegate_mfp
+{
+public:
+       // default constructor
+       delegate_mfp()
+               : m_rawdata(s_null_mfp),
+                       m_realobject(NULL),
+                       m_stubfunction(NULL) { }
+
+       // copy constructor
+       delegate_mfp(const delegate_mfp &src)
+               : m_rawdata(src.m_rawdata),
+                       m_realobject(src.m_realobject),
+                       m_stubfunction(src.m_stubfunction) { }
+
+       // construct from any member function pointer
+       template<typename _MemberFunctionType, class _MemberFunctionClass, typename _ReturnType, typename _StaticFunctionType>
+       delegate_mfp(_MemberFunctionType mfp, _MemberFunctionClass *, _ReturnType *, _StaticFunctionType)
+               : m_rawdata(s_null_mfp),
+                       m_realobject(NULL),
+                       m_stubfunction(make_generic<_StaticFunctionType>(&delegate_mfp::method_stub<_MemberFunctionClass, _ReturnType>))
+       {
+               assert(sizeof(mfp) <= sizeof(m_rawdata));
+               *reinterpret_cast<_MemberFunctionType *>(&m_rawdata) = mfp;
+       }
+
+       // comparison helpers
+       bool operator==(const delegate_mfp &rhs) const { return (m_rawdata == rhs.m_rawdata); }
+       bool isnull() const { return (m_rawdata == s_null_mfp); }
+
+       // getters
+       delegate_generic_class *real_object(delegate_generic_class *original) const { return m_realobject; }
+
+       // binding helper
+       template<typename _FunctionType>
+       void update_after_bind(_FunctionType &funcptr, delegate_generic_class *&object)
+       {
+               m_realobject = object;
+               object = reinterpret_cast<delegate_generic_class *>(this);
+               funcptr = reinterpret_cast<_FunctionType>(m_stubfunction);
+       }
+
+private:
+       // helper stubs for calling encased member function pointers
+       template<class _FunctionClass, typename _ReturnType>
+       static _ReturnType method_stub(delegate_generic_class *object)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)();
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)();
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8, p9);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type, typename _P11Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10, _P11Type p11)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10, _P11Type p11);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11);
+       }
+
+       template<class _FunctionClass, typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type, typename _P11Type, typename _P12Type>
+       static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10, _P11Type p11, _P12Type p12)
+       {
+               delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
+               typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10, _P11Type p11, _P12Type p12);
+               mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
+               return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12);
+       }
+       // helper to convert a function of a given type to a generic function, forcing template
+       // instantiation to match the source type
+       template <typename _SourceType>
+       delegate_generic_function make_generic(_SourceType funcptr)
+       {
+               return reinterpret_cast<delegate_generic_function>(funcptr);
+       }
+
+
+       struct raw_mfp_data
+       {
+#if defined (__INTEL_COMPILER) && defined (PTR64) // needed for "Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 14.0.2.176 Build 20140130" at least
+               int data[((sizeof(void *) + 4 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
+#else // all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller
+               int data[((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
+#endif
+               bool operator==(const raw_mfp_data &rhs) const { return (memcmp(data, rhs.data, sizeof(data)) == 0); }
+       };
+
+       // internal state
+       raw_mfp_data                m_rawdata;          // raw buffer to hold the copy of the function pointer
+       delegate_generic_class *    m_realobject;       // pointer to the object used for calling
+       delegate_generic_function   m_stubfunction;     // pointer to our matching stub function
+       static raw_mfp_data         s_null_mfp;         // NULL mfp
+};
+
+#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)
+
+// ======================> delegate_mfp
+
+// struct describing the contents of a member function pointer
+class delegate_mfp
+{
+public:
+       // default constructor
+       delegate_mfp()
+               : m_function(0),
+                       m_this_delta(0) { }
+
+       // copy constructor
+       delegate_mfp(const delegate_mfp &src)
+               : m_function(src.m_function),
+                       m_this_delta(src.m_this_delta) { }
+
+       // construct from any member function pointer
+       template<typename _MemberFunctionType, class _MemberFunctionClass, typename _ReturnType, typename _StaticFunctionType>
+       delegate_mfp(_MemberFunctionType mfp, _MemberFunctionClass *, _ReturnType *, _StaticFunctionType)
+       {
+               assert(sizeof(mfp) == sizeof(*this));
+               *reinterpret_cast<_MemberFunctionType *>(this) = mfp;
+       }
+
+       // comparison helpers
+       bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function && m_this_delta == rhs.m_this_delta); }
+       bool isnull() const { return (m_function == 0); }
+
+       // getters
+       delegate_generic_class *real_object(delegate_generic_class *original) const { return original; }
+
+       // binding helper
+       template<typename _FunctionType>
+       void update_after_bind(_FunctionType &funcptr, delegate_generic_class *&object)
+       {
+               funcptr = reinterpret_cast<_FunctionType>(convert_to_generic(object));
+       }
+
+private:
+       // extract the generic function and adjust the object pointer
+       delegate_generic_function convert_to_generic(delegate_generic_class *&object) const;
+
+       // actual state
+       FPTR                    m_function;         // first item can be one of two things:
+                                                                                               //    if even, it's a pointer to the function
+                                                                                               //    if odd, it's the byte offset into the vtable
+       int                     m_this_delta;       // delta to apply to the 'this' pointer
+};
+
+#endif
+
+
+
+//**************************************************************************
+//  COMMON DELEGATE BASE CLASS
+//**************************************************************************
+
+// ======================> delegate_base
+
+// general delegate class template supporting up to 5 parameters
+template<typename _ReturnType, typename _P1Type = _noparam, typename _P2Type = _noparam, typename _P3Type = _noparam, typename _P4Type = _noparam, typename _P5Type = _noparam, typename _P6Type = _noparam, typename _P7Type = _noparam, typename _P8Type = _noparam, typename _P9Type = _noparam, typename _P10Type = _noparam, typename _P11Type = _noparam, typename _P12Type = _noparam>
+class delegate_base
+{
+public:
+       // define our traits
+       template<class _FunctionClass>
+       struct traits
+       {
+               typedef typename delegate_traits<_FunctionClass, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type>::member_func_type member_func_type;
+               typedef typename delegate_traits<_FunctionClass, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type>::static_func_type static_func_type;
+               typedef typename delegate_traits<_FunctionClass, _ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type>::static_ref_func_type static_ref_func_type;
+       };
+       typedef typename traits<delegate_generic_class>::static_func_type generic_static_func;
+
+       // generic constructor
+       delegate_base()
+               : m_function(NULL),
+                       m_object(NULL),
+                       m_name(NULL),
+                       m_latebinder(NULL),
+                       m_raw_function(NULL) { }
+
+       // copy constructor
+       delegate_base(const delegate_base &src)
+               : m_function(src.m_function),
+                       m_object(NULL),
+                       m_name(src.m_name),
+                       m_latebinder(src.m_latebinder),
+                       m_raw_function(src.m_raw_function),
+                       m_raw_mfp(src.m_raw_mfp)
+       {
+               bind(src.object());
+       }
+
+       // copy constructor with late bind
+       delegate_base(const delegate_base &src, delegate_late_bind &object)
+               : m_function(src.m_function),
+                       m_object(NULL),
+                       m_name(src.m_name),
+                       m_latebinder(src.m_latebinder),
+                       m_raw_function(src.m_raw_function),
+                       m_raw_mfp(src.m_raw_mfp)
+       {
+               late_bind(object);
+       }
+
+       // construct from member function with object pointer
+       template<class _FunctionClass>
+       delegate_base(typename traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object)
+               : m_function(NULL),
+                       m_object(NULL),
+                       m_name(name),
+                       m_latebinder(&late_bind_helper<_FunctionClass>),
+                       m_raw_function(NULL),
+                       m_raw_mfp(funcptr, object, (_ReturnType *)0, (generic_static_func)0)
+       {
+               bind(reinterpret_cast<delegate_generic_class *>(object));
+       }
+
+       // construct from static function with object pointer
+       template<class _FunctionClass>
+       delegate_base(typename traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object)
+               : m_function(reinterpret_cast<generic_static_func>(funcptr)),
+                       m_object(NULL),
+                       m_name(name),
+                       m_latebinder(&late_bind_helper<_FunctionClass>),
+                       m_raw_function(reinterpret_cast<generic_static_func>(funcptr))
+       {
+               bind(reinterpret_cast<delegate_generic_class *>(object));
+       }
+
+       // construct from static reference function with object reference
+       template<class _FunctionClass>
+       delegate_base(typename traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object)
+               : m_function(reinterpret_cast<generic_static_func>(funcptr)),
+                       m_object(NULL),
+                       m_name(name),
+                       m_latebinder(&late_bind_helper<_FunctionClass>),
+                       m_raw_function(reinterpret_cast<generic_static_func>(funcptr))
+       {
+               bind(reinterpret_cast<delegate_generic_class *>(object));
+       }
+
+       // copy operator
+       delegate_base &operator=(const delegate_base &src)
+       {
+               if (this != &src)
+               {
+                       m_function = src.m_function;
+                       m_object = NULL;
+                       m_name = src.m_name;
+                       m_latebinder = src.m_latebinder;
+                       m_raw_function = src.m_raw_function;
+                       m_raw_mfp = src.m_raw_mfp;
+                       bind(src.object());
+               }
+               return *this;
+       }
+
+       // comparison helper
+       bool operator==(const delegate_base &rhs) const
+       {
+               return (m_raw_function == rhs.m_raw_function && object() == rhs.object() && m_raw_mfp == rhs.m_raw_mfp);
+       }
+
+       // call the function
+       _ReturnType operator()() const { return (*m_function)(m_object); }
+       _ReturnType operator()(_P1Type p1) const { return (*m_function)(m_object, p1); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2) const { return (*m_function)(m_object, p1, p2); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3) const { return (*m_function)(m_object, p1, p2, p3); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4) const { return (*m_function)(m_object, p1, p2, p3, p4); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5) const { return (*m_function)(m_object, p1, p2, p3, p4, p5); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6) const { return (*m_function)(m_object, p1, p2, p3, p4, p5, p6); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7) const { return (*m_function)(m_object, p1, p2, p3, p4, p5, p6, p7); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8) const { return (*m_function)(m_object, p1, p2, p3, p4, p5, p6, p7, p8); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9) const { return (*m_function)(m_object, p1, p2, p3, p4, p5, p6, p7, p8, p9); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10) const { return (*m_function)(m_object, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10, _P11Type p11) const { return (*m_function)(m_object, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); }
+       _ReturnType operator()(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7, _P8Type p8, _P9Type p9, _P10Type p10, _P11Type p11, _P12Type p12) const { return (*m_function)(m_object, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12); }
+
+       // getters
+       bool has_object() const { return (object() != NULL); }
+       const char *name() const { return m_name; }
+
+       // helpers
+       bool isnull() const { return (m_raw_function == NULL && m_raw_mfp.isnull()); }
+       bool is_mfp() const { return !m_raw_mfp.isnull(); }
+
+       // late binding
+       void late_bind(delegate_late_bind &object) { bind((*m_latebinder)(object)); }
+
+protected:
+       // return the actual object (not the one we use for calling)
+       delegate_generic_class *object() const { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
+
+       // late binding function
+       typedef delegate_generic_class *(*late_bind_func)(delegate_late_bind &object);
+
+       // late binding helper
+       template<class _FunctionClass>
+       static delegate_generic_class *late_bind_helper(delegate_late_bind &object)
+       {
+               _FunctionClass *result = dynamic_cast<_FunctionClass *>(&object);
+               if (result == NULL) {
+                       throw binding_type_exception(typeid(_FunctionClass), typeid(object));
+               }
+               return reinterpret_cast<delegate_generic_class *>(result);
+       }
+
+       // bind the actual object
+       void bind(delegate_generic_class *object)
+       {
+               m_object = object;
+
+               // if we're wrapping a member function pointer, handle special stuff
+               if (m_object != NULL && is_mfp())
+                       m_raw_mfp.update_after_bind(m_function, m_object);
+       }
+
+       // internal state
+       generic_static_func         m_function;         // resolved static function pointer
+       delegate_generic_class *    m_object;           // resolved object to the post-cast object
+       const char *                m_name;             // name string
+       late_bind_func              m_latebinder;       // late binding helper
+       generic_static_func         m_raw_function;     // raw static function pointer
+       delegate_mfp                m_raw_mfp;          // raw member function pointer
+};
+
+
+
+//**************************************************************************
+//  NATURAL SYNTAX
+//**************************************************************************
+
+// declare the base template
+template <typename Signature>
+class delegate;
+
+// specialize for 0 parameters; we derive from the base class and provide equivalent
+// pass-through constructors for each type, as well as an assignment operator
+template<typename _ReturnType>
+class delegate<_ReturnType ()> : public delegate_base<_ReturnType>
+{
+       typedef delegate_base<_ReturnType> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 1 parameter
+template<typename _ReturnType, typename _P1Type>
+class delegate<_ReturnType (_P1Type)> : public delegate_base<_ReturnType, _P1Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 2 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type>
+class delegate<_ReturnType (_P1Type, _P2Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 3 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 4 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 5 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 6 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 7 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 8 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 9 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 10 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 11 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type, typename _P11Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+// specialize for 12 parameters
+template<typename _ReturnType, typename _P1Type, typename _P2Type, typename _P3Type, typename _P4Type, typename _P5Type, typename _P6Type, typename _P7Type, typename _P8Type, typename _P9Type, typename _P10Type, typename _P11Type, typename _P12Type>
+class delegate<_ReturnType (_P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type)> : public delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type>
+{
+       typedef delegate_base<_ReturnType, _P1Type, _P2Type, _P3Type, _P4Type, _P5Type, _P6Type, _P7Type, _P8Type, _P9Type, _P10Type, _P11Type, _P12Type> basetype;
+
+public:
+       // create a standard set of constructors
+       delegate() : basetype() { }
+       delegate(const basetype &src) : basetype(src) { }
+       delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       template<class _FunctionClass> delegate(typename basetype::template traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { }
+       delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+};
+
+#endif  /* __DELEGATE_H__ */
diff --git a/archivers/chd/eminline.h b/archivers/chd/eminline.h
new file mode 100644 (file)
index 0000000..3e3f839
--- /dev/null
@@ -0,0 +1,457 @@
+/***************************************************************************
+
+    eminline.h
+
+    Definitions for inline functions that can be overriden by OSD-
+    specific code.
+
+    Copyright Nicola Salmoria and the MAME Team.
+    Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#ifndef __EMINLINE__
+#define __EMINLINE__
+
+#if !defined(SDLMAME_NOASM)
+/* we come with implementations for GCC x86 and PPC */
+#if defined(__GNUC__)
+
+#if defined(__i386__) || defined(__x86_64__)
+#include "eigccx86.h"
+#elif defined(__ppc__) || defined (__PPC__) || defined(__ppc64__) || defined(__PPC64__)
+#include "eigccppc.h"
+#else
+#include "osinline.h"
+#endif
+
+#else
+
+#include "osinline.h"
+
+#endif
+#endif
+
+
+/***************************************************************************
+    INLINE MATH FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    mul_32x32 - perform a signed 32 bit x 32 bit
+    multiply and return the full 64 bit result
+-------------------------------------------------*/
+
+#ifndef mul_32x32
+INLINE INT64 mul_32x32(INT32 a, INT32 b)
+{
+       return (INT64)a * (INT64)b;
+}
+#endif
+
+
+/*-------------------------------------------------
+    mulu_32x32 - perform an unsigned 32 bit x
+    32 bit multiply and return the full 64 bit
+    result
+-------------------------------------------------*/
+
+#ifndef mulu_32x32
+INLINE UINT64 mulu_32x32(UINT32 a, UINT32 b)
+{
+       return (UINT64)a * (UINT64)b;
+}
+#endif
+
+
+/*-------------------------------------------------
+    mul_32x32_hi - perform a signed 32 bit x 32 bit
+    multiply and return the upper 32 bits of the
+    result
+-------------------------------------------------*/
+
+#ifndef mul_32x32_hi
+INLINE INT32 mul_32x32_hi(INT32 a, INT32 b)
+{
+       return (UINT32)(((INT64)a * (INT64)b) >> 32);
+}
+#endif
+
+
+/*-------------------------------------------------
+    mulu_32x32_hi - perform an unsigned 32 bit x
+    32 bit multiply and return the upper 32 bits
+    of the result
+-------------------------------------------------*/
+
+#ifndef mulu_32x32_hi
+INLINE UINT32 mulu_32x32_hi(UINT32 a, UINT32 b)
+{
+       return (UINT32)(((UINT64)a * (UINT64)b) >> 32);
+}
+#endif
+
+
+/*-------------------------------------------------
+    mul_32x32_shift - perform a signed 32 bit x
+    32 bit multiply and shift the result by the
+    given number of bits before truncating the
+    result to 32 bits
+-------------------------------------------------*/
+
+#ifndef mul_32x32_shift
+INLINE INT32 mul_32x32_shift(INT32 a, INT32 b, UINT8 shift)
+{
+       return (INT32)(((INT64)a * (INT64)b) >> shift);
+}
+#endif
+
+
+/*-------------------------------------------------
+    mulu_32x32_shift - perform an unsigned 32 bit x
+    32 bit multiply and shift the result by the
+    given number of bits before truncating the
+    result to 32 bits
+-------------------------------------------------*/
+
+#ifndef mulu_32x32_shift
+INLINE UINT32 mulu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift)
+{
+       return (UINT32)(((UINT64)a * (UINT64)b) >> shift);
+}
+#endif
+
+
+/*-------------------------------------------------
+    div_64x32 - perform a signed 64 bit x 32 bit
+    divide and return the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef div_64x32
+INLINE INT32 div_64x32(INT64 a, INT32 b)
+{
+       return a / (INT64)b;
+}
+#endif
+
+
+/*-------------------------------------------------
+    divu_64x32 - perform an unsigned 64 bit x 32 bit
+    divide and return the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef divu_64x32
+INLINE UINT32 divu_64x32(UINT64 a, UINT32 b)
+{
+       return a / (UINT64)b;
+}
+#endif
+
+
+/*-------------------------------------------------
+    div_64x32_rem - perform a signed 64 bit x 32
+    bit divide and return the 32 bit quotient and
+    32 bit remainder
+-------------------------------------------------*/
+
+#ifndef div_64x32_rem
+INLINE INT32 div_64x32_rem(INT64 a, INT32 b, INT32 *remainder)
+{
+       INT32 res = div_64x32(a, b);
+       *remainder = a - ((INT64)b * res);
+       return res;
+}
+#endif
+
+
+/*-------------------------------------------------
+    divu_64x32_rem - perform an unsigned 64 bit x
+    32 bit divide and return the 32 bit quotient
+    and 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef divu_64x32_rem
+INLINE UINT32 divu_64x32_rem(UINT64 a, UINT32 b, UINT32 *remainder)
+{
+       UINT32 res = divu_64x32(a, b);
+       *remainder = a - ((UINT64)b * res);
+       return res;
+}
+#endif
+
+
+/*-------------------------------------------------
+    div_32x32_shift - perform a signed divide of
+    two 32 bit values, shifting the first before
+    division, and returning the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef div_32x32_shift
+INLINE INT32 div_32x32_shift(INT32 a, INT32 b, UINT8 shift)
+{
+       return ((INT64)a << shift) / (INT64)b;
+}
+#endif
+
+
+/*-------------------------------------------------
+    divu_32x32_shift - perform an unsigned divide of
+    two 32 bit values, shifting the first before
+    division, and returning the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef divu_32x32_shift
+INLINE UINT32 divu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift)
+{
+       return ((UINT64)a << shift) / (UINT64)b;
+}
+#endif
+
+
+/*-------------------------------------------------
+    mod_64x32 - perform a signed 64 bit x 32 bit
+    divide and return the 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef mod_64x32
+INLINE INT32 mod_64x32(INT64 a, INT32 b)
+{
+       return a - (b * div_64x32(a, b));
+}
+#endif
+
+
+/*-------------------------------------------------
+    modu_64x32 - perform an unsigned 64 bit x 32 bit
+    divide and return the 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef modu_64x32
+INLINE UINT32 modu_64x32(UINT64 a, UINT32 b)
+{
+       return a - (b * divu_64x32(a, b));
+}
+#endif
+
+
+/*-------------------------------------------------
+    recip_approx - compute an approximate floating
+    point reciprocal
+-------------------------------------------------*/
+
+#ifndef recip_approx
+INLINE float recip_approx(float value)
+{
+       return 1.0f / value;
+}
+#endif
+
+
+
+/***************************************************************************
+    INLINE BIT MANIPULATION FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    count_leading_zeros - return the number of
+    leading zero bits in a 32-bit value
+-------------------------------------------------*/
+
+#ifndef count_leading_zeros
+INLINE UINT8 count_leading_zeros(UINT32 val)
+{
+       UINT8 count;
+       for (count = 0; (INT32)val >= 0; count++) val <<= 1;
+       return count;
+}
+#endif
+
+
+/*-------------------------------------------------
+    count_leading_ones - return the number of
+    leading one bits in a 32-bit value
+-------------------------------------------------*/
+
+#ifndef count_leading_ones
+INLINE UINT8 count_leading_ones(UINT32 val)
+{
+       UINT8 count;
+       for (count = 0; (INT32)val < 0; count++) val <<= 1;
+       return count;
+}
+#endif
+
+
+
+/***************************************************************************
+    INLINE SYNCHRONIZATION FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    compare_exchange32 - compare the 'compare'
+    value against the memory at 'ptr'; if equal,
+    swap in the 'exchange' value. Regardless,
+    return the previous value at 'ptr'.
+
+    Note that the default implementation does
+    no synchronization. You MUST override this
+    in osinline.h for it to be useful in a
+    multithreaded environment!
+-------------------------------------------------*/
+
+#ifndef compare_exchange32
+INLINE INT32 compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange)
+{
+       INT32 oldval = *ptr;
+       if (*ptr == compare)
+               *ptr = exchange;
+       return oldval;
+}
+#endif
+
+
+/*-------------------------------------------------
+    compare_exchange64 - compare the 'compare'
+    value against the memory at 'ptr'; if equal,
+    swap in the 'exchange' value. Regardless,
+    return the previous value at 'ptr'.
+
+    Note that the default implementation does
+    no synchronization. You MUST override this
+    in osinline.h for it to be useful in a
+    multithreaded environment!
+-------------------------------------------------*/
+
+#ifdef PTR64
+#ifndef compare_exchange64
+INLINE INT64 compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange)
+{
+       INT64 oldval = *ptr;
+       if (*ptr == compare)
+               *ptr = exchange;
+       return oldval;
+}
+#endif
+#endif
+
+
+/*-------------------------------------------------
+    compare_exchange_ptr - compare the 'compare'
+    value against the memory at 'ptr'; if equal,
+    swap in the 'exchange' value. Regardless,
+    return the previous value at 'ptr'.
+-------------------------------------------------*/
+
+#ifndef compare_exchange_ptr
+INLINE void *compare_exchange_ptr(void * volatile *ptr, void *compare, void *exchange)
+{
+#ifdef PTR64
+       INT64 result;
+       result = compare_exchange64((INT64 volatile *)ptr, (INT64)compare, (INT64)exchange);
+#else
+       INT32 result;
+       result = compare_exchange32((INT32 volatile *)ptr, (INT32)compare, (INT32)exchange);
+#endif
+       return (void *)result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    atomic_exchange32 - atomically exchange the
+    exchange value with the memory at 'ptr',
+    returning the original value.
+
+    Note that the default implementation does
+    no synchronization. You MUST override this
+    in osinline.h for it to be useful in a
+    multithreaded environment!
+-------------------------------------------------*/
+
+#ifndef atomic_exchange32
+INLINE INT32 atomic_exchange32(INT32 volatile *ptr, INT32 exchange)
+{
+       INT32 oldval = *ptr;
+       *ptr = exchange;
+       return oldval;
+}
+#endif
+
+
+/*-------------------------------------------------
+    atomic_add32 - atomically add the delta value
+    to the memory at 'ptr', returning the final
+    result.
+
+    Note that the default implementation does
+    no synchronization. You MUST override this
+    in osinline.h for it to be useful in a
+    multithreaded environment!
+-------------------------------------------------*/
+
+#ifndef atomic_add32
+INLINE INT32 atomic_add32(INT32 volatile *ptr, INT32 delta)
+{
+       return (*ptr += delta);
+}
+#endif
+
+
+/*-------------------------------------------------
+    atomic_increment32 - atomically increment the
+    32-bit value in memory at 'ptr', returning the
+    final result.
+
+    Note that the default implementation does
+    no synchronization. You MUST override this
+    in osinline.h for it to be useful in a
+    multithreaded environment!
+-------------------------------------------------*/
+
+#ifndef atomic_increment32
+INLINE INT32 atomic_increment32(INT32 volatile *ptr)
+{
+       return atomic_add32(ptr, 1);
+}
+#endif
+
+
+/*-------------------------------------------------
+    atomic_decrement32 - atomically decrement the
+    32-bit value in memory at 'ptr', returning the
+    final result.
+
+    Note that the default implementation does
+    no synchronization. You MUST override this
+    in osinline.h for it to be useful in a
+    multithreaded environment!
+-------------------------------------------------*/
+
+#ifndef atomic_decrement32
+INLINE INT32 atomic_decrement32(INT32 volatile *ptr)
+{
+       return atomic_add32(ptr, -1);
+}
+#endif
+
+
+
+/***************************************************************************
+    INLINE TIMING FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    get_profile_ticks - return a tick counter
+    from the processor that can be used for
+    profiling. It does not need to run at any
+    particular rate.
+-------------------------------------------------*/
+
+#ifndef get_profile_ticks
+INLINE INT64 get_profile_ticks(void)
+{
+       return osd_ticks();
+}
+#endif
+
+#endif /* __EMINLINE__ */
index 955a28485ce30cdd301182a88c07e73bbaab7eea..2ab3a7a8c2a550b5a82646e354b51ad4a64d8d29 100644 (file)
@@ -1,42 +1,12 @@
 #include "chdtypes.h"
-
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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"
@@ -65,7 +35,7 @@ flac_encoder::flac_encoder(void *buffer, UINT32 buflength)
 }
 
 
-flac_encoder::flac_encoder(struct zfile *file)
+flac_encoder::flac_encoder(core_file &file)
 {
        init_common();
        reset(file);
@@ -130,12 +100,12 @@ bool flac_encoder::reset(void *buffer, UINT32 buflength)
 //  reset - reset state with new file parameters
 //-------------------------------------------------
 
-bool flac_encoder::reset(struct zfile *file)
+bool flac_encoder::reset(core_file &file)
 {
        // configure the output
        m_compressed_start = NULL;
        m_compressed_length = 0;
-       m_file = file;
+       m_file = &file;
        return reset();
 }
 
@@ -215,7 +185,7 @@ 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;
+       return (m_file != NULL) ? core_ftell(m_file) : m_compressed_offset;
 }
 
 
@@ -282,7 +252,7 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf
                {
                        int count = bytes - offset;
                        if (m_file != NULL)
-                               zfile_fwrite(buffer, count, 1, m_file);
+                               core_fwrite(m_file, buffer, count);
                        else
                        {
                                if (m_compressed_offset + count <= m_compressed_length)
@@ -296,7 +266,6 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf
 }
 
 
-
 //**************************************************************************
 //  FLAC DECODER
 //**************************************************************************
@@ -307,12 +276,12 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf
 
 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)
+               m_file(NULL),
+               m_compressed_offset(0),
+               m_compressed_start(NULL),
+               m_compressed_length(0),
+               m_compressed2_start(NULL),
+               m_compressed2_length(0)
 {
 }
 
@@ -323,12 +292,12 @@ flac_decoder::flac_decoder()
 
 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)
+               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();
 }
@@ -338,14 +307,14 @@ flac_decoder::flac_decoder(const void *buffer, UINT32 length, const void *buffer
 //  flac_decoder - constructor
 //-------------------------------------------------
 
-flac_decoder::flac_decoder(struct zfile *file)
+flac_decoder::flac_decoder(core_file &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)
+               m_file(&file),
+               m_compressed_offset(0),
+               m_compressed_start(NULL),
+               m_compressed_length(0),
+               m_compressed2_start(NULL),
+               m_compressed2_length(0)
 {
        reset();
 }
@@ -379,7 +348,7 @@ bool flac_decoder::reset()
                                &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;
+       return FLAC__stream_decoder_process_until_end_of_metadata(m_decoder);
 }
 
 
@@ -408,19 +377,19 @@ bool flac_decoder::reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_si
        // 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),
+               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)
+               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  //
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  //
                                                                                                                // +2A: start of stream data
        };
        memcpy(m_custom_header, s_header_template, sizeof(s_header_template));
@@ -444,9 +413,9 @@ bool flac_decoder::reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_si
 //  reset - reset state with new file parameter
 //-------------------------------------------------
 
-bool flac_decoder::reset(struct zfile *file)
+bool flac_decoder::reset(core_file &file)
 {
-       m_file = file;
+       m_file = &file;
        m_compressed_start = NULL;
        m_compressed_length = 0;
        m_compressed2_start = NULL;
@@ -541,7 +510,7 @@ FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], s
 
        // if a file, just read
        if (m_file != NULL)
-               *bytes = zfile_fread(buffer, 1, expected, m_file);
+               *bytes = core_fread(m_file, buffer, expected);
 
        // otherwise, copy from memory
        else
index 3f2cfe3a613b8d38ebf30dec535055880d551f0b..6a3f4e6b39c30569ae77b01f0600684dd8f6199b 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
@@ -46,7 +17,7 @@
 #include "corefile.h"
 
 #ifdef FLAC__NO_DLL
-#include "flac/all.h"
+#include "../../lib/libflac/include/FLAC/all.h"
 #else
 #include <FLAC/all.h>
 #endif
@@ -64,7 +35,7 @@ public:
        // construction/destruction
        flac_encoder();
        flac_encoder(void *buffer, UINT32 buflength);
-       flac_encoder(struct zfile *file);
+       flac_encoder(core_file &file);
        ~flac_encoder();
 
        // configuration
@@ -80,7 +51,7 @@ public:
        // reset
        bool reset();
        bool reset(void *buffer, UINT32 buflength);
-       bool reset(struct zfile *file);
+       bool reset(core_file &file);
 
        // encode a buffer
        bool encode_interleaved(const INT16 *samples, UINT32 samples_per_channel, bool swap_endian = false);
@@ -96,21 +67,21 @@ private:
        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
+       FLAC__StreamEncoder *   m_encoder;              // actual encoder
+       core_file *             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
+       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?
+       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?
 };
 
 
@@ -122,7 +93,7 @@ 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(core_file &file);
        ~flac_decoder();
 
        // getters (valid after reset)
@@ -137,7 +108,7 @@ public:
        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);
+       bool reset(core_file &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);
@@ -157,21 +128,21 @@ private:
        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
+       FLAC__StreamDecoder *   m_decoder;              // actual encoder
+       core_file *             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
 };
 
 
diff --git a/archivers/chd/harddisk.cpp b/archivers/chd/harddisk.cpp
new file mode 100644 (file)
index 0000000..a71bb49
--- /dev/null
@@ -0,0 +1,126 @@
+#include "chdtypes.h"
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    hardisk.c
+
+    Generic MAME hard disk implementation, with differencing files
+
+***************************************************************************/
+
+#include "harddisk.h"
+
+#include <stdlib.h>
+
+
+/***************************************************************************
+    TYPE DEFINITIONS
+***************************************************************************/
+
+struct hard_disk_file
+{
+       chd_file *          chd;                /* CHD file */
+       hard_disk_info      info;               /* hard disk info */
+};
+
+
+
+/***************************************************************************
+    CORE IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+    hard_disk_open - open a hard disk handle,
+    given a chd_file
+-------------------------------------------------*/
+
+hard_disk_file *hard_disk_open(chd_file *chd)
+{
+       int cylinders, heads, sectors, sectorbytes;
+       hard_disk_file *file;
+       astring metadata;
+       chd_error err;
+
+       /* punt if no CHD */
+       if (chd == NULL)
+               return NULL;
+
+       /* read the hard disk metadata */
+       err = chd->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
+       if (err != CHDERR_NONE)
+               return NULL;
+
+       /* parse the metadata */
+       if (sscanf(metadata, HARD_DISK_METADATA_FORMAT, &cylinders, &heads, &sectors, &sectorbytes) != 4)
+               return NULL;
+
+       /* allocate memory for the hard disk file */
+       file = (hard_disk_file *)malloc(sizeof(hard_disk_file));
+       if (file == NULL)
+               return NULL;
+
+       /* fill in the data */
+       file->chd = chd;
+       file->info.cylinders = cylinders;
+       file->info.heads = heads;
+       file->info.sectors = sectors;
+       file->info.sectorbytes = sectorbytes;
+       return file;
+}
+
+
+/*-------------------------------------------------
+    hard_disk_close - close a hard disk handle
+-------------------------------------------------*/
+
+void hard_disk_close(hard_disk_file *file)
+{
+       free(file);
+}
+
+
+/*-------------------------------------------------
+    hard_disk_get_chd - get a handle to a CHD
+    from a hard disk
+-------------------------------------------------*/
+
+chd_file *hard_disk_get_chd(hard_disk_file *file)
+{
+       return file->chd;
+}
+
+
+/*-------------------------------------------------
+    hard_disk_get_info - return information about
+    a hard disk
+-------------------------------------------------*/
+
+hard_disk_info *hard_disk_get_info(hard_disk_file *file)
+{
+       return &file->info;
+}
+
+
+/*-------------------------------------------------
+    hard_disk_read - read sectors from a hard
+    disk
+-------------------------------------------------*/
+
+UINT32 hard_disk_read(hard_disk_file *file, UINT32 lbasector, void *buffer)
+{
+       chd_error err = file->chd->read_units(lbasector, buffer);
+       return (err == CHDERR_NONE);
+}
+
+
+/*-------------------------------------------------
+    hard_disk_write - write  sectors to a hard
+    disk
+-------------------------------------------------*/
+
+UINT32 hard_disk_write(hard_disk_file *file, UINT32 lbasector, const void *buffer)
+{
+       chd_error err = file->chd->write_units(lbasector, buffer);
+       return (err == CHDERR_NONE);
+}
diff --git a/archivers/chd/harddisk.h b/archivers/chd/harddisk.h
new file mode 100644 (file)
index 0000000..5b32c90
--- /dev/null
@@ -0,0 +1,49 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    harddisk.h
+
+    Generic MAME hard disk implementation, with differencing files
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __HARDDISK_H__
+#define __HARDDISK_H__
+
+#include "osdcore.h"
+#include "chd.h"
+
+
+/***************************************************************************
+    TYPE DEFINITIONS
+***************************************************************************/
+
+struct hard_disk_file;
+
+struct hard_disk_info
+{
+       UINT32          cylinders;
+       UINT32          heads;
+       UINT32          sectors;
+       UINT32          sectorbytes;
+};
+
+
+
+/***************************************************************************
+    FUNCTION PROTOTYPES
+***************************************************************************/
+
+hard_disk_file *hard_disk_open(chd_file *chd);
+void hard_disk_close(hard_disk_file *file);
+
+chd_file *hard_disk_get_chd(hard_disk_file *file);
+hard_disk_info *hard_disk_get_info(hard_disk_file *file);
+
+UINT32 hard_disk_read(hard_disk_file *file, UINT32 lbasector, void *buffer);
+UINT32 hard_disk_write(hard_disk_file *file, UINT32 lbasector, const void *buffer);
+
+#endif  /* __HARDDISK_H__ */
index 0dff0d68e6764399e43cba46a02e78ea69921329..17b6c9f9e0290274b60ab621bf719f72338c7e26 100644 (file)
@@ -1,44 +1,14 @@
+#include "chdtypes.h"
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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"
 
@@ -264,39 +234,39 @@ 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
-    };
+               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);
 
@@ -305,4 +275,4 @@ void crc16_creator::append(const void *data, UINT32 length)
        while (length-- != 0)
                crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++];
        m_accum.m_raw = crc;
-}
\ No newline at end of file
+}
index d98b617bf1ee64b0d592ad70441a8b95654e0603..78e02c6e145527a523328f99169e4e37bb1fe366 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
@@ -42,6 +13,7 @@
 #ifndef __HASHING_H__
 #define __HASHING_H__
 
+#include "osdcore.h"
 #include "astring.h"
 #include "md5.h"
 #include "sha1.h"
@@ -98,7 +70,7 @@ public:
 
 protected:
        // internal state
-       struct sha1_ctx         m_context;              // internal context
+       struct sha1_ctx     m_context;      // internal context
 };
 
 
@@ -148,7 +120,7 @@ public:
 
 protected:
        // internal state
-       struct MD5Context       m_context;              // internal context
+       struct MD5Context   m_context;      // internal context
 };
 
 
@@ -194,7 +166,7 @@ public:
 
 protected:
        // internal state
-       crc32_t                         m_accum;                // internal accumulator
+       crc32_t             m_accum;        // internal accumulator
 };
 
 
@@ -240,7 +212,7 @@ public:
 
 protected:
        // internal state
-       crc16_t                         m_accum;                // internal accumulator
+       crc16_t             m_accum;        // internal accumulator
 };
 
 
index ff232d1156e492580c7b51ababcc3dc87c82efee..aa38c93e8dc3e099ef5d0c5093553b8efbd08a0f 100644 (file)
@@ -1,43 +1,12 @@
-
 #include "chdtypes.h"
-
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
 //  MACROS
 //**************************************************************************
 
-#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f))
+#define MAKE_LOOKUP(code,bits)  (((code) << 5) | ((bits) & 0x1f))
 
 
 
 
 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)
+               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)
@@ -347,7 +316,7 @@ huffman_error huffman_context_base::import_tree_huffman(bitstream_in &bitbuf)
 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);
+       dynamic_buffer rle_data(m_numcodes);
        UINT8 *dest = rle_data;
        dynamic_array<UINT16> rle_lengths(m_numcodes/3);
        UINT16 *lengths = rle_lengths;
@@ -539,9 +508,9 @@ int CLIB_DECL huffman_context_base::tree_node_compare(const void *item1, const v
        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;
+               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");
+               fprintf(stderr, "identical node sort keys, should not happen!\n");
        return (int)node1->m_bits - (int)node2->m_bits;
 }
 
index 47525d82ed6a8d3baa469694fda94990cedb6248..76ee1b9c23b16dd0e82aa9b303134a0a2888fb56 100644 (file)
@@ -1,40 +1,11 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
 /***************************************************************************
 
     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
@@ -78,11 +49,11 @@ protected:
        // 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
+               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
@@ -108,13 +79,13 @@ protected:
 
 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
+       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
 };
 
 
@@ -141,8 +112,8 @@ public:
 
 private:
        // array versions of the info we need
-       UINT32                                  m_datahisto_array[_NumCodes];
-       node_t                                  m_huffnode_array[_NumCodes * 2];
+       UINT32                  m_datahisto_array[_NumCodes];
+       node_t                  m_huffnode_array[_NumCodes * 2];
 };
 
 
@@ -166,8 +137,8 @@ public:
 
 private:
        // array versions of the info we need
-       node_t                                  m_huffnode_array[_NumCodes];
-       lookup_value                    m_lookup_array[1 << _MaxBits];
+       node_t                  m_huffnode_array[_NumCodes];
+       lookup_value            m_lookup_array[1 << _MaxBits];
 };
 
 
index 0c9c91c7206a207d9de16a2ed9c6b31e2b75c3f1..dbf2b01fe3de3d704eef51f453b119892d7357fc 100644 (file)
@@ -1,3 +1,4 @@
+#include "chdtypes.h"
 /*
  * This code implements the MD5 message-digest algorithm.
  * The algorithm is due to Ron Rivest.  This code was
@@ -20,7 +21,7 @@
  * Still in the public domain.
  */
 
-#include <string.h>            /* for memcpy() */
+#include <string.h>     /* for memcpy() */
 
 #include "md5.h"
 
@@ -69,9 +70,9 @@ MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len)
 
        t = ctx->bytes[0];
        if ((ctx->bytes[0] = t + len) < t)
-               ctx->bytes[1]++;        /* Carry from low to high */
+               ctx->bytes[1]++;    /* Carry from low to high */
 
-       t = 64 - (t & 0x3f);    /* Space available in ctx->in (at least 1) */
+       t = 64 - (t & 0x3f);    /* Space available in ctx->in (at least 1) */
        if (t > len) {
                memcpy((md5byte *)ctx->in + 64 - t, buf, len);
                return;
@@ -103,7 +104,7 @@ MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len)
 void
 MD5Final(md5byte digest[16], struct MD5Context *ctx)
 {
-       int count = ctx->bytes[0] & 0x3f;       /* Number of bytes in ctx->in */
+       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. */
@@ -112,7 +113,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx)
        /* Bytes of padding needed to make 56 bytes (-8..55) */
        count = 56 - 1 - count;
 
-       if (count < 0) {        /* Padding forces an extra block */
+       if (count < 0) {    /* Padding forces an extra block */
                memset(p, 0, count + 8);
                byteSwap(ctx->in, 16);
                MD5Transform(ctx->buf, ctx->in);
@@ -129,7 +130,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx)
 
        byteSwap(ctx->buf, 4);
        memcpy(digest, ctx->buf, 16);
-       memset(ctx, 0, sizeof(MD5Context));     /* In case it's sensitive */
+       memset(ctx, 0, sizeof(MD5Context)); /* In case it's sensitive */
 }
 
 #ifndef ASM_MD5
@@ -144,7 +145,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx)
 
 /* 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)
+               (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
diff --git a/archivers/chd/osdcomm.h b/archivers/chd/osdcomm.h
new file mode 100644 (file)
index 0000000..4c78355
--- /dev/null
@@ -0,0 +1,226 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    osdcomm.h
+
+    Common definitions shared by the OSD layer. This includes the most
+    fundamental integral types as well as compiler-specific tweaks.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+
+#include <stdio.h>
+#include <string.h>
+
+
+/***************************************************************************
+    COMPILER-SPECIFIC NASTINESS
+***************************************************************************/
+
+/* The Win32 port requires this constant for variable arg routines. */
+#ifndef CLIB_DECL
+#define CLIB_DECL
+#endif
+
+
+/* Some optimizations/warnings cleanups for GCC */
+#if defined(__GNUC__) && (__GNUC__ >= 3)
+#define ATTR_UNUSED             __attribute__((__unused__))
+#define ATTR_NORETURN           __attribute__((noreturn))
+#define ATTR_PRINTF(x,y)        __attribute__((format(printf, x, y)))
+#define ATTR_MALLOC             __attribute__((malloc))
+#define ATTR_PURE               __attribute__((pure))
+#define ATTR_CONST              __attribute__((const))
+#define ATTR_FORCE_INLINE       __attribute__((always_inline))
+#define ATTR_NONNULL(...)       __attribute__((nonnull(__VA_ARGS__)))
+#define ATTR_DEPRECATED         __attribute__((deprecated))
+/* not supported in GCC prior to 4.4.x */
+#if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)
+#define ATTR_HOT                __attribute__((hot))
+#define ATTR_COLD               __attribute__((cold))
+#else
+#define ATTR_HOT
+#define ATTR_COLD
+#endif
+#define UNEXPECTED(exp)         __builtin_expect(!!(exp), 0)
+#define EXPECTED(exp)           __builtin_expect(!!(exp), 1)
+#define RESTRICT                __restrict__
+#define SETJMP_GNUC_PROTECT()   (void)__builtin_return_address(1)
+#else
+#define ATTR_UNUSED
+#if defined(_MSC_VER) && (_MSC_VER >= 1200)
+#define ATTR_NORETURN           __declspec(noreturn)
+#else
+#define ATTR_NORETURN
+#endif
+#define ATTR_PRINTF(x,y)
+#define ATTR_MALLOC
+#define ATTR_PURE
+#define ATTR_CONST
+#define ATTR_FORCE_INLINE       __forceinline
+#define ATTR_NONNULL(...)
+#define ATTR_DEPRECATED         __declspec(deprecated)
+#define ATTR_HOT
+#define ATTR_COLD
+#define UNEXPECTED(exp)         (exp)
+#define EXPECTED(exp)           (exp)
+#define RESTRICT
+#define SETJMP_GNUC_PROTECT()   do {} while (0)
+#endif
+
+
+
+/***************************************************************************
+    FUNDAMENTAL TYPES
+***************************************************************************/
+
+/* These types work on most modern compilers; however, OSD code can
+   define their own by setting OSD_TYPES_DEFINED */
+
+#ifndef OSD_TYPES_DEFINED
+
+/* 8-bit values */
+typedef unsigned char                       UINT8;
+typedef signed char                         INT8;
+
+/* 16-bit values */
+typedef unsigned short                      UINT16;
+typedef signed short                        INT16;
+
+/* 32-bit values */
+#ifndef _WINDOWS_H
+typedef unsigned int                        UINT32;
+typedef signed int                          INT32;
+#endif
+
+/* 64-bit values */
+#ifndef _WINDOWS_H
+#ifdef _MSC_VER
+typedef signed __int64                      INT64;
+typedef unsigned __int64                    UINT64;
+#else
+__extension__ typedef unsigned long long    UINT64;
+__extension__ typedef signed long long      INT64;
+#endif
+#endif
+
+#endif
+
+/* pointer-sized values */
+#ifdef PTR64
+typedef UINT64                              FPTR;
+#else
+typedef UINT32                              FPTR;
+#endif
+
+
+
+/***************************************************************************
+    FUNDAMENTAL CONSTANTS
+***************************************************************************/
+
+/* Ensure that TRUE/FALSE are defined */
+#ifndef TRUE
+#define TRUE                1
+#endif
+
+#ifndef FALSE
+#define FALSE               0
+#endif
+
+
+
+/***************************************************************************
+    FUNDAMENTAL MACROS
+***************************************************************************/
+
+/* Standard MIN/MAX macros */
+#ifndef MIN
+#define MIN(x,y)            ((x) < (y) ? (x) : (y))
+#endif
+#ifndef MAX
+#define MAX(x,y)            ((x) > (y) ? (x) : (y))
+#endif
+
+
+/* U64 and S64 are used to wrap long integer constants. */
+#if defined(__GNUC__) || defined(_MSC_VER)
+#define U64(val) val##ULL
+#define S64(val) val##LL
+#else
+#define U64(val) val
+#define S64(val) val
+#endif
+
+
+/* Concatenate/extract 32-bit halves of 64-bit values */
+#define CONCAT_64(hi,lo)    (((UINT64)(hi) << 32) | (UINT32)(lo))
+#define EXTRACT_64HI(val)   ((UINT32)((val) >> 32))
+#define EXTRACT_64LO(val)   ((UINT32)(val))
+
+
+/* MINGW has adopted the MSVC formatting for 64-bit ints as of gcc 4.4 */
+#if (defined(__MINGW32__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4))) || defined(_MSC_VER)
+#define I64FMT   "I64"
+#else
+#define I64FMT   "ll"
+#endif
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#ifdef PTR64
+#define SIZETFMT   "I64u"
+#else
+#define SIZETFMT   "u"
+#endif
+#else
+#define SIZETFMT   "zu"
+#endif
+
+
+/* Highly useful macro for compile-time knowledge of an array size */
+#define ARRAY_LENGTH(x)     (sizeof(x) / sizeof(x[0]))
+
+
+/* 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)      \
+       )
+
+#ifdef LSB_FIRST
+#define BIG_ENDIANIZE_INT16(x)      (FLIPENDIAN_INT16(x))
+#define BIG_ENDIANIZE_INT32(x)      (FLIPENDIAN_INT32(x))
+#define BIG_ENDIANIZE_INT64(x)      (FLIPENDIAN_INT64(x))
+#define LITTLE_ENDIANIZE_INT16(x)   (x)
+#define LITTLE_ENDIANIZE_INT32(x)   (x)
+#define LITTLE_ENDIANIZE_INT64(x)   (x)
+#else
+#define BIG_ENDIANIZE_INT16(x)      (x)
+#define BIG_ENDIANIZE_INT32(x)      (x)
+#define BIG_ENDIANIZE_INT64(x)      (x)
+#define LITTLE_ENDIANIZE_INT16(x)   (FLIPENDIAN_INT16(x))
+#define LITTLE_ENDIANIZE_INT32(x)   (FLIPENDIAN_INT32(x))
+#define LITTLE_ENDIANIZE_INT64(x)   (FLIPENDIAN_INT64(x))
+#endif /* LSB_FIRST */
+
+// compatibility with non-clang compilers
+#ifndef __has_feature
+       #define __has_feature(x) 0
+#endif
+
+#endif  /* __OSDCOMM_H__ */
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d46938f215b0d65b7d9876044d953780879e1708 100644 (file)
@@ -0,0 +1,939 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+    osdcore.h
+
+    Core OS-dependent code interface.
+
+****************************************************************************
+
+    The prototypes in this file describe the interfaces that the MAME core
+    and various tools rely upon to interact with the outside world. They are
+    broken out into several categories.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __OSDCORE_H__
+#define __OSDCORE_H__
+
+#include "osdcomm.h"
+#include "delegate.h"
+
+/***************************************************************************
+    FILE I/O INTERFACES
+***************************************************************************/
+
+/* Make sure we have a path separator (default to /) */
+#ifndef PATH_SEPARATOR
+#define PATH_SEPARATOR          "/"
+#endif
+
+/* flags controlling file access */
+#define OPEN_FLAG_READ          0x0001      /* open for read */
+#define OPEN_FLAG_WRITE         0x0002      /* open for write */
+#define OPEN_FLAG_CREATE        0x0004      /* create & truncate file */
+#define OPEN_FLAG_CREATE_PATHS  0x0008      /* create paths as necessary */
+#define OPEN_FLAG_NO_PRELOAD    0x0010      /* do not decompress on open */
+
+/* error codes returned by routines below */
+enum file_error
+{
+       FILERR_NONE,
+       FILERR_FAILURE,
+       FILERR_OUT_OF_MEMORY,
+       FILERR_NOT_FOUND,
+       FILERR_ACCESS_DENIED,
+       FILERR_ALREADY_OPEN,
+       FILERR_TOO_MANY_FILES,
+       FILERR_INVALID_DATA,
+       FILERR_INVALID_ACCESS
+};
+
+/* osd_file is an opaque type which represents an open file */
+struct osd_file;
+
+/*-----------------------------------------------------------------------------
+    osd_open: open a new file.
+
+    Parameters:
+
+        path - path to the file to open
+
+        openflags - some combination of:
+
+            OPEN_FLAG_READ - open the file for read access
+            OPEN_FLAG_WRITE - open the file for write access
+            OPEN_FLAG_CREATE - create/truncate the file when opening
+            OPEN_FLAG_CREATE_PATHS - specifies that non-existant paths
+                    should be created if necessary
+
+        file - pointer to an osd_file * to receive the newly-opened file
+            handle; this is only valid if the function returns FILERR_NONE
+
+        filesize - pointer to a UINT64 to receive the size of the opened
+            file; this is only valid if the function returns FILERR_NONE
+
+    Return value:
+
+        a file_error describing any error that occurred while opening
+        the file, or FILERR_NONE if no error occurred
+
+    Notes:
+
+        This function is called by core_fopen and several other places in
+        the core to access files. These functions will construct paths by
+        concatenating various search paths held in the options.c options
+        database with partial paths specified by the core. The core assumes
+        that the path separator is the first character of the string
+        PATH_SEPARATOR, but does not interpret any path separators in the
+        search paths, so if you use a different path separator in a search
+        path, you may get a mixture of PATH_SEPARATORs (from the core) and
+        alternate path separators (specified by users and placed into the
+        options database).
+-----------------------------------------------------------------------------*/
+file_error osd_open(const TCHAR *path, UINT32 openflags, osd_file **file, UINT64 *filesize);
+
+
+/*-----------------------------------------------------------------------------
+    osd_close: close an open file
+
+    Parameters:
+
+        file - handle to a file previously opened via osd_open
+
+    Return value:
+
+        a file_error describing any error that occurred while closing
+        the file, or FILERR_NONE if no error occurred
+-----------------------------------------------------------------------------*/
+file_error osd_close(osd_file *file);
+
+
+/*-----------------------------------------------------------------------------
+    osd_read: read from an open file
+
+    Parameters:
+
+        file - handle to a file previously opened via osd_open
+
+        buffer - pointer to memory that will receive the data read
+
+        offset - offset within the file to read from
+
+        length - number of bytes to read from the file
+
+        actual - pointer to a UINT32 to receive the number of bytes actually
+            read during the operation; valid only if the function returns
+            FILERR_NONE
+
+    Return value:
+
+        a file_error describing any error that occurred while reading
+        from the file, or FILERR_NONE if no error occurred
+-----------------------------------------------------------------------------*/
+file_error osd_read(osd_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual);
+
+
+/*-----------------------------------------------------------------------------
+    osd_write: write to an open file
+
+    Parameters:
+
+        file - handle to a file previously opened via osd_open
+
+        buffer - pointer to memory that contains the data to write
+
+        offset - offset within the file to write to
+
+        length - number of bytes to write to the file
+
+        actual - pointer to a UINT32 to receive the number of bytes actually
+            written during the operation; valid only if the function returns
+            FILERR_NONE
+
+    Return value:
+
+        a file_error describing any error that occurred while writing to
+        the file, or FILERR_NONE if no error occurred
+-----------------------------------------------------------------------------*/
+file_error osd_write(osd_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual);
+
+
+/*-----------------------------------------------------------------------------
+    osd_truncate: change the size of an open file
+
+    Parameters:
+
+        file - handle to a file previously opened via osd_open
+
+        offset - future size of the file
+
+    Return value:
+
+        a file_error describing any error that occurred while writing to
+        the file, or FILERR_NONE if no error occurred
+-----------------------------------------------------------------------------*/
+file_error osd_truncate(osd_file *file, UINT64 offset);
+
+
+/*-----------------------------------------------------------------------------
+    osd_rmfile: deletes a file
+
+    Parameters:
+
+        filename - path to file to delete
+
+    Return value:
+
+        a file_error describing any error that occurred while deleting
+        the file, or FILERR_NONE if no error occurred
+-----------------------------------------------------------------------------*/
+file_error osd_rmfile(const TCHAR *filename);
+
+
+/*-----------------------------------------------------------------------------
+    osd_get_physical_drive_geometry: if the given path points to a physical
+        drive, return the geometry of that drive
+
+    Parameters:
+
+        filename - pointer to a path which might describe a physical drive
+
+        cylinders - pointer to a UINT32 to receive the number of cylinders
+            of the physical drive
+
+        heads - pointer to a UINT32 to receive the number of heads per
+            cylinder of the physical drive
+
+        sectors - pointer to a UINT32 to receive the number of sectors per
+            cylinder of the physical drive
+
+        bps - pointer to a UINT32 to receive the number of bytes per sector
+            of the physical drive
+
+    Return value:
+
+        TRUE if the filename points to a physical drive and if the values
+        pointed to by cylinders, heads, sectors, and bps are valid; FALSE in
+        any other case
+-----------------------------------------------------------------------------*/
+int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UINT32 *heads, UINT32 *sectors, UINT32 *bps);
+
+
+/*-----------------------------------------------------------------------------
+    osd_uchar_from_osdchar: convert the given character or sequence of
+        characters from the OS-default encoding to a Unicode character
+
+    Parameters:
+
+        uchar - pointer to a UINT32 to receive the resulting unicode
+            character
+
+        osdchar - pointer to one or more chars that are in the OS-default
+            encoding
+
+        count - number of characters provided in the OS-default encoding
+
+    Return value:
+
+        The number of characters required to form a Unicode character.
+-----------------------------------------------------------------------------*/
+int osd_uchar_from_osdchar(UINT32 /* unicode_char */ *uchar, const char *osdchar, size_t count);
+
+
+
+/***************************************************************************
+    DIRECTORY INTERFACES
+***************************************************************************/
+
+/* types of directory entries that can be returned */
+enum osd_dir_entry_type
+{
+       ENTTYPE_NONE,
+       ENTTYPE_FILE,
+       ENTTYPE_DIR,
+       ENTTYPE_OTHER
+};
+
+/* osd_directory is an opaque type which represents an open directory */
+struct osd_directory;
+
+/* osd_directory_entry contains basic information about a file when iterating through */
+/* a directory */
+struct osd_directory_entry
+{
+       const char *        name;           /* name of the entry */
+       osd_dir_entry_type  type;           /* type of the entry */
+       UINT64              size;           /* size of the entry */
+};
+
+
+/*-----------------------------------------------------------------------------
+    osd_opendir: open a directory for iteration
+
+    Parameters:
+
+        dirname - path to the directory in question
+
+    Return value:
+
+        upon success, this function should return an osd_directory pointer
+        which contains opaque data necessary to traverse the directory; on
+        failure, this function should return NULL
+-----------------------------------------------------------------------------*/
+osd_directory *osd_opendir(const char *dirname);
+
+
+/*-----------------------------------------------------------------------------
+    osd_readdir: return information about the next entry in the directory
+
+    Parameters:
+
+        dir - pointer to an osd_directory that was returned from a prior
+            call to osd_opendir
+
+    Return value:
+
+        a constant pointer to an osd_directory_entry representing the current item
+        in the directory, or NULL, indicating that no more entries are
+        present
+-----------------------------------------------------------------------------*/
+const osd_directory_entry *osd_readdir(osd_directory *dir);
+
+
+/*-----------------------------------------------------------------------------
+    osd_closedir: close an open directory for iteration
+
+    Parameters:
+
+        dir - pointer to an osd_directory that was returned from a prior
+            call to osd_opendir
+
+    Return value:
+
+        frees any allocated memory and resources associated with the open
+        directory
+-----------------------------------------------------------------------------*/
+void osd_closedir(osd_directory *dir);
+
+
+/*-----------------------------------------------------------------------------
+    osd_is_absolute_path: returns whether the specified path is absolute
+
+    Parameters:
+
+        path - the path in question
+
+    Return value:
+
+        non-zero if the path is absolute, zero otherwise
+-----------------------------------------------------------------------------*/
+int osd_is_absolute_path(const char *path);
+
+
+
+/***************************************************************************
+    TIMING INTERFACES
+***************************************************************************/
+
+/* a osd_ticks_t is a 64-bit unsigned integer that is used as a core type in timing interfaces */
+typedef UINT64 osd_ticks_t;
+
+
+/*-----------------------------------------------------------------------------
+    osd_ticks: return the current running tick counter
+
+    Parameters:
+
+        None
+
+    Return value:
+
+        an osd_ticks_t value which represents the current tick counter
+
+    Notes:
+
+        The resolution of this counter should be 1ms or better for accurate
+        performance. It is also important that the source of this timer be
+        accurate. It is ok if this call is not ultra-fast, since it is
+        primarily used for once/frame synchronization.
+-----------------------------------------------------------------------------*/
+osd_ticks_t osd_ticks(void);
+
+
+/*-----------------------------------------------------------------------------
+    osd_ticks_per_second: return the number of ticks per second
+
+    Parameters:
+
+        None
+
+    Return value:
+
+        an osd_ticks_t value which represents the number of ticks per
+        second
+-----------------------------------------------------------------------------*/
+osd_ticks_t osd_ticks_per_second(void);
+
+
+/*-----------------------------------------------------------------------------
+    osd_sleep: sleep for the specified time interval
+
+    Parameters:
+
+        duration - an osd_ticks_t value that specifies how long we should
+            sleep
+
+    Return value:
+
+        None
+
+    Notes:
+
+        The OSD layer should try to sleep for as close to the specified
+        duration as possible, or less. This is used as a mechanism to
+        "give back" unneeded time to other programs running in the system.
+        On a simple, non multitasking system, this routine can be a no-op.
+        If there is significant volatility in the amount of time that the
+        sleep occurs for, the OSD layer should strive to sleep for less time
+        than specified rather than sleeping too long.
+-----------------------------------------------------------------------------*/
+void osd_sleep(osd_ticks_t duration);
+
+
+
+/***************************************************************************
+    SYNCHRONIZATION INTERFACES
+***************************************************************************/
+
+/* osd_lock is an opaque type which represents a recursive lock/mutex */
+struct osd_lock;
+
+
+/*-----------------------------------------------------------------------------
+    osd_lock_alloc: allocate a new lock
+
+    Parameters:
+
+        None.
+
+    Return value:
+
+        A pointer to the allocated lock.
+-----------------------------------------------------------------------------*/
+osd_lock *osd_lock_alloc(void);
+
+
+/*-----------------------------------------------------------------------------
+    osd_lock_acquire: acquire a lock, blocking until it can be acquired
+
+    Parameters:
+
+        lock - a pointer to a previously allocated osd_lock.
+
+    Return value:
+
+        None.
+
+    Notes:
+
+        osd_locks are defined to be recursive. If the current thread already
+        owns the lock, this function should return immediately.
+-----------------------------------------------------------------------------*/
+void osd_lock_acquire(osd_lock *lock);
+
+
+/*-----------------------------------------------------------------------------
+    osd_lock_try: attempt to acquire a lock
+
+    Parameters:
+
+        lock - a pointer to a previously allocated osd_lock.
+
+    Return value:
+
+        TRUE if the lock was available and was acquired successfully.
+        FALSE if the lock was already in used by another thread.
+-----------------------------------------------------------------------------*/
+int osd_lock_try(osd_lock *lock);
+
+
+/*-----------------------------------------------------------------------------
+    osd_lock_release: release control of a lock that has been acquired
+
+    Parameters:
+
+        lock - a pointer to a previously allocated osd_lock.
+
+    Return value:
+
+        None.
+-----------------------------------------------------------------------------*/
+void osd_lock_release(osd_lock *lock);
+
+
+/*-----------------------------------------------------------------------------
+    osd_lock_free: free the memory and resources associated with an osd_lock
+
+    Parameters:
+
+        lock - a pointer to a previously allocated osd_lock.
+
+    Return value:
+
+        None.
+-----------------------------------------------------------------------------*/
+void osd_lock_free(osd_lock *lock);
+
+
+
+/***************************************************************************
+    WORK ITEM INTERFACES
+***************************************************************************/
+
+/* this is the maximum number of supported threads for a single work queue */
+/* threadid values are expected to range from 0..WORK_MAX_THREADS-1 */
+#define WORK_MAX_THREADS            16
+
+/* these flags can be set when creating a queue to give hints to the code about
+   how to configure the queue */
+#define WORK_QUEUE_FLAG_IO          0x0001
+#define WORK_QUEUE_FLAG_MULTI       0x0002
+#define WORK_QUEUE_FLAG_HIGH_FREQ   0x0004
+
+/* these flags can be set when queueing a work item to indicate how to handle
+   its deconstruction */
+#define WORK_ITEM_FLAG_AUTO_RELEASE 0x0001
+
+/* osd_work_queue is an opaque type which represents a queue of work items */
+struct osd_work_queue;
+
+/* osd_work_item is an opaque type which represents a single work item */
+struct osd_work_item;
+
+/* osd_work_callback is a callback function that does work */
+typedef void *(*osd_work_callback)(void *param, int threadid);
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_queue_alloc: create a new work queue
+
+    Parameters:
+
+        flags - one or more of the WORK_QUEUE_FLAG_* values ORed together:
+
+            WORK_QUEUE_FLAG_IO - indicates that the work queue will do some
+                I/O; this may be a useful hint so that threads are created
+                even on single-processor systems since I/O can often be
+                overlapped with other work
+
+            WORK_QUEUE_FLAG_MULTI - indicates that the work queue should
+                take advantage of as many processors as it can; items queued
+                here are assumed to be fully independent or shared
+
+            WORK_QUEUE_FLAG_HIGH_FREQ - indicates that items are expected
+                to be queued at high frequency and acted upon quickly; in
+                general, this implies doing some spin-waiting internally
+                before falling back to OS-specific synchronization
+
+    Return value:
+
+        A pointer to an allocated osd_work_queue object.
+
+    Notes:
+
+        A work queue abstracts the notion of how potentially threaded work
+        can be performed. If no threading support is available, it is a
+        simple matter to execute the work items as they are queued.
+-----------------------------------------------------------------------------*/
+osd_work_queue *osd_work_queue_alloc(int flags);
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_queue_items: return the number of pending items in the queue
+
+    Parameters:
+
+        queue - pointer to an osd_work_queue that was previously created via
+            osd_work_queue_alloc
+
+    Return value:
+
+        The number of incomplete items remaining in the queue.
+-----------------------------------------------------------------------------*/
+int osd_work_queue_items(osd_work_queue *queue);
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_queue_wait: wait for the queue to be empty
+
+    Parameters:
+
+        queue - pointer to an osd_work_queue that was previously created via
+            osd_work_queue_alloc
+
+        timeout - a timeout value in osd_ticks_per_second()
+
+    Return value:
+
+        TRUE if the queue is empty; FALSE if the wait timed out before the
+        queue was emptied.
+-----------------------------------------------------------------------------*/
+int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout);
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_queue_free: free a work queue, waiting for all items to complete
+
+    Parameters:
+
+        queue - pointer to an osd_work_queue that was previously created via
+            osd_work_queue_alloc
+
+    Return value:
+
+        None.
+-----------------------------------------------------------------------------*/
+void osd_work_queue_free(osd_work_queue *queue);
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_item_queue_multiple: queue a set of work items
+
+    Parameters:
+
+        queue - pointer to an osd_work_queue that was previously created via
+            osd_work_queue_alloc
+
+        callback - pointer to a function that will do the work
+
+        numitems - number of work items to queue
+
+        param - a void * parameter that can be used to pass data to the
+            function
+
+        paramstep - the number of bytes to increment param by for each item
+            queued; for example, if you have an array of work_unit objects,
+            you can point param to the base of the array and set paramstep to
+            sizeof(work_unit)
+
+        flags - one or more of the WORK_ITEM_FLAG_* values ORed together:
+
+            WORK_ITEM_FLAG_AUTO_RELEASE - indicates that the work item
+                should be automatically freed when it is complete
+
+    Return value:
+
+        A pointer to the final allocated osd_work_item in the list.
+
+    Notes:
+
+        On single-threaded systems, this function may actually execute the
+        work item immediately before returning.
+-----------------------------------------------------------------------------*/
+osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags);
+
+
+/* inline helper to queue a single work item using the same interface */
+INLINE osd_work_item *osd_work_item_queue(osd_work_queue *queue, osd_work_callback callback, void *param, UINT32 flags)
+{
+       return osd_work_item_queue_multiple(queue, callback, 1, param, 0, flags);
+}
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_item_wait: wait for a work item to complete
+
+    Parameters:
+
+        item - pointer to an osd_work_item that was previously returned from
+            osd_work_item_queue
+
+        timeout - a timeout value in osd_ticks_per_second()
+
+    Return value:
+
+        TRUE if the item completed; FALSE if the wait timed out before the
+        item completed.
+-----------------------------------------------------------------------------*/
+int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout);
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_item_result: get the result of a work item
+
+    Parameters:
+
+        item - pointer to an osd_work_item that was previously returned from
+            osd_work_item_queue
+
+    Return value:
+
+        A void * that represents the work item's result.
+-----------------------------------------------------------------------------*/
+void *osd_work_item_result(osd_work_item *item);
+
+
+/*-----------------------------------------------------------------------------
+    osd_work_item_release: release the memory allocated to a work item
+
+    Parameters:
+
+        item - pointer to an osd_work_item that was previously returned from
+            osd_work_item_queue
+
+    Return value:
+
+        None.
+
+    Notes:
+
+        The osd_work_item exists until explicitly released, even if it has
+        long since completed. It is the queuer's responsibility to release
+        any work items it has queued.
+-----------------------------------------------------------------------------*/
+void osd_work_item_release(osd_work_item *item);
+
+
+
+/***************************************************************************
+    MISCELLANEOUS INTERFACES
+***************************************************************************/
+
+/*-----------------------------------------------------------------------------
+    osd_malloc: allocate memory
+
+    Parameters:
+
+        size - the number of bytes to allocate
+
+    Return value:
+
+        a pointer to the allocated memory
+
+    Notes:
+
+        This is just a hook to do OS-specific allocation trickery.
+        It can be safely written as a wrapper to malloc().
+-----------------------------------------------------------------------------*/
+void *osd_malloc(size_t size);
+
+
+/*-----------------------------------------------------------------------------
+    osd_malloc_array: allocate memory, hinting tha this memory contains an
+    array
+
+    Parameters:
+
+        size - the number of bytes to allocate
+
+    Return value:
+
+        a pointer to the allocated memory
+
+    Notes:
+
+        This is just a hook to do OS-specific allocation trickery.
+        It can be safely written as a wrapper to malloc().
+-----------------------------------------------------------------------------*/
+void *osd_malloc_array(size_t size);
+
+
+/*-----------------------------------------------------------------------------
+    osd_free: free memory allocated by osd_malloc
+
+    Parameters:
+
+        ptr - the pointer returned from osd_mallo
+
+    Return value:
+
+        None
+-----------------------------------------------------------------------------*/
+void osd_free(void *ptr);
+
+
+/*-----------------------------------------------------------------------------
+    osd_alloc_executable: allocate memory that can contain executable code
+
+    Parameters:
+
+        size - the number of bytes to allocate
+
+    Return value:
+
+        a pointer to the allocated memory
+
+    Notes:
+
+        On many systems, this call may acceptably map to malloc(). On systems
+        where pages are tagged with "no execute" privileges, it may be
+        necessary to perform some kind of special allocation to ensure that
+        code placed into this buffer can be executed.
+-----------------------------------------------------------------------------*/
+void *osd_alloc_executable(size_t size);
+
+
+/*-----------------------------------------------------------------------------
+    osd_free_executable: free memory allocated by osd_alloc_executable
+
+    Parameters:
+
+        ptr - the pointer returned from osd_alloc_executable
+
+        size - the number of bytes originally requested
+
+    Return value:
+
+        None
+-----------------------------------------------------------------------------*/
+void osd_free_executable(void *ptr, size_t size);
+
+
+/*-----------------------------------------------------------------------------
+    osd_break_into_debugger: break into the hosting system's debugger if one
+        is attached
+
+    Parameters:
+
+        message - pointer to string to output to the debugger
+
+    Return value:
+
+        None.
+
+    Notes:
+
+        This function is called when an assertion or other important error
+        occurs. If a debugger is attached to the current process, it should
+        break into the debugger and display the given message.
+-----------------------------------------------------------------------------*/
+void osd_break_into_debugger(const char *message);
+
+
+/*-----------------------------------------------------------------------------
+  MESS specific code below
+-----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+    osd_get_clipboard_text: retrieves text from the clipboard
+
+    Return value:
+
+        the returned string needs to be osd_free()-ed!
+
+-----------------------------------------------------------------------------*/
+char *osd_get_clipboard_text(void);
+
+
+/***************************************************************************
+    DIRECTORY INTERFACES
+***************************************************************************/
+
+/*-----------------------------------------------------------------------------
+    osd_stat: return a directory entry for a path
+
+    Parameters:
+
+        path - path in question
+
+    Return value:
+
+        an allocated pointer to an osd_directory_entry representing
+        info on the path; even if the file does not exist.
+        free with osd_free()
+
+-----------------------------------------------------------------------------*/
+osd_directory_entry *osd_stat(const char *path);
+
+/***************************************************************************
+    PATH INTERFACES
+***************************************************************************/
+
+/*-----------------------------------------------------------------------------
+    osd_get_full_path: retrieves the full path
+
+    Parameters:
+
+        path - the path in question
+        dst - pointer to receive new path; the returned string needs to be osd_free()-ed!
+
+    Return value:
+
+        file error
+
+-----------------------------------------------------------------------------*/
+file_error osd_get_full_path(char **dst, const char *path);
+
+
+/***************************************************************************
+    MIDI I/O INTERFACES
+***************************************************************************/
+struct osd_midi_device;
+
+void osd_list_midi_devices(void);
+// free result with osd_close_midi_channel()
+osd_midi_device *osd_open_midi_input(const char *devname);
+// free result with osd_close_midi_channel()
+osd_midi_device *osd_open_midi_output(const char *devname);
+void osd_close_midi_channel(osd_midi_device *dev);
+bool osd_poll_midi_channel(osd_midi_device *dev);
+int osd_read_midi_channel(osd_midi_device *dev, UINT8 *pOut);
+void osd_write_midi_channel(osd_midi_device *dev, UINT8 data);
+
+/***************************************************************************
+    UNCATEGORIZED INTERFACES
+***************************************************************************/
+
+/*-----------------------------------------------------------------------------
+    osd_get_volume_name: retrieves the volume name
+
+    Parameters:
+
+        idx - order number of volume
+
+    Return value:
+
+        pointer to volume name
+
+-----------------------------------------------------------------------------*/
+const char *osd_get_volume_name(int idx);
+
+/* ----- output management ----- */
+
+// output channels
+enum output_channel
+{
+       OSD_OUTPUT_CHANNEL_ERROR,
+       OSD_OUTPUT_CHANNEL_WARNING,
+       OSD_OUTPUT_CHANNEL_INFO,
+       OSD_OUTPUT_CHANNEL_DEBUG,
+       OSD_OUTPUT_CHANNEL_VERBOSE,
+       OSD_OUTPUT_CHANNEL_LOG,
+       OSD_OUTPUT_CHANNEL_COUNT
+};
+
+// output channel callback
+typedef delegate<void (const char *, va_list)> output_delegate;
+
+/* set the output handler for a channel, returns the current one */
+output_delegate osd_set_output_channel(output_channel channel, output_delegate callback);
+
+/* calls to be used by the code */
+void CLIB_DECL osd_printf_error(const char *format, ...) ATTR_PRINTF(1,2);
+void CLIB_DECL osd_printf_warning(const char *format, ...) ATTR_PRINTF(1,2);
+void CLIB_DECL osd_printf_info(const char *format, ...) ATTR_PRINTF(1,2);
+void CLIB_DECL osd_printf_verbose(const char *format, ...) ATTR_PRINTF(1,2);
+void CLIB_DECL osd_printf_debug(const char *format, ...) ATTR_PRINTF(1,2);
+
+/* discourage the use of printf directly */
+/* sadly, can't do this because of the ATTR_PRINTF under GCC */
+/*
+#undef printf
+#define printf !MUST_USE_osd_printf_*_CALLS_WITHIN_THE_CORE!
+*/
+
+#endif  /* __OSDEPEND_H__ */
diff --git a/archivers/chd/osinline.h b/archivers/chd/osinline.h
new file mode 100644 (file)
index 0000000..3e28c98
--- /dev/null
@@ -0,0 +1,51 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+//  osinline.h
+//
+//  Inline implementations for non-GCC Win32 compilers
+//
+//============================================================
+
+#ifndef __OSINLINE__
+#define __OSINLINE__
+
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include "eivcx86.h"
+#endif
+
+#if defined(_MSC_VER)
+#include "eivc.h"
+#endif
+
+INT32 win_compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange);
+INT32 win_atomic_exchange32(INT32 volatile *ptr, INT32 exchange);
+INT32 win_atomic_add32(INT32 volatile *ptr, INT32 delta);
+
+#ifdef PTR64
+INT64 win_compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange);
+#endif
+
+
+#ifndef compare_exchange32
+#define compare_exchange32 win_compare_exchange32
+#endif /* compare_exchange32 */
+
+#ifdef PTR64
+#ifndef compare_exchange64
+#define compare_exchange64 win_compare_exchange64
+#endif /* compare_exchange64 */
+#endif
+
+#ifndef atomic_exchange32
+#define atomic_exchange32 win_atomic_exchange32
+#endif /* atomic_exchange32 */
+
+
+#ifndef atomic_add32
+#define atomic_add32 win_atomic_add32
+#endif /* atomic_add32 */
+
+
+#endif /* __OSINLINE__ */
diff --git a/archivers/chd/palette.h b/archivers/chd/palette.h
new file mode 100644 (file)
index 0000000..939138e
--- /dev/null
@@ -0,0 +1,274 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/******************************************************************************
+
+    palette.h
+
+    Core palette routines.
+
+***************************************************************************/
+
+#pragma once
+
+#ifndef __PALETTE_H__
+#define __PALETTE_H__
+
+#include "osdcore.h"
+#include "coretmpl.h"
+
+
+//**************************************************************************
+//  TYPE DEFINITIONS
+//**************************************************************************
+
+// forward definitions
+class palette_t;
+
+// an rgb15_t is a single combined 15-bit R,G,B value
+typedef UINT16 rgb15_t;
+
+
+// ======================> rgb_t
+
+// an rgb_t is a single combined R,G,B (and optionally alpha) value
+class rgb_t
+{
+public:
+       // construction/destruction
+       rgb_t() { }
+       rgb_t(UINT32 data) { m_data = data; }
+       rgb_t(UINT8 r, UINT8 g, UINT8 b) { m_data = (255 << 24) | (r << 16) | (g << 8) | b; }
+       rgb_t(UINT8 a, UINT8 r, UINT8 g, UINT8 b) { m_data = (a << 24) | (r << 16) | (g << 8) | b; }
+
+       // getters
+       UINT8 a() const { return m_data >> 24; }
+       UINT8 r() const { return m_data >> 16; }
+       UINT8 g() const { return m_data >> 8; }
+       UINT8 b() const { return m_data >> 0; }
+       rgb15_t as_rgb15() const { return ((r() >> 3) << 10) | ((g() >> 3) << 5) | ((b() >> 3) << 0); }
+       UINT8 brightness() const { return (r() * 222 + g() * 707 + b() * 71) / 1000; }
+       UINT32 const *ptr() const { return &m_data; }
+
+       // setters
+       rgb_t &set_a(UINT8 a) { m_data &= ~0xff000000; m_data |= a << 24; return *this; }
+       rgb_t &set_r(UINT8 r) { m_data &= ~0x00ff0000; m_data |= r << 16; return *this; }
+       rgb_t &set_g(UINT8 g) { m_data &= ~0x0000ff00; m_data |= g <<  8; return *this; }
+       rgb_t &set_b(UINT8 b) { m_data &= ~0x000000ff; m_data |= b <<  0; return *this; }
+
+       // implicit conversion operators
+       operator UINT32() const { return m_data; }
+
+       // operations
+       rgb_t &scale8(UINT8 scale) { m_data = rgb_t(clamphi((a() * scale) >> 8), clamphi((r() * scale) >> 8), clamphi((g() * scale) >> 8), clamphi((b() * scale) >> 8)); return *this; }
+
+       // assignment operators
+       rgb_t &operator=(UINT32 rhs) { m_data = rhs; return *this; }
+       rgb_t &operator+=(const rgb_t &rhs) { m_data = rgb_t(clamphi(a() + rhs.a()), clamphi(r() + rhs.r()), clamphi(g() + rhs.g()), clamphi(b() + rhs.b())); return *this; }
+       rgb_t &operator-=(const rgb_t &rhs) { m_data = rgb_t(clamplo(a() - rhs.a()), clamplo(r() - rhs.r()), clamplo(g() - rhs.g()), clamplo(b() - rhs.b())); return *this; }
+
+       // arithmetic operators
+       const rgb_t operator+(const rgb_t &rhs) const { rgb_t result = *this; result += rhs; return result; }
+       const rgb_t operator-(const rgb_t &rhs) const { rgb_t result = *this; result -= rhs; return result; }
+
+       // static helpers
+       static UINT8 clamp(INT32 value) { return (value < 0) ? 0 : (value > 255) ? 255 : value; }
+       static UINT8 clamphi(INT32 value) { return (value > 255) ? 255 : value; }
+       static UINT8 clamplo(INT32 value) { return (value < 0) ? 0 : value; }
+
+       // constants
+       static const rgb_t black;
+       static const rgb_t white;
+
+private:
+       UINT32  m_data;
+};
+
+
+// ======================> palette_client
+
+// a single palette client
+class palette_client
+{
+public:
+       // construction/destruction
+       palette_client(palette_t &palette);
+       ~palette_client();
+
+       // getters
+       palette_client *next() const { return m_next; }
+       palette_t &palette() const { return m_palette; }
+       const UINT32 *dirty_list(UINT32 &mindirty, UINT32 &maxdirty);
+
+       // dirty marking
+       void mark_dirty(UINT32 index) { m_live->mark_dirty(index); }
+
+private:
+       // internal object to track dirty states
+       class dirty_state
+       {
+       public:
+               // construction
+               dirty_state();
+
+               // operations
+               const UINT32 *dirty_list(UINT32 &mindirty, UINT32 &maxdirty);
+               void resize(UINT32 colors);
+               void mark_dirty(UINT32 index);
+               void reset();
+
+       private:
+               // internal state
+               dynamic_array<UINT32> m_dirty;          // bitmap of dirty entries
+               UINT32          m_mindirty;             // minimum dirty entry
+               UINT32          m_maxdirty;             // minimum dirty entry
+       };
+
+       // internal state
+       palette_t &     m_palette;                  // reference to the palette
+       palette_client *m_next;                     // pointer to next client
+       dirty_state *   m_live;                     // live dirty state
+       dirty_state *   m_previous;                 // previous dirty state
+       dirty_state     m_dirty[2];                 // two dirty states
+};
+
+
+// ======================> palette_t
+
+// a palette object
+class palette_t
+{
+       friend class palette_client;
+
+public:
+       // static constructor: used to ensure same new/delete is used
+       static palette_t *alloc(UINT32 numcolors, UINT32 numgroups = 1);
+
+       // reference counting
+       void ref() { m_refcount++; }
+       void deref();
+
+       // getters
+       int num_colors() const { return m_numcolors; }
+       int num_groups() const { return m_numgroups; }
+       int max_index() const { return m_numcolors * m_numgroups + 2; }
+       UINT32 black_entry() const { return m_numcolors * m_numgroups + 0; }
+       UINT32 white_entry() const { return m_numcolors * m_numgroups + 1; }
+
+       // overall adjustments
+       void set_brightness(float brightness);
+       void set_contrast(float contrast);
+       void set_gamma(float gamma);
+
+       // entry getters
+       rgb_t entry_color(UINT32 index) const { return (index < m_numcolors) ? m_entry_color[index] : rgb_t::black; }
+       rgb_t entry_adjusted_color(UINT32 index) const { return (index < m_numcolors * m_numgroups) ? m_adjusted_color[index] : rgb_t::black; }
+       float entry_contrast(UINT32 index) const { return (index < m_numcolors) ? m_entry_contrast[index] : 1.0f; }
+
+       // entry setters
+       void entry_set_color(UINT32 index, rgb_t rgb);
+       void entry_set_contrast(UINT32 index, float contrast);
+
+       // entry list getters
+       const rgb_t *entry_list_raw() const { return m_entry_color; }
+       const rgb_t *entry_list_adjusted() const { return m_adjusted_color; }
+       const rgb_t *entry_list_adjusted_rgb15() const { return m_adjusted_rgb15; }
+
+       // group adjustments
+       void group_set_brightness(UINT32 group, float brightness);
+       void group_set_contrast(UINT32 group, float contrast);
+
+       // utilities
+       void normalize_range(UINT32 start, UINT32 end, int lum_min = 0, int lum_max = 255);
+
+private:
+       // construction/destruction
+       palette_t(UINT32 numcolors, UINT32 numgroups = 1);
+       ~palette_t();
+
+       // internal helpers
+       rgb_t adjust_palette_entry(rgb_t entry, float brightness, float contrast, const UINT8 *gamma_map);
+       void update_adjusted_color(UINT32 group, UINT32 index);
+
+       // internal state
+       UINT32          m_refcount;                   // reference count on the palette
+       UINT32          m_numcolors;                  // number of colors in the palette
+       UINT32          m_numgroups;                  // number of groups in the palette
+
+       float           m_brightness;                 // overall brightness value
+       float           m_contrast;                   // overall contrast value
+       float           m_gamma;                      // overall gamma value
+       UINT8           m_gamma_map[256];             // gamma map
+
+       dynamic_array<rgb_t> m_entry_color;           // array of raw colors
+       dynamic_array<float> m_entry_contrast;        // contrast value for each entry
+       dynamic_array<rgb_t> m_adjusted_color;        // array of adjusted colors
+       dynamic_array<rgb_t> m_adjusted_rgb15;        // array of adjusted colors as RGB15
+
+       dynamic_array<float> m_group_bright;          // brightness value for each group
+       dynamic_array<float> m_group_contrast;        // contrast value for each group
+
+       palette_client *m_client_list;                // list of clients for this palette
+};
+
+
+
+//**************************************************************************
+//  INLINE FUNCTIONS
+//**************************************************************************
+
+//-------------------------------------------------
+//  palexpand - expand a palette value to 8 bits
+//-------------------------------------------------
+
+template<int _NumBits>
+inline UINT8 palexpand(UINT8 bits)
+{
+       if (_NumBits == 1) { return (bits & 1) ? 0xff : 0x00; }
+       if (_NumBits == 2) { bits &= 3; return (bits << 6) | (bits << 4) | (bits << 2) | bits; }
+       if (_NumBits == 3) { bits &= 7; return (bits << 5) | (bits << 2) | (bits >> 1); }
+       if (_NumBits == 4) { bits &= 0xf; return (bits << 4) | bits; }
+       if (_NumBits == 5) { bits &= 0x1f; return (bits << 3) | (bits >> 2); }
+       if (_NumBits == 6) { bits &= 0x3f; return (bits << 2) | (bits >> 4); }
+       if (_NumBits == 7) { bits &= 0x7f; return (bits << 1) | (bits >> 6); }
+       return bits;
+}
+
+
+//-------------------------------------------------
+//  palxbit - convert an x-bit value to 8 bits
+//-------------------------------------------------
+
+inline UINT8 pal1bit(UINT8 bits) { return palexpand<1>(bits); }
+inline UINT8 pal2bit(UINT8 bits) { return palexpand<2>(bits); }
+inline UINT8 pal3bit(UINT8 bits) { return palexpand<3>(bits); }
+inline UINT8 pal4bit(UINT8 bits) { return palexpand<4>(bits); }
+inline UINT8 pal5bit(UINT8 bits) { return palexpand<5>(bits); }
+inline UINT8 pal6bit(UINT8 bits) { return palexpand<6>(bits); }
+inline UINT8 pal7bit(UINT8 bits) { return palexpand<7>(bits); }
+
+
+//-------------------------------------------------
+//  rgbexpand - expand a 32-bit raw data to 8-bit
+//  RGB
+//-------------------------------------------------
+
+template<int _RBits, int _GBits, int _BBits>
+inline rgb_t rgbexpand(UINT32 data, UINT8 rshift, UINT8 gshift, UINT8 bshift)
+{
+       return rgb_t(palexpand<_RBits>(data >> rshift), palexpand<_GBits>(data >> gshift), palexpand<_BBits>(data >> bshift));
+}
+
+
+//-------------------------------------------------
+//  palxxx - create an x-x-x color by extracting
+//  bits from a UINT32
+//-------------------------------------------------
+
+inline rgb_t pal332(UINT32 data, UINT8 rshift, UINT8 gshift, UINT8 bshift) { return rgbexpand<3,3,2>(data, rshift, gshift, bshift); }
+inline rgb_t pal444(UINT32 data, UINT8 rshift, UINT8 gshift, UINT8 bshift) { return rgbexpand<4,4,4>(data, rshift, gshift, bshift); }
+inline rgb_t pal555(UINT32 data, UINT8 rshift, UINT8 gshift, UINT8 bshift) { return rgbexpand<5,5,5>(data, rshift, gshift, bshift); }
+inline rgb_t pal565(UINT32 data, UINT8 rshift, UINT8 gshift, UINT8 bshift) { return rgbexpand<5,6,5>(data, rshift, gshift, bshift); }
+inline rgb_t pal888(UINT32 data, UINT8 rshift, UINT8 gshift, UINT8 bshift) { return rgbexpand<8,8,8>(data, rshift, gshift, bshift); }
+
+
+#endif  // __PALETTE_H__
index 5e2de8447e738130728769dc553d4137c8c3cd5e..266f3ed56ef36a6b6df51a0cc6c30b670ad8ba94 100644 (file)
@@ -1,3 +1,4 @@
+#include "chdtypes.h"
 /* sha1.h
  *
  * The sha1 hash function.
@@ -23,8 +24,6 @@
  * MA 02111-1307, USA.
  */
 
-#include "chdtypes.h"
-
 #include "sha1.h"
 
 #include <assert.h>
@@ -99,8 +98,8 @@ static void WRITE_UINT32(unsigned char* data, UINT32 val)
    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 ] ) ) )
+                               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:
@@ -117,25 +116,25 @@ static void WRITE_UINT32(unsigned char* data, UINT32 val)
    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 ) )
+       ( 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;
+       /* 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
@@ -148,156 +147,156 @@ sha1_init(struct sha1_ctx *ctx)
 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;
+       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;
+       UINT32 data[SHA1_DATA_LENGTH];
+       int i;
 
-  /* Update block count */
-  if (!++ctx->count_low)
-    ++ctx->count_high;
+       /* 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);
+       /* Endian independent conversion */
+       for (i = 0; i<SHA1_DATA_LENGTH; i++, block += 4)
+       data[i] = READ_UINT32(block);
 
-  sha1_transform(ctx->digest, data);
+       sha1_transform(ctx->digest, data);
 }
 
 void
 sha1_update(struct sha1_ctx *ctx,
-           unsigned length, const UINT8 *buffer)
+               unsigned length, const UINT8 *buffer)
 {
-  if (ctx->index)
-    { /* Try to fill partial block */
-      unsigned left = SHA1_DATA_SIZE - ctx->index;
-      if (length < left)
+       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 */
+               memcpy(ctx->block + ctx->index, buffer, length);
+               ctx->index += length;
+               return; /* Finished */
        }
-      else
+               else
        {
-         memcpy(ctx->block + ctx->index, buffer, left);
-         sha1_block(ctx, ctx->block);
-         buffer += left;
-         length -= left;
+               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);
+       }
+       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
@@ -306,84 +305,84 @@ sha1_update(struct sha1_ctx *ctx,
 void
 sha1_final(struct sha1_ctx *ctx)
 {
-  UINT32 data[SHA1_DATA_LENGTH];
-  int i;
-  int words;
+       UINT32 data[SHA1_DATA_LENGTH];
+       int i;
+       int words;
 
-  i = ctx->index;
+       i = ctx->index;
 
-  /* Set the first char of padding to 0x80.  This is safe since there is
-     always at least one byte free */
+       /* 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;
+       assert(i < SHA1_DATA_SIZE);
+       ctx->block[i++] = 0x80;
 
-  /* Fill rest of word */
-  for( ; i & 3; i++)
-    ctx->block[i] = 0;
+       /* 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);
+       /* 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
+       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++)
+               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++)
+               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);
+       }
+       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 length,
+               UINT8 *digest)
 {
-  unsigned i;
-  unsigned words;
-  unsigned leftover;
+       unsigned i;
+       unsigned words;
+       unsigned leftover;
 
-  assert(length <= SHA1_DIGEST_SIZE);
+       assert(length <= SHA1_DIGEST_SIZE);
 
-  words = length / 4;
-  leftover = length % 4;
+       words = length / 4;
+       leftover = length % 4;
 
-  for (i = 0; i < words; i++, digest += 4)
-    WRITE_UINT32(digest, ctx->digest[i]);
+       for (i = 0; i < words; i++, digest += 4)
+       WRITE_UINT32(digest, ctx->digest[i]);
 
-  if (leftover)
-    {
-      UINT32 word;
-      unsigned j = leftover;
+       if (leftover)
+       {
+               UINT32 word;
+               unsigned j = leftover;
 
-      assert(i < _SHA1_DIGEST_LENGTH);
+               assert(i < _SHA1_DIGEST_LENGTH);
 
-      word = ctx->digest[i];
+               word = ctx->digest[i];
 
-      switch (leftover)
+               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 */
+               digest[--j] = (word >> 8) & 0xff;
+               /* Fall through */
        case 2:
-         digest[--j] = (word >> 16) & 0xff;
-         /* Fall through */
+               digest[--j] = (word >> 16) & 0xff;
+               /* Fall through */
        case 1:
-         digest[--j] = (word >> 24) & 0xff;
+               digest[--j] = (word >> 24) & 0xff;
+       }
        }
-    }
 }
index 028f3303cb13c138180d545e485207ba2687df67..dcec92e453dcbf11ef6f669ef247087d00aac7ae 100644 (file)
 
 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 */
+       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
@@ -47,15 +47,15 @@ sha1_init(struct sha1_ctx *ctx);
 
 void
 sha1_update(struct sha1_ctx *ctx,
-           unsigned length,
-           const UINT8 *data);
+               unsigned length,
+               const UINT8 *data);
 
 void
 sha1_final(struct sha1_ctx *ctx);
 
 void
 sha1_digest(const struct sha1_ctx *ctx,
-           unsigned length,
-           UINT8 *digest);
+               unsigned length,
+               UINT8 *digest);
 
 #endif /* NETTLE_SHA1_H_INCLUDED */
diff --git a/archivers/chd/windows/eivc.h b/archivers/chd/windows/eivc.h
new file mode 100644 (file)
index 0000000..93927b1
--- /dev/null
@@ -0,0 +1,174 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+//  eivc.h
+//
+//  Inline implementations for MSVC compiler.
+//
+//============================================================
+
+#ifndef __EIVC__
+#define __EIVC__
+
+#if (_MSC_VER >= 1400)
+
+// need to ignore 'nonstandard extension used' warning in setjmp.h
+#pragma warning(push)
+#pragma warning(disable: 4987)
+#include <intrin.h>
+#pragma warning(pop)
+
+#else
+extern "C" long __cdecl _InterlockedIncrement(long volatile *);
+extern "C" long __cdecl _InterlockedDecrement(long volatile *);
+extern "C" long _InterlockedExchange(long volatile *, long);
+extern "C" long _InterlockedCompareExchange (long volatile *, long, long);
+extern "C" long _InterlockedExchangeAdd(long volatile *, long);
+extern "C" unsigned char _BitScanReverse(unsigned long *Index, unsigned long Mask);
+#endif
+
+#pragma intrinsic(_InterlockedIncrement)
+#pragma intrinsic(_InterlockedDecrement)
+#pragma intrinsic(_InterlockedCompareExchange)
+#pragma intrinsic(_InterlockedExchange)
+#pragma intrinsic(_InterlockedExchangeAdd)
+#if (_MSC_VER >= 1310)
+#pragma intrinsic(_BitScanReverse)
+#endif
+
+
+/***************************************************************************
+    INLINE BIT MANIPULATION FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    count_leading_zeros - return the number of
+    leading zero bits in a 32-bit value
+-------------------------------------------------*/
+
+#ifndef count_leading_zeros
+#define count_leading_zeros _count_leading_zeros
+INLINE UINT8 _count_leading_zeros(UINT32 value)
+{
+       UINT32 index;
+       return _BitScanReverse((unsigned long *)&index, value) ? (index ^ 31) : 32;
+}
+#endif
+
+
+/*-------------------------------------------------
+    count_leading_ones - return the number of
+    leading one bits in a 32-bit value
+-------------------------------------------------*/
+
+#ifndef count_leading_ones
+#define count_leading_ones _count_leading_ones
+INLINE UINT8 _count_leading_ones(UINT32 value)
+{
+       UINT32 index;
+       return _BitScanReverse((unsigned long *)&index, ~value) ? (index ^ 31) : 32;
+}
+#endif
+
+
+
+/***************************************************************************
+    INLINE SYNCHRONIZATION FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    compare_exchange32 - compare the 'compare'
+    value against the memory at 'ptr'; if equal,
+    swap in the 'exchange' value. Regardless,
+    return the previous value at 'ptr'.
+-------------------------------------------------*/
+
+#ifndef compare_exchange32
+#define compare_exchange32 _compare_exchange32
+INLINE INT32 _compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange)
+{
+       return _InterlockedCompareExchange((volatile long *)ptr, exchange, compare);
+}
+#endif
+
+
+/*-------------------------------------------------
+    compare_exchange64 - compare the 'compare'
+    value against the memory at 'ptr'; if equal,
+    swap in the 'exchange' value. Regardless,
+    return the previous value at 'ptr'.
+-------------------------------------------------*/
+
+#ifdef PTR64
+#ifndef compare_exchange64
+#define compare_exchange64 _compare_exchange64
+INLINE INT64 _compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange)
+{
+       return _InterlockedCompareExchange64(ptr, exchange, compare);
+}
+#endif
+#endif
+
+
+/*-------------------------------------------------
+    atomic_exchange32 - atomically exchange the
+    exchange value with the memory at 'ptr',
+    returning the original value.
+-------------------------------------------------*/
+
+#ifndef atomic_exchange32
+#define atomic_exchange32 _atomic_exchange32
+INLINE INT32 _atomic_exchange32(INT32 volatile *ptr, INT32 exchange)
+{
+       return _InterlockedExchange((volatile long *)ptr, exchange);
+}
+#endif
+
+
+/*-------------------------------------------------
+    atomic_add32 - atomically add the delta value
+    to the memory at 'ptr', returning the final
+    result.
+-------------------------------------------------*/
+
+#ifndef atomic_add32
+#define atomic_add32 _atomic_add32
+INLINE INT32 _atomic_add32(INT32 volatile *ptr, INT32 delta)
+{
+       return _InterlockedExchangeAdd((volatile long *)ptr, delta) + delta;
+}
+#endif
+
+
+/*-------------------------------------------------
+    atomic_increment32 - atomically increment the
+    32-bit value in memory at 'ptr', returning the
+    final result.
+-------------------------------------------------*/
+
+#ifndef atomic_increment32
+#define atomic_increment32 _atomic_increment32
+INLINE INT32 _atomic_increment32(INT32 volatile *ptr)
+{
+       return _InterlockedIncrement((volatile long *)ptr);
+}
+#endif
+
+
+/*-------------------------------------------------
+    atomic_decrement32 - atomically decrement the
+    32-bit value in memory at 'ptr', returning the
+    final result.
+-------------------------------------------------*/
+
+#ifndef atomic_decrement32
+#define atomic_decrement32 _atomic_decrement32
+INLINE INT32 _atomic_decrement32(INT32 volatile *ptr)
+{
+       return _InterlockedDecrement((volatile long *)ptr);
+}
+#endif
+
+
+#endif /* __EIVC__ */
diff --git a/archivers/chd/windows/eivcx86.h b/archivers/chd/windows/eivcx86.h
new file mode 100644 (file)
index 0000000..b834bc2
--- /dev/null
@@ -0,0 +1,504 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+//  eivcx86.h
+//
+//  x86 inline implementations for MSVC compiler.
+//
+//============================================================
+
+#ifndef __EIVCX86__
+#define __EIVCX86__
+
+#ifdef PTR64
+#include <emmintrin.h>
+#include <intrin.h>
+#endif
+
+
+/***************************************************************************
+    INLINE MATH FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    mul_32x32 - perform a signed 32 bit x 32 bit
+    multiply and return the full 64 bit result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mul_32x32 _mul_32x32
+INLINE INT64 _mul_32x32(INT32 a, INT32 b)
+{
+       // in theory this should work, but it is untested
+       __asm
+       {
+               mov   eax,a
+               imul  b
+               // leave results in edx:eax
+       }
+}
+#endif
+
+
+/*-------------------------------------------------
+    mulu_32x32 - perform an unsigned 32 bit x
+    32 bit multiply and return the full 64 bit
+    result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mulu_32x32 _mulu_32x32
+INLINE UINT64 _mulu_32x32(UINT32 a, UINT32 b)
+{
+       // in theory this should work, but it is untested
+       __asm
+       {
+               mov   eax,a
+               mul   b
+               // leave results in edx:eax
+       }
+}
+#endif
+
+
+/*-------------------------------------------------
+    mul_32x32_hi - perform a signed 32 bit x 32 bit
+    multiply and return the upper 32 bits of the
+    result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mul_32x32_hi _mul_32x32_hi
+INLINE INT32 _mul_32x32_hi(INT32 a, INT32 b)
+{
+       INT32 result;
+
+       __asm
+       {
+               mov   eax,a
+               imul  b
+               mov   result,edx
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    mulu_32x32_hi - perform an unsigned 32 bit x
+    32 bit multiply and return the upper 32 bits
+    of the result
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mulu_32x32_hi _mulu_32x32_hi
+INLINE UINT32 _mulu_32x32_hi(UINT32 a, UINT32 b)
+{
+       INT32 result;
+
+       __asm
+       {
+               mov   eax,a
+               mul   b
+               mov   result,edx
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    mul_32x32_shift - perform a signed 32 bit x
+    32 bit multiply and shift the result by the
+    given number of bits before truncating the
+    result to 32 bits
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mul_32x32_shift _mul_32x32_shift
+INLINE INT32 _mul_32x32_shift(INT32 a, INT32 b, UINT8 shift)
+{
+       INT32 result;
+
+       __asm
+       {
+               mov   eax,a
+               imul  b
+               mov   cl,shift
+               shrd  eax,edx,cl
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    mulu_32x32_shift - perform an unsigned 32 bit x
+    32 bit multiply and shift the result by the
+    given number of bits before truncating the
+    result to 32 bits
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mulu_32x32_shift _mulu_32x32_shift
+INLINE UINT32 _mulu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift)
+{
+       INT32 result;
+
+       __asm
+       {
+               mov   eax,a
+               mul   b
+               mov   cl,shift
+               shrd  eax,edx,cl
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    div_64x32 - perform a signed 64 bit x 32 bit
+    divide and return the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define div_64x32 _div_64x32
+INLINE INT32 _div_64x32(INT64 a, INT32 b)
+{
+       INT32 result;
+       INT32 alow = a;
+       INT32 ahigh = a >> 32;
+
+       __asm
+       {
+               mov   eax,alow
+               mov   edx,ahigh
+               idiv  b
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    divu_64x32 - perform an unsigned 64 bit x 32 bit
+    divide and return the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define divu_64x32 _divu_64x32
+INLINE UINT32 _divu_64x32(UINT64 a, UINT32 b)
+{
+       UINT32 result;
+       UINT32 alow = a;
+       UINT32 ahigh = a >> 32;
+
+       __asm
+       {
+               mov   eax,alow
+               mov   edx,ahigh
+               div   b
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    div_64x32_rem - perform a signed 64 bit x 32
+    bit divide and return the 32 bit quotient and
+    32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define div_64x32_rem _div_64x32_rem
+INLINE INT32 _div_64x32_rem(INT64 a, INT32 b, INT32 *remainder)
+{
+       INT32 result;
+       INT32 alow = a;
+       INT32 ahigh = a >> 32;
+       INT32 rem;
+
+       __asm
+       {
+               mov   eax,alow
+               mov   edx,ahigh
+               idiv  b
+               mov   result,eax
+               mov   rem,edx
+       }
+
+       *remainder = rem;
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    divu_64x32_rem - perform an unsigned 64 bit x
+    32 bit divide and return the 32 bit quotient
+    and 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define divu_64x32_rem _divu_64x32_rem
+INLINE UINT32 _divu_64x32_rem(UINT64 a, UINT32 b, UINT32 *remainder)
+{
+       UINT32 result;
+       UINT32 alow = a;
+       UINT32 ahigh = a >> 32;
+       UINT32 rem;
+
+       __asm
+       {
+               mov   eax,alow
+               mov   edx,ahigh
+               div   b
+               mov   result,eax
+               mov   rem,edx
+       }
+
+       *remainder = rem;
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    div_32x32_shift - perform a signed divide of
+    two 32 bit values, shifting the first before
+    division, and returning the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define div_32x32_shift _div_32x32_shift
+INLINE INT32 _div_32x32_shift(INT32 a, INT32 b, UINT8 shift)
+{
+       INT32 result;
+
+       __asm
+       {
+               mov   eax,a
+               cdq
+               mov   cl,shift
+               shld  edx,eax,cl
+               shl   eax,cl
+               idiv  b
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    divu_32x32_shift - perform an unsigned divide of
+    two 32 bit values, shifting the first before
+    division, and returning the 32 bit quotient
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define divu_32x32_shift _divu_32x32_shift
+INLINE UINT32 _divu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift)
+{
+       UINT32 result;
+
+       __asm
+       {
+               mov   eax,a
+               xor   edx,edx
+               mov   cl,shift
+               shld  edx,eax,cl
+               shl   eax,cl
+               div   b
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    mod_64x32 - perform a signed 64 bit x 32 bit
+    divide and return the 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define mod_64x32 _mod_64x32
+INLINE INT32 _mod_64x32(INT64 a, INT32 b)
+{
+       INT32 result;
+       INT32 alow = a;
+       INT32 ahigh = a >> 32;
+
+       __asm
+       {
+               mov   eax,alow
+               mov   edx,ahigh
+               idiv  b
+               mov   result,edx
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    modu_64x32 - perform an unsigned 64 bit x 32 bit
+    divide and return the 32 bit remainder
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define modu_64x32 _modu_64x32
+INLINE UINT32 _modu_64x32(UINT64 a, UINT32 b)
+{
+       UINT32 result;
+       UINT32 alow = a;
+       UINT32 ahigh = a >> 32;
+
+       __asm
+       {
+               mov   eax,alow
+               mov   edx,ahigh
+               div   b
+               mov   result,edx
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    recip_approx - compute an approximate floating
+    point reciprocal
+-------------------------------------------------*/
+
+#ifdef PTR64
+#define recip_approx _recip_approx
+INLINE float _recip_approx(float z)
+{
+       __m128 mz = _mm_set_ss(z);
+       __m128 mooz = _mm_rcp_ss(mz);
+       float ooz;
+       _mm_store_ss(&ooz, mooz);
+       return ooz;
+}
+#endif
+
+
+
+/***************************************************************************
+    INLINE BIT MANIPULATION FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    count_leading_zeros - return the number of
+    leading zero bits in a 32-bit value
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define count_leading_zeros _count_leading_zeros
+INLINE UINT8 _count_leading_zeros(UINT32 value)
+{
+       INT32 result;
+
+       __asm
+       {
+               bsr   eax,value
+               jnz   skip
+               mov   eax,63
+       skip:
+               xor   eax,31
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+/*-------------------------------------------------
+    count_leading_ones - return the number of
+    leading one bits in a 32-bit value
+-------------------------------------------------*/
+
+#ifndef PTR64
+#define count_leading_ones _count_leading_ones
+INLINE UINT8 _count_leading_ones(UINT32 value)
+{
+       INT32 result;
+
+       __asm
+       {
+               mov   eax,value
+               not   eax
+               bsr   eax,eax
+               jnz   skip
+               mov   eax,63
+       skip:
+               xor   eax,31
+               mov   result,eax
+       }
+
+       return result;
+}
+#endif
+
+
+
+/***************************************************************************
+    INLINE TIMING FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+    get_profile_ticks - return a tick counter
+    from the processor that can be used for
+    profiling. It does not need to run at any
+    particular rate.
+-------------------------------------------------*/
+
+#define get_profile_ticks _get_profile_ticks
+
+#ifdef PTR64
+
+INLINE osd_ticks_t _get_profile_ticks(void)
+{
+       return __rdtsc();
+}
+
+#else
+
+INLINE osd_ticks_t _get_profile_ticks(void)
+{
+       UINT64 result;
+       UINT64 *presult = &result;
+
+       __asm {
+               __asm _emit 0Fh __asm _emit 031h    // rdtsc
+               mov ebx, presult
+               mov [ebx],eax
+               mov [ebx+4],edx
+       }
+
+       return result;
+}
+
+#endif
+
+#endif /* __EIVCX86__ */
diff --git a/archivers/chd/windows/osinline.h b/archivers/chd/windows/osinline.h
new file mode 100644 (file)
index 0000000..a84da48
--- /dev/null
@@ -0,0 +1,53 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+//  osinline.h
+//
+//  Inline implementations for non-GCC Win32 compilers
+//
+//============================================================
+
+#ifndef __OSINLINE__
+#define __OSINLINE__
+
+#if 0
+#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+#include "eivcx86.h"
+#endif
+#endif
+
+#if defined(_MSC_VER)
+#include "eivc.h"
+#endif
+
+INT32 win_compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange);
+INT32 win_atomic_exchange32(INT32 volatile *ptr, INT32 exchange);
+INT32 win_atomic_add32(INT32 volatile *ptr, INT32 delta);
+
+#ifdef PTR64
+INT64 win_compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange);
+#endif
+
+
+#ifndef compare_exchange32
+#define compare_exchange32 win_compare_exchange32
+#endif /* compare_exchange32 */
+
+#ifdef PTR64
+#ifndef compare_exchange64
+#define compare_exchange64 win_compare_exchange64
+#endif /* compare_exchange64 */
+#endif
+
+#ifndef atomic_exchange32
+#define atomic_exchange32 win_atomic_exchange32
+#endif /* atomic_exchange32 */
+
+
+#ifndef atomic_add32
+#define atomic_add32 win_atomic_add32
+#endif /* atomic_add32 */
+
+
+#endif /* __OSINLINE__ */
diff --git a/archivers/chd/windows/wintime.cpp b/archivers/chd/windows/wintime.cpp
new file mode 100644 (file)
index 0000000..b1b88e0
--- /dev/null
@@ -0,0 +1,111 @@
+#include "../chdtypes.h"
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+//  wintime.c - Win32 OSD core timing functions
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+
+// MAME headers
+#include "../osdcore.h"
+
+
+
+//============================================================
+//  GLOBAL VARIABLES
+//============================================================
+
+static osd_ticks_t ticks_per_second = 0;
+static osd_ticks_t suspend_ticks = 0;
+static BOOL using_qpc = TRUE;
+
+
+
+//============================================================
+//  osd_ticks
+//============================================================
+
+osd_ticks_t osd_ticks(void)
+{
+       LARGE_INTEGER performance_count;
+
+       // if we're suspended, just return that
+       if (suspend_ticks != 0)
+               return suspend_ticks;
+
+       // if we have a per second count, just go for it
+       if (ticks_per_second != 0)
+       {
+               // QueryPerformanceCounter if we can
+               if (using_qpc)
+               {
+                       QueryPerformanceCounter(&performance_count);
+                       return (osd_ticks_t)performance_count.QuadPart - suspend_ticks;
+               }
+
+               // otherwise, fall back to timeGetTime
+               else
+                       return (osd_ticks_t)timeGetTime() - suspend_ticks;
+       }
+
+       // if not, we have to determine it
+       using_qpc = QueryPerformanceFrequency(&performance_count) && (performance_count.QuadPart != 0);
+       if (using_qpc)
+               ticks_per_second = (osd_ticks_t)performance_count.QuadPart;
+       else
+               ticks_per_second = 1000;
+
+       // call ourselves to get the first value
+       return osd_ticks();
+}
+
+
+//============================================================
+//  osd_ticks_per_second
+//============================================================
+
+osd_ticks_t osd_ticks_per_second(void)
+{
+       if (ticks_per_second == 0)
+               osd_ticks();
+       return ticks_per_second;
+}
+
+
+//============================================================
+//  osd_sleep
+//============================================================
+
+void osd_sleep(osd_ticks_t duration)
+{
+       DWORD msec;
+
+       // make sure we've computed ticks_per_second
+       if (ticks_per_second == 0)
+               (void)osd_ticks();
+
+       // convert to milliseconds, rounding down
+       msec = (DWORD)(duration * 1000 / ticks_per_second);
+
+       // only sleep if at least 2 full milliseconds
+       if (msec >= 2)
+       {
+               HANDLE current_thread = GetCurrentThread();
+               int old_priority = GetThreadPriority(current_thread);
+
+               // take a couple of msecs off the top for good measure
+               msec -= 2;
+
+               // bump our thread priority super high so that we get
+               // priority when we need it
+               SetThreadPriority(current_thread, THREAD_PRIORITY_TIME_CRITICAL);
+               Sleep(msec);
+               SetThreadPriority(current_thread, old_priority);
+       }
+}
diff --git a/archivers/chd/windows/winwork.cpp b/archivers/chd/windows/winwork.cpp
new file mode 100644 (file)
index 0000000..adf46fb
--- /dev/null
@@ -0,0 +1,785 @@
+#include "../chdtypes.h"
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+//============================================================
+//
+//  winwork.c - Win32 OSD core work item functions
+//
+//============================================================
+
+// standard windows headers
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <process.h>
+#include <tchar.h>
+#include <stdlib.h>
+
+#ifdef __GNUC__
+#include <stdint.h>
+#endif
+
+// MAME headers
+#include "../osdcore.h"
+#include "../eminline.h"
+
+
+//============================================================
+//  DEBUGGING
+//============================================================
+
+#define KEEP_STATISTICS         (0)
+#define USE_SCALABLE_LOCKS      (0)
+
+
+
+//============================================================
+//  PARAMETERS
+//============================================================
+
+#define SPIN_LOOP_TIME          (osd_ticks_per_second() / 1000)
+
+
+
+//============================================================
+//  MACROS
+//============================================================
+
+#if KEEP_STATISTICS
+#define add_to_stat(v,x)        do { atomic_add32((v), (x)); } while (0)
+#define begin_timing(v)         do { (v) -= get_profile_ticks(); } while (0)
+#define end_timing(v)           do { (v) += get_profile_ticks(); } while (0)
+#else
+#define add_to_stat(v,x)        do { } while (0)
+#define begin_timing(v)         do { } while (0)
+#define end_timing(v)           do { } while (0)
+#endif
+
+#if __GNUC__ && defined(__i386__) && !defined(__x86_64)
+#undef YieldProcessor
+#endif
+
+#ifndef YieldProcessor
+#ifdef __GNUC__
+INLINE void YieldProcessor(void)
+{
+       __asm__ __volatile__ ( "rep; nop" );
+}
+#else
+INLINE void YieldProcessor(void)
+{
+       __asm { rep nop }
+}
+#endif
+#endif
+
+
+
+//============================================================
+//  TYPE DEFINITIONS
+//============================================================
+
+struct scalable_lock
+{
+#if USE_SCALABLE_LOCKS
+       struct
+       {
+               volatile INT32  haslock;        // do we have the lock?
+               INT32           filler[64/4-1]; // assumes a 64-byte cache line
+       } slot[WORK_MAX_THREADS];           // one slot per thread
+       volatile INT32      nextindex;      // index of next slot to use
+#else
+       CRITICAL_SECTION    section;
+#endif
+};
+
+
+struct work_thread_info
+{
+       osd_work_queue *    queue;          // pointer back to the queue
+       HANDLE              handle;         // handle to the thread
+       HANDLE              wakeevent;      // wake event for the thread
+       volatile INT32      active;         // are we actively processing work?
+
+#if KEEP_STATISTICS
+       INT32               itemsdone;
+       osd_ticks_t         actruntime;
+       osd_ticks_t         runtime;
+       osd_ticks_t         spintime;
+       osd_ticks_t         waittime;
+#endif
+};
+
+
+struct osd_work_queue
+{
+       scalable_lock       lock;           // lock for protecting the queue
+       osd_work_item * volatile list;      // list of items in the queue
+       osd_work_item ** volatile tailptr;  // pointer to the tail pointer of work items in the queue
+       osd_work_item * volatile free;      // free list of work items
+       volatile INT32      items;          // items in the queue
+       volatile INT32      livethreads;    // number of live threads
+       volatile INT32      waiting;        // is someone waiting on the queue to complete?
+       volatile UINT8      exiting;        // should the threads exit on their next opportunity?
+       UINT32              threads;        // number of threads in this queue
+       UINT32              flags;          // creation flags
+       work_thread_info *  thread;         // array of thread information
+       HANDLE              doneevent;      // event signalled when work is complete
+
+#if KEEP_STATISTICS
+       volatile INT32      itemsqueued;    // total items queued
+       volatile INT32      setevents;      // number of times we called SetEvent
+       volatile INT32      extraitems;     // how many extra items we got after the first in the queue loop
+       volatile INT32      spinloops;      // how many times spinning bought us more items
+#endif
+};
+
+
+struct osd_work_item
+{
+       osd_work_item *     next;           // pointer to next item
+       osd_work_queue *    queue;          // pointer back to the owning queue
+       osd_work_callback   callback;       // callback function
+       void *              param;          // callback parameter
+       void *              result;         // callback result
+       HANDLE              event;          // event signalled when complete
+       UINT32              flags;          // creation flags
+       volatile INT32      done;           // is the item done?
+};
+
+//============================================================
+//  GLOBAL VARIABLES
+//============================================================
+
+int osd_num_processors = 0;
+
+//============================================================
+//  FUNCTION PROTOTYPES
+//============================================================
+
+static int effective_num_processors(void);
+static unsigned __stdcall worker_thread_entry(void *param);
+static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread);
+
+
+
+//============================================================
+//  Scalable Locks
+//============================================================
+
+INLINE void scalable_lock_init(scalable_lock *lock)
+{
+       memset(lock, 0, sizeof(*lock));
+#if USE_SCALABLE_LOCKS
+       lock->slot[0].haslock = TRUE;
+#else
+       InitializeCriticalSection(&lock->section);
+#endif
+}
+
+
+INLINE INT32 scalable_lock_acquire(scalable_lock *lock)
+{
+#if USE_SCALABLE_LOCKS
+       INT32 myslot = (atomic_increment32(&lock->nextindex) - 1) & (WORK_MAX_THREADS - 1);
+       INT32 backoff = 1;
+
+       while (!lock->slot[myslot].haslock)
+       {
+               INT32 backcount;
+               for (backcount = 0; backcount < backoff; backcount++)
+                       YieldProcessor();
+               backoff <<= 1;
+       }
+       lock->slot[myslot].haslock = FALSE;
+       return myslot;
+#else
+       EnterCriticalSection(&lock->section);
+       return 0;
+#endif
+}
+
+
+INLINE void scalable_lock_release(scalable_lock *lock, INT32 myslot)
+{
+#if USE_SCALABLE_LOCKS
+       atomic_exchange32(&lock->slot[(myslot + 1) & (WORK_MAX_THREADS - 1)].haslock, TRUE);
+#else
+       LeaveCriticalSection(&lock->section);
+#endif
+}
+
+
+INLINE void scalable_lock_delete(scalable_lock *lock)
+{
+#if USE_SCALABLE_LOCKS
+#else
+       DeleteCriticalSection(&lock->section);
+#endif
+}
+
+
+//============================================================
+//  osd_work_queue_alloc
+//============================================================
+
+osd_work_queue *osd_work_queue_alloc(int flags)
+{
+       int numprocs = effective_num_processors();
+       osd_work_queue *queue;
+       int threadnum;
+       TCHAR *osdworkqueuemaxthreads = _tgetenv(_T("OSDWORKQUEUEMAXTHREADS"));
+
+       // allocate a new queue
+       queue = (osd_work_queue *)malloc(sizeof(*queue));
+       if (queue == NULL)
+               goto error;
+       memset(queue, 0, sizeof(*queue));
+
+       // initialize basic queue members
+       queue->tailptr = (osd_work_item **)&queue->list;
+       queue->flags = flags;
+
+       // allocate events for the queue
+       queue->doneevent = CreateEvent(NULL, TRUE, TRUE, NULL);     // manual reset, signalled
+       if (queue->doneevent == NULL)
+               goto error;
+
+       // initialize the critical section
+       scalable_lock_init(&queue->lock);
+
+       // determine how many threads to create...
+       // on a single-CPU system, create 1 thread for I/O queues, and 0 threads for everything else
+       if (numprocs == 1)
+               queue->threads = (flags & WORK_QUEUE_FLAG_IO) ? 1 : 0;
+
+       // on an n-CPU system, create n threads for multi queues, and 1 thread for everything else
+       else
+               queue->threads = (flags & WORK_QUEUE_FLAG_MULTI) ? numprocs : 1;
+
+       if (osdworkqueuemaxthreads != NULL && _stscanf(osdworkqueuemaxthreads, _T("%d"), &threadnum) == 1 && queue->threads > threadnum)
+               queue->threads = threadnum;
+
+       // multi-queues with high frequency items should top out at 4 for now
+       // since we have scaling problems above that
+       if ((flags & WORK_QUEUE_FLAG_HIGH_FREQ) && queue->threads > 1)
+               queue->threads = MIN(queue->threads - 1, 4);
+
+       // clamp to the maximum
+       queue->threads = MIN(queue->threads, WORK_MAX_THREADS);
+
+       // allocate memory for thread array (+1 to count the calling thread)
+       queue->thread = (work_thread_info *)malloc((queue->threads + 1) * sizeof(queue->thread[0]));
+       if (queue->thread == NULL)
+               goto error;
+       memset(queue->thread, 0, (queue->threads + 1) * sizeof(queue->thread[0]));
+
+       // iterate over threads
+       for (threadnum = 0; threadnum < queue->threads; threadnum++)
+       {
+               work_thread_info *thread = &queue->thread[threadnum];
+               uintptr_t handle;
+
+               // set a pointer back to the queue
+               thread->queue = queue;
+
+               // create the per-thread wake event
+               thread->wakeevent = CreateEvent(NULL, FALSE, FALSE, NULL);  // auto-reset, not signalled
+               if (thread->wakeevent == NULL)
+                       goto error;
+
+               // create the thread
+               handle = _beginthreadex(NULL, 0, worker_thread_entry, thread, 0, NULL);
+               thread->handle = (HANDLE)handle;
+               if (thread->handle == NULL)
+                       goto error;
+
+               // set its priority: I/O threads get high priority because they are assumed to be
+               // blocked most of the time; other threads just match the creator's priority
+               if (flags & WORK_QUEUE_FLAG_IO)
+                       SetThreadPriority(thread->handle, THREAD_PRIORITY_ABOVE_NORMAL);
+               else
+                       SetThreadPriority(thread->handle, GetThreadPriority(GetCurrentThread()));
+       }
+
+       // start a timer going for "waittime" on the main thread
+       begin_timing(queue->thread[queue->threads].waittime);
+       return queue;
+
+error:
+       osd_work_queue_free(queue);
+       return NULL;
+}
+
+
+//============================================================
+//  osd_work_queue_items
+//============================================================
+
+int osd_work_queue_items(osd_work_queue *queue)
+{
+       // return the number of items currently in the queue
+       return queue->items;
+}
+
+
+//============================================================
+//  osd_work_queue_wait
+//============================================================
+
+int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout)
+{
+       // if no threads, no waiting
+       if (queue->threads == 0)
+               return TRUE;
+
+       // if no items, we're done
+       if (queue->items == 0)
+               return TRUE;
+
+       // if this is a multi queue, help out rather than doing nothing
+       if (queue->flags & WORK_QUEUE_FLAG_MULTI)
+       {
+               work_thread_info *thread = &queue->thread[queue->threads];
+               osd_ticks_t stopspin = osd_ticks() + timeout;
+
+               end_timing(thread->waittime);
+
+               // process what we can as a worker thread
+               worker_thread_process(queue, thread);
+
+               // if we're a high frequency queue, spin until done
+               if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ)
+               {
+                       // spin until we're done
+                       begin_timing(thread->spintime);
+                       while (queue->items != 0 && osd_ticks() < stopspin)
+                               YieldProcessor();
+                       end_timing(thread->spintime);
+
+                       begin_timing(thread->waittime);
+                       return (queue->items == 0);
+               }
+               begin_timing(thread->waittime);
+       }
+
+       // reset our done event and double-check the items before waiting
+       ResetEvent(queue->doneevent);
+       atomic_exchange32(&queue->waiting, TRUE);
+       if (queue->items != 0)
+               WaitForSingleObject(queue->doneevent, timeout * 1000 / osd_ticks_per_second());
+       atomic_exchange32(&queue->waiting, FALSE);
+
+       // return TRUE if we actually hit 0
+       return (queue->items == 0);
+}
+
+
+//============================================================
+//  osd_work_queue_free
+//============================================================
+
+void osd_work_queue_free(osd_work_queue *queue)
+{
+       // if we have threads, clean them up
+       if (queue->threads > 0 && queue->thread != NULL)
+       {
+               int threadnum;
+
+               // stop the timer for "waittime" on the main thread
+               end_timing(queue->thread[queue->threads].waittime);
+
+               // signal all the threads to exit
+               queue->exiting = TRUE;
+               for (threadnum = 0; threadnum < queue->threads; threadnum++)
+               {
+                       work_thread_info *thread = &queue->thread[threadnum];
+                       if (thread->wakeevent != NULL)
+                               SetEvent(thread->wakeevent);
+               }
+
+               // wait for all the threads to go away
+               for (threadnum = 0; threadnum < queue->threads; threadnum++)
+               {
+                       work_thread_info *thread = &queue->thread[threadnum];
+
+                       // block on the thread going away, then close the handle
+                       if (thread->handle != NULL)
+                       {
+                               WaitForSingleObject(thread->handle, INFINITE);
+                               CloseHandle(thread->handle);
+                       }
+
+                       // clean up the wake event
+                       if (thread->wakeevent != NULL)
+                               CloseHandle(thread->wakeevent);
+               }
+
+#if KEEP_STATISTICS
+               // output per-thread statistics
+               for (threadnum = 0; threadnum <= queue->threads; threadnum++)
+               {
+                       work_thread_info *thread = &queue->thread[threadnum];
+                       osd_ticks_t total = thread->runtime + thread->waittime + thread->spintime;
+                       printf("Thread %d:  items=%9d  run=%5.2f%% (%5.2f%%)  spin=%5.2f%%  wait/other=%5.2f%%\n",
+                                       threadnum, thread->itemsdone,
+                                       (double)thread->runtime * 100.0 / (double)total,
+                                       (double)thread->actruntime * 100.0 / (double)total,
+                                       (double)thread->spintime * 100.0 / (double)total,
+                                       (double)thread->waittime * 100.0 / (double)total);
+               }
+#endif
+       }
+
+       // free the list
+       if (queue->thread != NULL)
+               free(queue->thread);
+
+       scalable_lock_delete(&queue->lock);
+
+       // free all the events
+       if (queue->doneevent != NULL)
+               CloseHandle(queue->doneevent);
+
+       // free all items in the free list
+       while (queue->free != NULL)
+       {
+               osd_work_item *item = (osd_work_item *)queue->free;
+               queue->free = item->next;
+               if (item->event != NULL)
+                       CloseHandle(item->event);
+               free(item);
+       }
+
+       // free all items in the active list
+       while (queue->list != NULL)
+       {
+               osd_work_item *item = (osd_work_item *)queue->list;
+               queue->list = item->next;
+               if (item->event != NULL)
+                       CloseHandle(item->event);
+               free(item);
+       }
+
+#if KEEP_STATISTICS
+       printf("Items queued   = %9d\n", queue->itemsqueued);
+       printf("SetEvent calls = %9d\n", queue->setevents);
+       printf("Extra items    = %9d\n", queue->extraitems);
+       printf("Spin loops     = %9d\n", queue->spinloops);
+#endif
+
+       // free the queue itself
+       free(queue);
+}
+
+
+//============================================================
+//  osd_work_item_queue_multiple
+//============================================================
+
+osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags)
+{
+       osd_work_item *itemlist = NULL, *lastitem = NULL;
+       osd_work_item **item_tailptr = &itemlist;
+       INT32 lockslot;
+       int itemnum;
+
+       // loop over items, building up a local list of work
+       for (itemnum = 0; itemnum < numitems; itemnum++)
+       {
+               osd_work_item *item;
+
+               // first allocate a new work item; try the free list first
+               do
+               {
+                       item = (osd_work_item *)queue->free;
+               } while (item != NULL && compare_exchange_ptr((PVOID volatile *)&queue->free, item, item->next) != item);
+
+               // if nothing, allocate something new
+               if (item == NULL)
+               {
+                       // allocate the item
+                       item = (osd_work_item *)malloc(sizeof(*item));
+                       if (item == NULL)
+                               return NULL;
+                       item->event = NULL;
+                       item->queue = queue;
+               }
+
+               // fill in the basics
+               item->next = NULL;
+               item->callback = callback;
+               item->param = parambase;
+               item->result = NULL;
+               item->flags = flags;
+               item->done = FALSE;
+
+               // advance to the next
+               lastitem = item;
+               *item_tailptr = item;
+               item_tailptr = &item->next;
+               parambase = (UINT8 *)parambase + paramstep;
+       }
+
+       // enqueue the whole thing within the critical section
+       lockslot = scalable_lock_acquire(&queue->lock);
+       *queue->tailptr = itemlist;
+       queue->tailptr = item_tailptr;
+       scalable_lock_release(&queue->lock, lockslot);
+
+       // increment the number of items in the queue
+       atomic_add32(&queue->items, numitems);
+       add_to_stat(&queue->itemsqueued, numitems);
+
+       // look for free threads to do the work
+       if (queue->livethreads < queue->threads)
+       {
+               int threadnum;
+
+               // iterate over all the threads
+               for (threadnum = 0; threadnum < queue->threads; threadnum++)
+               {
+                       work_thread_info *thread = &queue->thread[threadnum];
+
+                       // if this thread is not active, wake him up
+                       if (!thread->active)
+                       {
+                               SetEvent(thread->wakeevent);
+                               add_to_stat(&queue->setevents, 1);
+
+                               // for non-shared, the first one we find is good enough
+                               if (--numitems == 0)
+                                       break;
+                       }
+               }
+       }
+
+       // if no threads, run the queue now on this thread
+       if (queue->threads == 0)
+               worker_thread_process(queue, &queue->thread[0]);
+
+       // only return the item if it won't get released automatically
+       return (flags & WORK_ITEM_FLAG_AUTO_RELEASE) ? NULL : lastitem;
+}
+
+
+//============================================================
+//  osd_work_item_wait
+//============================================================
+
+int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout)
+{
+       // if we're done already, just return
+       if (item->done)
+               return TRUE;
+
+       // if we don't have an event, create one
+       if (item->event == NULL)
+               item->event = CreateEvent(NULL, TRUE, FALSE, NULL);     // manual reset, not signalled
+       else
+               ResetEvent(item->event);
+
+       // if we don't have an event, we need to spin (shouldn't ever really happen)
+       if (item->event == NULL)
+       {
+               osd_ticks_t stopspin = osd_ticks() + timeout;
+               while (!item->done && osd_ticks() < stopspin)
+                       YieldProcessor();
+       }
+
+       // otherwise, block on the event until done
+       else if (!item->done)
+               WaitForSingleObject(item->event, timeout * 1000 / osd_ticks_per_second());
+
+       // return TRUE if the refcount actually hit 0
+       return item->done;
+}
+
+
+//============================================================
+//  osd_work_item_result
+//============================================================
+
+void *osd_work_item_result(osd_work_item *item)
+{
+       return item->result;
+}
+
+
+//============================================================
+//  osd_work_item_release
+//============================================================
+
+void osd_work_item_release(osd_work_item *item)
+{
+       osd_work_item *next;
+
+       // make sure we're done first
+       osd_work_item_wait(item, 100 * osd_ticks_per_second());
+
+       // add us to the free list on our queue
+       do
+       {
+               next = (osd_work_item *)item->queue->free;
+               item->next = next;
+       } while (compare_exchange_ptr((PVOID volatile *)&item->queue->free, next, item) != next);
+}
+
+
+//============================================================
+//  effective_num_processors
+//============================================================
+
+static int effective_num_processors(void)
+{
+       SYSTEM_INFO info;
+       // fetch the info from the system
+       GetSystemInfo(&info);
+
+       if (osd_num_processors > 0)
+       {
+               return MIN(info.dwNumberOfProcessors * 4, osd_num_processors);
+       }
+       else
+       {
+               TCHAR *procsoverride;
+               int numprocs = 0;
+
+               // if the OSDPROCESSORS environment variable is set, use that value if valid
+               // note that we permit more than the real number of processors for testing
+               procsoverride = _tgetenv(_T("OSDPROCESSORS"));
+               if (procsoverride != NULL && _stscanf(procsoverride, _T("%d"), &numprocs) == 1 && numprocs > 0)
+                       return MIN(info.dwNumberOfProcessors * 4, numprocs);
+
+               return info.dwNumberOfProcessors;
+       }
+}
+
+
+//============================================================
+//  worker_thread_entry
+//============================================================
+
+static unsigned __stdcall worker_thread_entry(void *param)
+{
+       work_thread_info *thread = (work_thread_info *)param;
+       osd_work_queue *queue = thread->queue;
+
+       // loop until we exit
+       for ( ;; )
+       {
+               // bail on exit, and only wait if there are no pending items in queue
+               if (!queue->exiting && queue->list == NULL)
+               {
+                       begin_timing(thread->waittime);
+                       WaitForSingleObject(thread->wakeevent, INFINITE);
+                       end_timing(thread->waittime);
+               }
+               if (queue->exiting)
+                       break;
+
+               // indicate that we are live
+               atomic_exchange32(&thread->active, TRUE);
+               atomic_increment32(&queue->livethreads);
+
+               // process work items
+               for ( ;; )
+               {
+                       osd_ticks_t stopspin;
+
+                       // process as much as we can
+                       worker_thread_process(queue, thread);
+
+                       // if we're a high frequency queue, spin for a while before giving up
+                       if (queue->flags & WORK_QUEUE_FLAG_HIGH_FREQ)
+                       {
+                               // spin for a while looking for more work
+                               begin_timing(thread->spintime);
+                               stopspin = osd_ticks() + SPIN_LOOP_TIME;
+                               while (queue->list == NULL && osd_ticks() < stopspin)
+                                       YieldProcessor();
+                               end_timing(thread->spintime);
+                       }
+
+                       // if nothing more, release the processor
+                       if (queue->list == NULL)
+                               break;
+                       add_to_stat(&queue->spinloops, 1);
+               }
+
+               // decrement the live thread count
+               atomic_exchange32(&thread->active, FALSE);
+               atomic_decrement32(&queue->livethreads);
+       }
+       return 0;
+}
+
+
+//============================================================
+//  worker_thread_process
+//============================================================
+
+static void worker_thread_process(osd_work_queue *queue, work_thread_info *thread)
+{
+       int threadid = thread - queue->thread;
+
+       begin_timing(thread->runtime);
+
+       // loop until everything is processed
+       while (queue->list != NULL)
+       {
+               osd_work_item *item;
+               INT32 lockslot;
+
+               // use a critical section to synchronize the removal of items
+               lockslot = scalable_lock_acquire(&queue->lock);
+               {
+                       // pull the item from the queue
+                       item = (osd_work_item *)queue->list;
+                       if (item != NULL)
+                       {
+                               queue->list = item->next;
+                               if (queue->list == NULL)
+                                       queue->tailptr = (osd_work_item **)&queue->list;
+                       }
+               }
+               scalable_lock_release(&queue->lock, lockslot);
+
+               // process non-NULL items
+               if (item != NULL)
+               {
+                       // call the callback and stash the result
+                       begin_timing(thread->actruntime);
+                       item->result = (*item->callback)(item->param, threadid);
+                       end_timing(thread->actruntime);
+
+                       // decrement the item count after we are done
+                       atomic_decrement32(&queue->items);
+                       atomic_exchange32(&item->done, TRUE);
+                       add_to_stat(&thread->itemsdone, 1);
+
+                       // if it's an auto-release item, release it
+                       if (item->flags & WORK_ITEM_FLAG_AUTO_RELEASE)
+                               osd_work_item_release(item);
+
+                       // set the result and signal the event
+                       else if (item->event != NULL)
+                       {
+                               SetEvent(item->event);
+                               add_to_stat(&item->queue->setevents, 1);
+                       }
+
+                       // if we removed an item and there's still work to do, bump the stats
+                       if (queue->list != NULL)
+                               add_to_stat(&queue->extraitems, 1);
+               }
+       }
+
+       // we don't need to set the doneevent for multi queues because they spin
+       if (queue->waiting)
+       {
+               SetEvent(queue->doneevent);
+               add_to_stat(&queue->setevents, 1);
+       }
+
+       end_timing(thread->runtime);
+}