+#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
+// 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"
+
//**************************************************************************
// 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;
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;
};
--- /dev/null
+// 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
--- /dev/null
+// 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__
+// 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
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
};
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
};
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)
{
}
inline UINT32 bitstream_in::peek(int numbits)
{
+ if (numbits == 0)
+ return 0;
+
// fetch data if we need more
if (numbits > m_bits)
{
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)
{
}
}
// 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;
-
#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";
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
//**************************************************************************
// 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
};
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
return result;
}
+
//-------------------------------------------------
// be_write - write a big-endian number to a byte
// buffer
}
}
+
//-------------------------------------------------
// be_read_sha1 - fetch a sha1_t from a data
// stream in bigendian order
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
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
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)
}
}
+
+//-------------------------------------------------
+// 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
}
+//-------------------------------------------------
+// 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
//-------------------------------------------------
-// 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
// 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;
// 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;
//-------------------------------------------------
-// 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;
}
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;
}
+// 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
#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;
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');
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
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; }
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);
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__
--- /dev/null
+/***************************************************************************
+
+ 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;
+}
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;
};
};
-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__ */
#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
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
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 */
};
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();
}
}
/* 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
{
int i;
cdrom_file *file;
- UINT32 physofs, chdofs;
+ UINT32 physofs, chdofs, logofs;
chd_error err;
/* punt if no 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;
{
for (int i = 0; i < file->cdtoc.numtrks; i++)
{
- zfile_fclose(file->fhandle[i]);
+ core_fclose(file->fhandle[i]);
}
}
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 )
buffer[ swapindex + 1 ] = swaptemp;
}
}
-#endif
- return CHDERR_NONE;
+ return result;
}
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 */
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));
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);
}
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;
}
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
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
}
{
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";
}
}
{
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";
}
}
chd_error err;
int i;
+ toc->flags = 0;
+
/* start with no tracks */
for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++)
{
{
/* 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)
{
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 */
/* 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;
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;
/*-------------------------------------------------
cdrom_write_metadata - write metadata
-------------------------------------------------*/
-#if 0
+
chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
{
chd_error err;
/* 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
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;
}
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;
}
{
// 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]);
}
+// 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
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];
};
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);
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
-#endif // __CDROM_H__
+#endif // __CDROM_H__
#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"
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];
};
private:
// internal state
- z_stream m_deflater;
- chd_zlib_allocator m_allocator;
+ z_stream m_deflater;
+ chd_zlib_allocator m_allocator;
};
private:
// internal state
- z_stream m_inflater;
- chd_zlib_allocator m_allocator;
+ z_stream m_inflater;
+ chd_zlib_allocator m_allocator;
};
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];
};
private:
// internal state
- CLzmaEncProps m_props;
- chd_lzma_allocator m_allocator;
+ CLzmaEncProps m_props;
+ chd_lzma_allocator m_allocator;
};
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
private:
// internal state
- huffman_8bit_encoder m_encoder;
+ huffman_8bit_encoder m_encoder;
};
-
+#endif
// ======================> chd_huffman_decompressor
private:
// internal state
- huffman_8bit_decoder m_decoder;
+ huffman_8bit_decoder m_decoder;
};
-
// ======================> chd_flac_compressor
// FLAC compressor
private:
// internal state
- bool m_big_endian;
- flac_encoder m_encoder;
+ bool m_big_endian;
+ flac_encoder m_encoder;
};
-
// ======================> chd_flac_decompressor
// FLAC decompressor
private:
// internal state
- bool m_big_endian;
- flac_decoder m_decoder;
+ bool m_big_endian;
+ flac_decoder m_decoder;
};
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;
};
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;
};
// 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)
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;
};
// 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)
private:
// internal state
- _BaseDecompressor m_base_decompressor;
+ _BaseDecompressor m_base_decompressor;
_SubcodeDecompressor m_subcode_decompressor;
- dynamic_buffer m_buffer;
+ dynamic_buffer m_buffer;
};
void postinit();
// internal state
- avhuff_encoder m_encoder;
- bool m_postinit;
+ avhuff_encoder m_encoder;
+ bool m_postinit;
};
private:
// internal state
- avhuff_decoder m_decoder;
+ avhuff_decoder m_decoder;
};
#endif
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
};
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)
{
}
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
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
// set the low bit of the size so we don't match next time
*ptr = size | 1;
return ptr + 1;
-#endif
}
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
*ptr &= ~1;
return;
}
-#endif
}
: 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);
: 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);
}
-
+#if 0
//**************************************************************************
// HUFFMAN COMPRESSOR
//**************************************************************************
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
//**************************************************************************
}
-
//**************************************************************************
// FLAC COMPRESSOR
//**************************************************************************
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
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);
throw std::bad_alloc();
else if (zerr != Z_OK)
throw CHDERR_CODEC_ERROR;
+#else
+ throw CHDERR_CODEC_ERROR;
#endif
}
}
-
//**************************************************************************
// CD FLAC DECOMPRESSOR
//**************************************************************************
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)
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);
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
{
}
-
//**************************************************************************
// AVHUFF DECOMPRESSOR
//**************************************************************************
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)
}
}
+
//-------------------------------------------------
// config - codec-specific configuration for the
// A/V codec
else
throw CHDERR_INVALID_PARAMETER;
}
-#endif
\ No newline at end of file
+#endif
+// 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
// 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))
private:
// internal state
- chd_file & m_chd;
- UINT32 m_hunkbytes;
- bool m_lossy;
+ chd_file & m_chd;
+ UINT32 m_hunkbytes;
+ bool m_lossy;
};
// 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
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
};
//**************************************************************************
// 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
--- /dev/null
+
+#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
#include "zfile.h"
#endif
+#define NO_MEM_TRACKING
+
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
#define DECL_NORETURN __declspec(noreturn)
#else
#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)
--- /dev/null
+#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);
+}
--- /dev/null
+// 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__ */
+// 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__ */
+// 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
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
// 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
--- /dev/null
+// 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__ */
--- /dev/null
+/***************************************************************************
+
+ 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__ */
#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"
}
-flac_encoder::flac_encoder(struct zfile *file)
+flac_encoder::flac_encoder(core_file &file)
{
init_common();
reset(file);
// 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();
}
{
// 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;
}
{
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)
}
-
//**************************************************************************
// FLAC DECODER
//**************************************************************************
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)
{
}
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();
}
// 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();
}
&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);
}
// 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));
// 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;
// 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
+// 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
#include "corefile.h"
#ifdef FLAC__NO_DLL
-#include "flac/all.h"
+#include "../../lib/libflac/include/FLAC/all.h"
#else
#include <FLAC/all.h>
#endif
// construction/destruction
flac_encoder();
flac_encoder(void *buffer, UINT32 buflength);
- flac_encoder(struct zfile *file);
+ flac_encoder(core_file &file);
~flac_encoder();
// configuration
// 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);
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?
};
// 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)
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);
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
};
--- /dev/null
+#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, §ors, §orbytes) != 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);
+}
--- /dev/null
+// 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__ */
+#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"
{
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);
while (length-- != 0)
crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++];
m_accum.m_raw = crc;
-}
\ No newline at end of file
+}
+// 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
#ifndef __HASHING_H__
#define __HASHING_H__
+#include "osdcore.h"
#include "astring.h"
#include "md5.h"
#include "sha1.h"
protected:
// internal state
- struct sha1_ctx m_context; // internal context
+ struct sha1_ctx m_context; // internal context
};
protected:
// internal state
- struct MD5Context m_context; // internal context
+ struct MD5Context m_context; // internal context
};
protected:
// internal state
- crc32_t m_accum; // internal accumulator
+ crc32_t m_accum; // internal accumulator
};
protected:
// internal state
- crc16_t m_accum; // internal accumulator
+ crc16_t m_accum; // internal accumulator
};
-
#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)
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;
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;
}
+// 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
// 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
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
};
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];
};
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];
};
+#include "chdtypes.h"
/*
* This code implements the MD5 message-digest algorithm.
* The algorithm is due to Ron Rivest. This code was
* Still in the public domain.
*/
-#include <string.h> /* for memcpy() */
+#include <string.h> /* for memcpy() */
#include "md5.h"
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;
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. */
/* 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);
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
/* 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
--- /dev/null
+// 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__ */
+// 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__ */
--- /dev/null
+// 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__ */
--- /dev/null
+// 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__
+#include "chdtypes.h"
/* sha1.h
*
* The sha1 hash function.
* MA 02111-1307, USA.
*/
-#include "chdtypes.h"
-
#include "sha1.h"
#include <assert.h>
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:
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
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
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;
+ }
}
- }
}
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
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 */
--- /dev/null
+// 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__ */
--- /dev/null
+// 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__ */
--- /dev/null
+// 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__ */
--- /dev/null
+#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);
+ }
+}
--- /dev/null
+#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);
+}