From: Toni Wilen Date: Sun, 17 Aug 2014 17:10:11 +0000 (+0300) Subject: CHD support update. X-Git-Tag: 3000~86 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=a3725f448da6ad9979fbfd0a56bd833227a2d813;p=francis%2Fwinuae.git CHD support update. --- diff --git a/archivers/chd/astring.cpp b/archivers/chd/astring.cpp index e74d8122..e04dbd2e 100644 --- a/archivers/chd/astring.cpp +++ b/archivers/chd/astring.cpp @@ -1,52 +1,23 @@ +#include "chdtypes.h" +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** astring.c Allocated string manipulation functions. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ****************************************************************************/ -#include "chdtypes.h" - -#include "astring.h" - #include #include #include #include #include +#include "astring.h" +#include "osdcore.h" + /*************************************************************************** GLOBAL VARIABLES diff --git a/archivers/chd/astring.h b/archivers/chd/astring.h index ca528d67..639591e0 100644 --- a/archivers/chd/astring.h +++ b/archivers/chd/astring.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** astring.h Allocated string manipulation functions. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -42,9 +13,10 @@ #ifndef __ASTRING_H__ #define __ASTRING_H__ -#include #include #include +#include "osdcomm.h" + //************************************************************************** @@ -137,10 +109,10 @@ public: // formatted string helpers int vprintf(const char *format, va_list args); int catvprintf(const char *format, va_list args); - int printf(const char *format, ...) { va_list ap; va_start(ap, format); int result = this->vprintf(format, ap); va_end(ap); return result; } - int catprintf(const char *format, ...) { va_list ap; va_start(ap, format); int result = catvprintf(format, ap); va_end(ap); return result; } - astring &format(const char *format, ...) { va_list ap; va_start(ap, format); this->vprintf(format, ap); va_end(ap); return *this; } - astring &catformat(const char *format, ...) { va_list ap; va_start(ap, format); catvprintf(format, ap); va_end(ap); return *this; } + int printf(const char *format, ...) ATTR_PRINTF(2,3) { va_list ap; va_start(ap, format); int result = this->vprintf(format, ap); va_end(ap); return result; } + int catprintf(const char *format, ...) ATTR_PRINTF(2,3) { va_list ap; va_start(ap, format); int result = catvprintf(format, ap); va_end(ap); return result; } + astring &format(const char *format, ...) ATTR_PRINTF(2,3) { va_list ap; va_start(ap, format); this->vprintf(format, ap); va_end(ap); return *this; } + astring &catformat(const char *format, ...) ATTR_PRINTF(2,3) { va_list ap; va_start(ap, format); catvprintf(format, ap); va_end(ap); return *this; } // comparison helpers int cmp(const char *str2, int count) const; @@ -179,10 +151,10 @@ private: void normalize_substr(int &start, int &count, int length) const; // internal state - char * m_text; - int m_alloclen; - char m_smallbuf[64]; - int m_len; + char * m_text; + int m_alloclen; + char m_smallbuf[64]; + int m_len; }; diff --git a/archivers/chd/avhuff.h b/archivers/chd/avhuff.h new file mode 100644 index 00000000..efc3c71a --- /dev/null +++ b/archivers/chd/avhuff.h @@ -0,0 +1,202 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + avhuff.h + + Audio/video compression and decompression helpers. + +***************************************************************************/ + +#pragma once + +#ifndef __AVHUFF_H__ +#define __AVHUFF_H__ + +#include "osdcore.h" +#include "coretmpl.h" +#include "bitmap.h" +#include "huffman.h" +#include "flac.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +#define AVHUFF_USE_FLAC (1) + + +// errors +enum avhuff_error +{ + AVHERR_NONE = 0, + AVHERR_INVALID_DATA, + AVHERR_VIDEO_TOO_LARGE, + AVHERR_AUDIO_TOO_LARGE, + AVHERR_METADATA_TOO_LARGE, + AVHERR_OUT_OF_MEMORY, + AVHERR_COMPRESSION_ERROR, + AVHERR_TOO_MANY_CHANNELS, + AVHERR_INVALID_CONFIGURATION, + AVHERR_INVALID_PARAMETER, + AVHERR_BUFFER_TOO_SMALL +}; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> av_codec_decompress_config + +// decompression configuration +class avhuff_decompress_config +{ +public: + avhuff_decompress_config() + : maxsamples(0), + actsamples(NULL), + maxmetalength(0), + actmetalength(NULL), + metadata(NULL) + { + memset(audio, 0, sizeof(audio)); + } + + bitmap_yuy16 video; // pointer to video bitmap + UINT32 maxsamples; // maximum number of samples per channel + UINT32 * actsamples; // actual number of samples per channel + INT16 * audio[16]; // pointer to individual audio channels + UINT32 maxmetalength; // maximum length of metadata + UINT32 * actmetalength; // actual length of metadata + UINT8 * metadata; // pointer to metadata buffer +}; + + +// ======================> avhuff_encoder + +// core state for the codec +class avhuff_encoder +{ +public: + // construction/destruction + avhuff_encoder(); + + // encode/decode + avhuff_error encode_data(const UINT8 *source, UINT8 *dest, UINT32 &complength); + + // static helpers + static UINT32 raw_data_size(const UINT8 *data); + static UINT32 raw_data_size(UINT32 width, UINT32 height, UINT8 channels, UINT32 numsamples, UINT32 metadatasize = 0) { return 12 + channels * numsamples * 2 + width * height * 2; } + static avhuff_error assemble_data(dynamic_buffer &buffer, bitmap_yuy16 &bitmap, UINT8 channels, UINT32 numsamples, INT16 **samples, UINT8 *metadata = NULL, UINT32 metadatasize = 0); + +private: + // delta-RLE Huffman encoder + class deltarle_encoder + { + public: + // construction/destruction + deltarle_encoder() + : m_rlecount(0) { } + + // histogramming + UINT16 *rle_and_histo_bitmap(const UINT8 *source, UINT32 items_per_row, UINT32 item_advance, UINT32 row_count); + + // encoding + void flush_rle() { m_rlecount = 0; } + void encode_one(bitstream_out &bitbuf, UINT16 *&rleptr); + huffman_error export_tree_rle(bitstream_out &bitbuf) { return m_encoder.export_tree_rle(bitbuf); } + + private: + // internal state + int m_rlecount; + huffman_encoder<256 + 16> m_encoder; + dynamic_array m_rlebuffer; + }; + + // internal helpers + avhuff_error encode_audio(const UINT8 *source, int channels, int samples, UINT8 *dest, UINT8 *sizes); + avhuff_error encode_video(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength); + avhuff_error encode_video_lossless(const UINT8 *source, int width, int height, UINT8 *dest, UINT32 &complength); + + // video encoding contexts + deltarle_encoder m_ycontext; + deltarle_encoder m_cbcontext; + deltarle_encoder m_crcontext; + + // audio encoding contexts + dynamic_buffer m_audiobuffer; +#if AVHUFF_USE_FLAC + flac_encoder m_flac_encoder; +#else + huffman_8bit_encoder m_audiohi_encoder; + huffman_8bit_encoder m_audiolo_encoder; +#endif +}; + + +// ======================> avhuff_decoder + +// core state for the codec +class avhuff_decoder +{ +public: + // construction/destruction + avhuff_decoder(); + + // configuration + void configure(const avhuff_decompress_config &config); + + // encode/decode + avhuff_error decode_data(const UINT8 *source, UINT32 complength, UINT8 *dest); + +private: + // delta-RLE Huffman decoder + class deltarle_decoder + { + public: + // construction/destruction + deltarle_decoder() + : m_rlecount(0), m_prevdata(0) { } + + // general + void reset() { m_rlecount = m_prevdata = 0; } + + // decoding + void flush_rle() { m_rlecount = 0; } + UINT32 decode_one(bitstream_in &bitbuf); + huffman_error import_tree_rle(bitstream_in &bitbuf) { return m_decoder.import_tree_rle(bitbuf); } + + private: + // internal state + int m_rlecount; + UINT8 m_prevdata; + huffman_decoder<256 + 16> m_decoder; + }; + + + // internal helpers + avhuff_error decode_audio(int channels, int samples, const UINT8 *source, UINT8 **dest, UINT32 dxor, const UINT8 *sizes); + avhuff_error decode_video(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor); + avhuff_error decode_video_lossless(int width, int height, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 dstride, UINT32 dxor); + + // internal state + avhuff_decompress_config m_config; + + // video decoding contexts + deltarle_decoder m_ycontext; + deltarle_decoder m_cbcontext; + deltarle_decoder m_crcontext; + + // audio decoding contexts + huffman_8bit_decoder m_audiohi_decoder; + huffman_8bit_decoder m_audiolo_decoder; +#if AVHUFF_USE_FLAC + flac_decoder m_flac_decoder; +#endif +}; + + +#endif diff --git a/archivers/chd/bitmap.h b/archivers/chd/bitmap.h new file mode 100644 index 00000000..a82ec7ee --- /dev/null +++ b/archivers/chd/bitmap.h @@ -0,0 +1,412 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + bitmap.h + + Core bitmap routines. + +***************************************************************************/ + +#pragma once + +#ifndef __BITMAP_H__ +#define __BITMAP_H__ + +#include "osdcore.h" +#include "palette.h" +#include + + +//************************************************************************** +// 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 + _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(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(y, x); } + pixel_t &pix8(INT32 y, INT32 x = 0) const { return pixt(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(y, x); } + pixel_t &pix16(INT32 y, INT32 x = 0) const { return pixt(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(y, x); } + pixel_t &pix32(INT32 y, INT32 x = 0) const { return pixt(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(y, x); } + pixel_t &pix64(INT32 y, INT32 x = 0) const { return pixt(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(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(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(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(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(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(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(source), subrect); } + + // getters + bitmap_format format() const { return k_bitmap_format; } +}; + + +#endif // __BITMAP_H__ diff --git a/archivers/chd/bitstream.h b/archivers/chd/bitstream.h index 1c185919..2f36271d 100644 --- a/archivers/chd/bitstream.h +++ b/archivers/chd/bitstream.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** bitstream.h Helper classes for reading/writing at the bit level. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -68,11 +39,11 @@ public: private: // internal state - UINT32 m_buffer; // current bit accumulator - int m_bits; // number of bits in the accumulator - const UINT8 * m_read; // read pointer - UINT32 m_doffset; // byte offset within the data - UINT32 m_dlength; // length of the data + UINT32 m_buffer; // current bit accumulator + int m_bits; // number of bits in the accumulator + const UINT8 * m_read; // read pointer + UINT32 m_doffset; // byte offset within the data + UINT32 m_dlength; // length of the data }; @@ -92,11 +63,11 @@ public: private: // internal state - UINT32 m_buffer; // current bit accumulator - int m_bits; // number of bits in the accumulator - UINT8 * m_write; // write pointer - UINT32 m_doffset; // byte offset within the data - UINT32 m_dlength; // length of the data + UINT32 m_buffer; // current bit accumulator + int m_bits; // number of bits in the accumulator + UINT8 * m_write; // write pointer + UINT32 m_doffset; // byte offset within the data + UINT32 m_dlength; // length of the data }; @@ -111,10 +82,10 @@ private: inline bitstream_in::bitstream_in(const void *src, UINT32 srclength) : m_buffer(0), - m_bits(0), - m_read(reinterpret_cast(src)), - m_doffset(0), - m_dlength(srclength) + m_bits(0), + m_read(reinterpret_cast(src)), + m_doffset(0), + m_dlength(srclength) { } @@ -126,6 +97,9 @@ inline bitstream_in::bitstream_in(const void *src, UINT32 srclength) inline UINT32 bitstream_in::peek(int numbits) { + if (numbits == 0) + return 0; + // fetch data if we need more if (numbits > m_bits) { @@ -206,10 +180,10 @@ inline UINT32 bitstream_in::flush() inline bitstream_out::bitstream_out(void *dest, UINT32 destlength) : m_buffer(0), - m_bits(0), - m_write(reinterpret_cast(dest)), - m_doffset(0), - m_dlength(destlength) + m_bits(0), + m_write(reinterpret_cast(dest)), + m_doffset(0), + m_dlength(destlength) { } @@ -234,7 +208,10 @@ inline void bitstream_out::write(UINT32 newbits, int numbits) } // shift the bits to the top - newbits <<= 32 - numbits; + if (numbits == 0) + newbits = 0; + else + newbits <<= 32 - numbits; // now shift it down to account for the number of bits we already have and OR them in m_buffer |= newbits >> m_bits; diff --git a/archivers/chd/chd.cpp b/archivers/chd/chd.cpp index 14d7d5e1..bc0af076 100644 --- a/archivers/chd/chd.cpp +++ b/archivers/chd/chd.cpp @@ -1,12 +1,32 @@ - #include "chdtypes.h" +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + chd.c + + MAME Compressed Hunks of Data file format + +***************************************************************************/ #include "chd.h" +#include "avhuff.h" #include "hashing.h" +#include "flac.h" #include "chdcdrom.h" #include "coretmpl.h" -#include "bitstream.h" -#include "huffman.h" +#include "chdcodec.h" + +#include +#include +#include +#include +#include + + +//************************************************************************** +// CONSTANTS +//************************************************************************** // standard metadata formats const char *HARD_DISK_METADATA_FORMAT = "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d"; @@ -15,48 +35,49 @@ const char *CDROM_TRACK_METADATA2_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:% const char *GDROM_TRACK_METADATA_FORMAT = "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d PAD:%d PREGAP:%d PGTYPE:%s PGSUB:%s POSTGAP:%d"; const char *AV_METADATA_FORMAT = "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d"; -static const UINT32 METADATA_HEADER_SIZE = 16; // metadata header size +static const UINT32 METADATA_HEADER_SIZE = 16; // metadata header size -static const UINT8 V34_MAP_ENTRY_FLAG_TYPE_MASK = 0x0f; // what type of hunk -static const UINT8 V34_MAP_ENTRY_FLAG_NO_CRC = 0x10; // no CRC is present +static const UINT8 V34_MAP_ENTRY_FLAG_TYPE_MASK = 0x0f; // what type of hunk +static const UINT8 V34_MAP_ENTRY_FLAG_NO_CRC = 0x10; // no CRC is present // V3-V4 entry types enum { - V34_MAP_ENTRY_TYPE_INVALID = 0, // invalid type - V34_MAP_ENTRY_TYPE_COMPRESSED = 1, // standard compression - V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, // uncompressed data - V34_MAP_ENTRY_TYPE_MINI = 3, // mini: use offset as raw data - V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, // same as another hunk in this file - V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, // same as a hunk in the parent file - V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 // compressed with secondary algorithm (usually FLAC CDDA) + V34_MAP_ENTRY_TYPE_INVALID = 0, // invalid type + V34_MAP_ENTRY_TYPE_COMPRESSED = 1, // standard compression + V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, // uncompressed data + V34_MAP_ENTRY_TYPE_MINI = 3, // mini: use offset as raw data + V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, // same as another hunk in this file + V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, // same as a hunk in the parent file + V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 // compressed with secondary algorithm (usually FLAC CDDA) }; // V5 compression types enum { // these types are live when running - COMPRESSION_TYPE_0 = 0, // codec #0 - COMPRESSION_TYPE_1 = 1, // codec #1 - COMPRESSION_TYPE_2 = 2, // codec #2 - COMPRESSION_TYPE_3 = 3, // codec #3 - COMPRESSION_NONE = 4, // no compression; implicit length = hunkbytes - COMPRESSION_SELF = 5, // same as another block in this chd - COMPRESSION_PARENT = 6, // same as a hunk's worth of units in the parent chd + COMPRESSION_TYPE_0 = 0, // codec #0 + COMPRESSION_TYPE_1 = 1, // codec #1 + COMPRESSION_TYPE_2 = 2, // codec #2 + COMPRESSION_TYPE_3 = 3, // codec #3 + COMPRESSION_NONE = 4, // no compression; implicit length = hunkbytes + COMPRESSION_SELF = 5, // same as another block in this chd + COMPRESSION_PARENT = 6, // same as a hunk's worth of units in the parent chd // these additional pseudo-types are used for compressed encodings: - COMPRESSION_RLE_SMALL, // start of small RLE run (4-bit length) - COMPRESSION_RLE_LARGE, // start of large RLE run (8-bit length) - COMPRESSION_SELF_0, // same as the last COMPRESSION_SELF block - COMPRESSION_SELF_1, // same as the last COMPRESSION_SELF block + 1 - COMPRESSION_PARENT_SELF, // same block in the parent - COMPRESSION_PARENT_0, // same as the last COMPRESSION_PARENT block - COMPRESSION_PARENT_1 // same as the last COMPRESSION_PARENT block + 1 + COMPRESSION_RLE_SMALL, // start of small RLE run (4-bit length) + COMPRESSION_RLE_LARGE, // start of large RLE run (8-bit length) + COMPRESSION_SELF_0, // same as the last COMPRESSION_SELF block + COMPRESSION_SELF_1, // same as the last COMPRESSION_SELF block + 1 + COMPRESSION_PARENT_SELF, // same block in the parent + COMPRESSION_PARENT_0, // same as the last COMPRESSION_PARENT block + COMPRESSION_PARENT_1 // same as the last COMPRESSION_PARENT block + 1 }; + //************************************************************************** // TYPE DEFINITIONS //************************************************************************** @@ -66,12 +87,12 @@ enum // description of where a metadata entry lives within the file struct chd_file::metadata_entry { - UINT64 offset; // offset within the file of the header - UINT64 next; // offset within the file of the next header - UINT64 prev; // offset within the file of the previous header - UINT32 length; // length of the metadata - UINT32 metatag; // metadata tag - UINT8 flags; // flag bits + UINT64 offset; // offset within the file of the header + UINT64 next; // offset within the file of the next header + UINT64 prev; // offset within the file of the previous header + UINT32 length; // length of the metadata + UINT32 metatag; // metadata tag + UINT8 flags; // flag bits }; @@ -79,11 +100,16 @@ struct chd_file::metadata_entry struct chd_file::metadata_hash { - UINT8 tag[4]; // tag of the metadata in big-endian - sha1_t sha1; // hash data + UINT8 tag[4]; // tag of the metadata in big-endian + sha1_t sha1; // hash data }; + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + //------------------------------------------------- // be_read - extract a big-endian number from // a byte buffer @@ -97,6 +123,7 @@ inline UINT64 chd_file::be_read(const UINT8 *base, int numbytes) return result; } + //------------------------------------------------- // be_write - write a big-endian number to a byte // buffer @@ -112,6 +139,7 @@ inline void chd_file::be_write(UINT8 *base, UINT64 value, int numbytes) } } + //------------------------------------------------- // be_read_sha1 - fetch a sha1_t from a data // stream in bigendian order @@ -124,6 +152,18 @@ inline sha1_t chd_file::be_read_sha1(const UINT8 *base) return result; } + +//------------------------------------------------- +// be_write_sha1 - write a sha1_t to a data +// stream in bigendian order +//------------------------------------------------- + +inline void chd_file::be_write_sha1(UINT8 *base, sha1_t value) +{ + memcpy(base, &value.m_raw[0], sizeof(value.m_raw)); +} + + //------------------------------------------------- // file_read - read from the file at the given // offset; on failure throw an error @@ -136,12 +176,191 @@ inline void chd_file::file_read(UINT64 offset, void *dest, UINT32 length) throw CHDERR_NOT_OPEN; // seek and read - zfile_fseek(m_file, offset, SEEK_SET); - UINT32 count = zfile_fread(dest, 1, length, m_file); + core_fseek(m_file, offset, SEEK_SET); + UINT32 count = core_fread(m_file, dest, length); + if (count != length) + throw CHDERR_READ_ERROR; +} + + +//------------------------------------------------- +// file_write - write to the file at the given +// offset; on failure throw an error +//------------------------------------------------- + +inline void chd_file::file_write(UINT64 offset, const void *source, UINT32 length) +{ + // no file = failure + if (m_file == NULL) + throw CHDERR_NOT_OPEN; + + // seek and write + core_fseek(m_file, offset, SEEK_SET); + UINT32 count = core_fwrite(m_file, source, length); + if (count != length) + throw CHDERR_WRITE_ERROR; +} + + +//------------------------------------------------- +// file_append - append to the file at the given +// offset, ensuring we start at the given +// alignment; on failure throw an error +//------------------------------------------------- + +inline UINT64 chd_file::file_append(const void *source, UINT32 length, UINT32 alignment) +{ + // no file = failure + if (m_file == NULL) + throw CHDERR_NOT_OPEN; + + // seek to the end and align if necessary + core_fseek(m_file, 0, SEEK_END); + if (alignment != 0) + { + UINT64 offset = core_ftell(m_file); + UINT32 delta = offset % alignment; + if (delta != 0) + { + // pad with 0's from a local buffer + UINT8 buffer[1024]; + memset(buffer, 0, sizeof(buffer)); + delta = alignment - delta; + while (delta != 0) + { + UINT32 bytes_to_write = MIN(sizeof(buffer), delta); + UINT32 count = core_fwrite(m_file, buffer, bytes_to_write); + if (count != bytes_to_write) + throw CHDERR_WRITE_ERROR; + delta -= bytes_to_write; + } + } + } + + // write the real data + UINT64 offset = core_ftell(m_file); + UINT32 count = core_fwrite(m_file, source, length); if (count != length) throw CHDERR_READ_ERROR; + return offset; +} + + +//------------------------------------------------- +// bits_for_value - return the number of bits +// necessary to represent all numbers 0..value +//------------------------------------------------- + +inline UINT8 chd_file::bits_for_value(UINT64 value) +{ + UINT8 result = 0; + while (value != 0) + value >>= 1, result++; + return result; +} + + + +//************************************************************************** +// CHD FILE MANAGEMENT +//************************************************************************** + +//------------------------------------------------- +// chd_file - constructor +//------------------------------------------------- + +chd_file::chd_file() + : m_file(NULL), + m_owns_file(false) +{ + // reset state + memset(m_decompressor, 0, sizeof(m_decompressor)); + close(); +} + + +//------------------------------------------------- +// ~chd_file - destructor +//------------------------------------------------- + +chd_file::~chd_file() +{ + // close any open files + close(); +} + + +//------------------------------------------------- +// sha1 - return our SHA1 value +//------------------------------------------------- + +sha1_t chd_file::sha1() +{ + try + { + // read the big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); + return be_read_sha1(rawbuf); + } + catch (chd_error &) + { + // on failure, return NULL + return sha1_t::null; + } +} + + +//------------------------------------------------- +// raw_sha1 - return our raw SHA1 value +//------------------------------------------------- + +sha1_t chd_file::raw_sha1() +{ + try + { + // determine offset within the file for data-only + if (m_rawsha1_offset == 0) + throw CHDERR_UNSUPPORTED_VERSION; + + // read the big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf)); + return be_read_sha1(rawbuf); + } + catch (chd_error &) + { + // on failure, return NULL + return sha1_t::null; + } +} + + +//------------------------------------------------- +// parent_sha1 - return our parent's SHA1 value +//------------------------------------------------- + +sha1_t chd_file::parent_sha1() +{ + try + { + // determine offset within the file + if (m_parentsha1_offset == 0) + throw CHDERR_UNSUPPORTED_VERSION; + + // read the big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); + return be_read_sha1(rawbuf); + } + catch (chd_error &) + { + // on failure, return NULL + return sha1_t::null; + } } + //------------------------------------------------- // hunk_info - return information about this // hunk @@ -241,88 +460,275 @@ chd_error chd_file::hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 return CHDERR_NONE; } + //------------------------------------------------- -// open - open an existing file for read or -// read/write +// set_raw_sha1 - set our SHA1 values +//------------------------------------------------- + +void chd_file::set_raw_sha1(sha1_t rawdata) +{ + // create a big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + be_write_sha1(rawbuf, rawdata); + + // write to the header + UINT64 offset = (m_rawsha1_offset != 0) ? m_rawsha1_offset : m_sha1_offset; + assert(offset != 0); + file_write(offset, rawbuf, sizeof(rawbuf)); + + // if we have a separate rawsha1_offset, update the full sha1 as well + if (m_rawsha1_offset != 0) + metadata_update_hash(); +} + + //------------------------------------------------- +// set_parent_sha1 - set the parent SHA1 value +//------------------------------------------------- + +void chd_file::set_parent_sha1(sha1_t parent) +{ + // if no file, fail + if (m_file == NULL) + throw CHDERR_INVALID_FILE; + + // create a big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + be_write_sha1(rawbuf, parent); + + // write to the header + assert(m_parentsha1_offset != 0); + file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); +} -chd_error chd_file::open(struct zfile *file, bool writeable, chd_file *parent) + +//------------------------------------------------- +// create - create a new file with no parent +// using an existing opened file handle +//------------------------------------------------- + +chd_error chd_file::create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]) { // make sure we don't already have a file open if (m_file != NULL) return CHDERR_ALREADY_OPEN; - // open the file - m_file = file; + // set the header parameters + m_logicalbytes = logicalbytes; + m_hunkbytes = hunkbytes; + m_unitbytes = unitbytes; + memcpy(m_compression, compression, sizeof(m_compression)); + m_parent = NULL; + + // take ownership of the file + m_file = &file; m_owns_file = false; - m_parent = parent; - return open_common(writeable); + return create_common(); } + //------------------------------------------------- -// close - close a CHD file for access +// create - create a new file with a parent +// using an existing opened file handle //------------------------------------------------- -void chd_file::close() +chd_error chd_file::create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent) { - // reset file characteristics - if (m_owns_file && m_file != NULL) - zfile_fclose(m_file); - m_file = NULL; + // make sure we don't already have a file open + if (m_file != NULL) + return CHDERR_ALREADY_OPEN; + + // set the header parameters + m_logicalbytes = logicalbytes; + m_hunkbytes = hunkbytes; + m_unitbytes = parent.unit_bytes(); + memcpy(m_compression, compression, sizeof(m_compression)); + m_parent = &parent; + + // take ownership of the file + m_file = &file; m_owns_file = false; - m_allow_reads = false; - m_allow_writes = false; + return create_common(); +} - // reset core parameters from the header - m_version = HEADER_VERSION; - m_logicalbytes = 0; - m_mapoffset = 0; - m_metaoffset = 0; - m_hunkbytes = 0; - m_hunkcount = 0; - m_unitbytes = 0; - m_unitcount = 0; - memset(m_compression, 0, sizeof(m_compression)); - m_parent = NULL; - m_parent_missing = false; - // reset key offsets within the header - m_mapoffset_offset = 0; - m_metaoffset_offset = 0; - m_sha1_offset = 0; - m_rawsha1_offset = 0; - m_parentsha1_offset = 0; +//------------------------------------------------- +// create - create a new file with no parent +// using a filename +//------------------------------------------------- - // reset map information - m_mapentrybytes = 0; - m_rawmap.reset(); +chd_error chd_file::create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]) +{ + // make sure we don't already have a file open + if (m_file != NULL) + return CHDERR_ALREADY_OPEN; - // reset compression management - for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_decompressor); decompnum++) + // create the new file + core_file *file = NULL; + file_error filerr = core_fopen(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &file); + if (filerr != FILERR_NONE) + return CHDERR_FILE_NOT_FOUND; + + // create the file normally, then claim the file + chd_error chderr = create(*file, logicalbytes, hunkbytes, unitbytes, compression); + m_owns_file = true; + + // if an error happened, close and delete the file + if (chderr != CHDERR_NONE) { - delete m_decompressor[decompnum]; - m_decompressor[decompnum] = NULL; + core_fclose(file); + osd_rmfile(filename); } - m_compressed.reset(); - - // reset caching - m_cache.reset(); - m_cachehunk = ~0; + return chderr; } //------------------------------------------------- -// read - read a single hunk from the CHD file +// create - create a new file with a parent +// using a filename //------------------------------------------------- -chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer) +chd_error chd_file::create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent) { - // wrap this for clean reporting - try - { - // punt if no file - if (m_file == NULL) - throw CHDERR_NOT_OPEN; + // make sure we don't already have a file open + if (m_file != NULL) + return CHDERR_ALREADY_OPEN; + + // create the new file + core_file *file = NULL; + file_error filerr = core_fopen(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &file); + if (filerr != FILERR_NONE) + return CHDERR_FILE_NOT_FOUND; + + // create the file normally, then claim the file + chd_error chderr = create(*file, logicalbytes, hunkbytes, compression, parent); + m_owns_file = true; + + // if an error happened, close and delete the file + if (chderr != CHDERR_NONE) + { + core_fclose(file); + osd_rmfile(filename); + } + return chderr; +} + + +//------------------------------------------------- +// open - open an existing file for read or +// read/write +//------------------------------------------------- + +chd_error chd_file::open(const TCHAR *filename, bool writeable, chd_file *parent) +{ + // make sure we don't already have a file open + if (m_file != NULL) + return CHDERR_ALREADY_OPEN; + + // open the file + UINT32 openflags = writeable ? (OPEN_FLAG_READ | OPEN_FLAG_WRITE) : OPEN_FLAG_READ; + core_file *file = NULL; + file_error filerr = core_fopen(filename, openflags, &file); + if (filerr != FILERR_NONE) + return CHDERR_FILE_NOT_FOUND; + + // now open the CHD + chd_error err = open(*file, writeable, parent); + if (err != CHDERR_NONE) + { + core_fclose(file); + return err; + } + + // we now own this file + m_owns_file = true; + return err; +} + + +//------------------------------------------------- +// open - open an existing file for read or +// read/write +//------------------------------------------------- + +chd_error chd_file::open(core_file &file, bool writeable, chd_file *parent) +{ + // make sure we don't already have a file open + if (m_file != NULL) + return CHDERR_ALREADY_OPEN; + + // open the file + m_file = &file; + m_owns_file = false; + m_parent = parent; + return open_common(writeable); +} + + +//------------------------------------------------- +// close - close a CHD file for access +//------------------------------------------------- + +void chd_file::close() +{ + // reset file characteristics + if (m_owns_file && m_file != NULL) + core_fclose(m_file); + m_file = NULL; + m_owns_file = false; + m_allow_reads = false; + m_allow_writes = false; + + // reset core parameters from the header + m_version = HEADER_VERSION; + m_logicalbytes = 0; + m_mapoffset = 0; + m_metaoffset = 0; + m_hunkbytes = 0; + m_hunkcount = 0; + m_unitbytes = 0; + m_unitcount = 0; + memset(m_compression, 0, sizeof(m_compression)); + m_parent = NULL; + m_parent_missing = false; + + // reset key offsets within the header + m_mapoffset_offset = 0; + m_metaoffset_offset = 0; + m_sha1_offset = 0; + m_rawsha1_offset = 0; + m_parentsha1_offset = 0; + + // reset map information + m_mapentrybytes = 0; + m_rawmap.reset(); + + // reset compression management + for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_decompressor); decompnum++) + { + delete m_decompressor[decompnum]; + m_decompressor[decompnum] = NULL; + } + m_compressed.reset(); + + // reset caching + m_cache.reset(); + m_cachehunk = ~0; +} + + +//------------------------------------------------- +// read - read a single hunk from the CHD file +//------------------------------------------------- + +chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer) +{ + // wrap this for clean reporting + try + { + // punt if no file + if (m_file == NULL) + throw CHDERR_NOT_OPEN; // return an error if out of range if (hunknum >= m_hunkcount) @@ -441,6 +847,101 @@ chd_error chd_file::read_hunk(UINT32 hunknum, void *buffer) } } + +//------------------------------------------------- +// write - write a single hunk to the CHD file +//------------------------------------------------- + +chd_error chd_file::write_hunk(UINT32 hunknum, const void *buffer) +{ + // wrap this for clean reporting + try + { + // punt if no file + if (m_file == NULL) + throw CHDERR_NOT_OPEN; + + // return an error if out of range + if (hunknum >= m_hunkcount) + throw CHDERR_HUNK_OUT_OF_RANGE; + + // if not writeable, fail + if (!m_allow_writes) + throw CHDERR_FILE_NOT_WRITEABLE; + + // uncompressed writes only via this interface + if (compressed()) + throw CHDERR_FILE_NOT_WRITEABLE; + + // see if we have allocated the space on disk for this hunk + UINT8 *rawmap = m_rawmap + hunknum * 4; + UINT32 rawentry = be_read(rawmap, 4); + + // if not, allocate one now + if (rawentry == 0) + { + // first make sure we need to allocate it + bool all_zeros = true; + const UINT32 *scan = reinterpret_cast(buffer); + for (UINT32 index = 0; index < m_hunkbytes / 4; index++) + if (scan[index] != 0) + { + all_zeros = false; + break; + } + + // if it's all zeros, do nothing more + if (all_zeros) + return CHDERR_NONE; + + // append new data to the end of the file, aligning the first chunk + rawentry = file_append(buffer, m_hunkbytes, m_hunkbytes) / m_hunkbytes; + + // write the map entry back + be_write(rawmap, rawentry, 4); + file_write(m_mapoffset + hunknum * 4, rawmap, 4); + + // update the cached hunk if we just wrote it + if (hunknum == m_cachehunk && buffer != m_cache) + memcpy(m_cache, buffer, m_hunkbytes); + } + + // otherwise, just overwrite + else + file_write(UINT64(rawentry) * UINT64(m_hunkbytes), buffer, m_hunkbytes); + return CHDERR_NONE; + } + + // just return errors + catch (chd_error &err) + { + return err; + } +} + + +//------------------------------------------------- +// read_units - read the given number of units +// from the CHD +//------------------------------------------------- + +chd_error chd_file::read_units(UINT64 unitnum, void *buffer, UINT32 count) +{ + return read_bytes(unitnum * UINT64(m_unitbytes), buffer, count * m_unitbytes); +} + + +//------------------------------------------------- +// write_units - write the given number of units +// to the CHD +//------------------------------------------------- + +chd_error chd_file::write_units(UINT64 unitnum, const void *buffer, UINT32 count) +{ + return write_bytes(unitnum * UINT64(m_unitbytes), buffer, count * m_unitbytes); +} + + //------------------------------------------------- // read_bytes - read from the CHD at a byte level, // using the cache to handle partial hunks @@ -485,6 +986,51 @@ chd_error chd_file::read_bytes(UINT64 offset, void *buffer, UINT32 bytes) } +//------------------------------------------------- +// write_bytes - write to the CHD at a byte level, +// using the cache to handle partial hunks +//------------------------------------------------- + +chd_error chd_file::write_bytes(UINT64 offset, const void *buffer, UINT32 bytes) +{ + // iterate over hunks + UINT32 first_hunk = offset / m_hunkbytes; + UINT32 last_hunk = (offset + bytes - 1) / m_hunkbytes; + const UINT8 *source = reinterpret_cast(buffer); + for (UINT32 curhunk = first_hunk; curhunk <= last_hunk; curhunk++) + { + // determine start/end boundaries + UINT32 startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; + UINT32 endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); + + // if it's a full block, just write directly to disk unless it's the cached hunk + chd_error err = CHDERR_NONE; + if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) + err = write_hunk(curhunk, source); + + // otherwise, write from the cache + else + { + if (curhunk != m_cachehunk) + { + err = read_hunk(curhunk, m_cache); + if (err != CHDERR_NONE) + return err; + m_cachehunk = curhunk; + } + memcpy(&m_cache[startoffs], source, endoffs + 1 - startoffs); + err = write_hunk(curhunk, m_cache); + } + + // handle errors and advance + if (err != CHDERR_NONE) + return err; + source += endoffs + 1 - startoffs; + } + return CHDERR_NONE; +} + + //------------------------------------------------- // read_metadata - read the indexed metadata // of the given type @@ -590,46 +1136,310 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, UINT32 searchindex //------------------------------------------------- -// guess_unitbytes - for older CHD formats, take -// a guess at the bytes/unit based on metadata +// write_metadata - write the indexed metadata +// of the given type //------------------------------------------------- -UINT32 chd_file::guess_unitbytes() +chd_error chd_file::write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen, UINT8 flags) { - // look for hard disk metadata; if found, then the unit size == sector size - astring metadata; - int i0, i1, i2, i3; - if (read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE && sscanf(metadata, HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4) - return i3; + // wrap this for clean reporting + try + { + // must write at least 1 byte and no more than 16MB + if (inputlen < 1 || inputlen >= 16 * 1024 * 1024) + return CHDERR_INVALID_PARAMETER; - // look for CD-ROM metadata; if found, then the unit size == CD frame size - if (read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE || - read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE || - read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE || - read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE) - return CD_FRAME_SIZE; + // find the entry if it already exists + metadata_entry metaentry; + bool finished = false; + if (metadata_find(metatag, metaindex, metaentry)) + { + // if the new data fits over the old data, just overwrite + if (inputlen <= metaentry.length) + { + file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen); - // otherwise, just map 1:1 with the hunk size - return m_hunkbytes; + // if the lengths don't match, we need to update the length in our header + if (inputlen != metaentry.length) + { + UINT8 length[3]; + be_write(length, inputlen, 3); + file_write(metaentry.offset + 5, length, sizeof(length)); + } + + // indicate we did everything + finished = true; + } + + // if it doesn't fit, unlink the current entry + else + metadata_set_previous_next(metaentry.prev, metaentry.next); + } + + // if not yet done, create a new entry and append + if (!finished) + { + // now build us a new entry + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + be_write(&raw_meta_header[0], metatag, 4); + raw_meta_header[4] = flags; + be_write(&raw_meta_header[5], (inputlen & 0x00ffffff) | (flags << 24), 3); + be_write(&raw_meta_header[8], 0, 8); + + // append the new header, then the data + UINT64 offset = file_append(raw_meta_header, sizeof(raw_meta_header)); + file_append(inputbuf, inputlen); + + // set the previous entry to point to us + metadata_set_previous_next(metaentry.prev, offset); + } + + // update the hash + metadata_update_hash(); + return CHDERR_NONE; + } + + // return any errors + catch (chd_error &err) + { + return err; + } } //------------------------------------------------- -// parse_v3_header - parse the header from a v3 -// file and configure core parameters +// delete_metadata - remove the given metadata +// from the list //------------------------------------------------- -void chd_file::parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1) +chd_error chd_file::delete_metadata(chd_metadata_tag metatag, UINT32 metaindex) { - // verify header length - if (be_read(&rawheader[8], 4) != V3_HEADER_SIZE) - throw CHDERR_INVALID_FILE; + // wrap this for clean reporting + try + { + // find the entry + metadata_entry metaentry; + if (!metadata_find(metatag, metaindex, metaentry)) + throw CHDERR_METADATA_NOT_FOUND; - // extract core info - m_logicalbytes = be_read(&rawheader[28], 8); - m_mapoffset = 120; - m_metaoffset = be_read(&rawheader[36], 8); - m_hunkbytes = be_read(&rawheader[76], 4); + // point the previous to the next, unlinking us + metadata_set_previous_next(metaentry.prev, metaentry.next); + return CHDERR_NONE; + } + + // return any errors + catch (chd_error &err) + { + return err; + } +} + + +//------------------------------------------------- +// clone_all_metadata - clone the metadata from +// one CHD to a second +//------------------------------------------------- + +chd_error chd_file::clone_all_metadata(chd_file &source) +{ + // wrap this for clean reporting + try + { + // iterate over metadata entries in the source + dynamic_buffer filedata; + metadata_entry metaentry; + metaentry.metatag = 0; + metaentry.length = 0; + metaentry.next = 0; + metaentry.flags = 0; + for (bool has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) + { + // read the metadata item + filedata.resize(metaentry.length); + source.file_read(metaentry.offset + METADATA_HEADER_SIZE, filedata, metaentry.length); + + // write it to the destination + chd_error err = write_metadata(metaentry.metatag, (UINT32)-1, filedata, metaentry.length, metaentry.flags); + if (err != CHDERR_NONE) + throw err; + } + return CHDERR_NONE; + } + + // return any errors + catch (chd_error &err) + { + return err; + } +} + + +//------------------------------------------------- +// compute_overall_sha1 - iterate through the +// metadata and compute the overall hash of the +// CHD file +//------------------------------------------------- + +sha1_t chd_file::compute_overall_sha1(sha1_t rawsha1) +{ + // only works for v4 and above + if (m_version < 4) + return rawsha1; + + // iterate over metadata + dynamic_buffer filedata; + dynamic_array hasharray; + metadata_entry metaentry; + for (bool has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) + { + // if not checksumming, continue + if ((metaentry.flags & CHD_MDFLAGS_CHECKSUM) == 0) + continue; + + // allocate memory and read the data + filedata.resize(metaentry.length); + file_read(metaentry.offset + METADATA_HEADER_SIZE, filedata, metaentry.length); + + // create an entry for this metadata and add it + metadata_hash hashentry; + be_write(hashentry.tag, metaentry.metatag, 4); + hashentry.sha1 = sha1_creator::simple(filedata, metaentry.length); + hasharray.append(hashentry); + } + + // sort the array + if (hasharray.count() != 0) + qsort(&hasharray[0], hasharray.count(), sizeof(hasharray[0]), metadata_hash_compare); + + // read the raw data hash from our header and start a new SHA1 with that data + sha1_creator overall_sha1; + overall_sha1.append(&rawsha1, sizeof(rawsha1)); + if (hasharray.count() != 0) + overall_sha1.append(&hasharray[0], hasharray.count() * sizeof(hasharray[0])); + return overall_sha1.finish(); +} + + +//------------------------------------------------- +// codec_config - set internal codec parameters +//------------------------------------------------- + +chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *config) +{ + // wrap this for clean reporting + try + { + // find the codec and call its configuration + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compression); codecnum++) + if (m_compression[codecnum] == codec) + { + m_decompressor[codecnum]->configure(param, config); + return CHDERR_NONE; + } + return CHDERR_INVALID_PARAMETER; + } + + // return any errors + catch (chd_error &err) + { + return err; + } +} + + +//------------------------------------------------- +// error_string - return an error string for +// the given CHD error +//------------------------------------------------- + +const char *chd_file::error_string(chd_error err) +{ + switch (err) + { + case CHDERR_NONE: return "no error"; + case CHDERR_NO_INTERFACE: return "no drive interface"; + case CHDERR_OUT_OF_MEMORY: return "out of memory"; + case CHDERR_NOT_OPEN: return "file not open"; + case CHDERR_ALREADY_OPEN: return "file already open"; + case CHDERR_INVALID_FILE: return "invalid file"; + case CHDERR_INVALID_PARAMETER: return "invalid parameter"; + case CHDERR_INVALID_DATA: return "invalid data"; + case CHDERR_FILE_NOT_FOUND: return "file not found"; + case CHDERR_REQUIRES_PARENT: return "requires parent"; + case CHDERR_FILE_NOT_WRITEABLE: return "file not writeable"; + case CHDERR_READ_ERROR: return "read error"; + case CHDERR_WRITE_ERROR: return "write error"; + case CHDERR_CODEC_ERROR: return "codec error"; + case CHDERR_INVALID_PARENT: return "invalid parent"; + case CHDERR_HUNK_OUT_OF_RANGE: return "hunk out of range"; + case CHDERR_DECOMPRESSION_ERROR: return "decompression error"; + case CHDERR_COMPRESSION_ERROR: return "compression error"; + case CHDERR_CANT_CREATE_FILE: return "can't create file"; + case CHDERR_CANT_VERIFY: return "can't verify file"; + case CHDERR_NOT_SUPPORTED: return "operation not supported"; + case CHDERR_METADATA_NOT_FOUND: return "can't find metadata"; + case CHDERR_INVALID_METADATA_SIZE: return "invalid metadata size"; + case CHDERR_UNSUPPORTED_VERSION: return "mismatched DIFF and CHD or unsupported CHD version"; + case CHDERR_VERIFY_INCOMPLETE: return "incomplete verify"; + case CHDERR_INVALID_METADATA: return "invalid metadata"; + case CHDERR_INVALID_STATE: return "invalid state"; + case CHDERR_OPERATION_PENDING: return "operation pending"; + case CHDERR_UNSUPPORTED_FORMAT: return "unsupported format"; + case CHDERR_UNKNOWN_COMPRESSION: return "unknown compression type"; + case CHDERR_WALKING_PARENT: return "currently examining parent"; + case CHDERR_COMPRESSING: return "currently compressing"; + default: return "undocumented error"; + } +} + + + +//************************************************************************** +// INTERNAL HELPERS +//************************************************************************** + +//------------------------------------------------- +// guess_unitbytes - for older CHD formats, take +// a guess at the bytes/unit based on metadata +//------------------------------------------------- + +UINT32 chd_file::guess_unitbytes() +{ + // look for hard disk metadata; if found, then the unit size == sector size + astring metadata; + int i0, i1, i2, i3; + if (read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE && sscanf(metadata, HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4) + return i3; + + // look for CD-ROM metadata; if found, then the unit size == CD frame size + if (read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE || + read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE || + read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE || + read_metadata(GDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE || + read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE) + return CD_FRAME_SIZE; + + // otherwise, just map 1:1 with the hunk size + return m_hunkbytes; +} + + +//------------------------------------------------- +// parse_v3_header - parse the header from a v3 +// file and configure core parameters +//------------------------------------------------- + +void chd_file::parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1) +{ + // verify header length + if (be_read(&rawheader[8], 4) != V3_HEADER_SIZE) + throw CHDERR_INVALID_FILE; + + // extract core info + m_logicalbytes = be_read(&rawheader[28], 8); + m_mapoffset = 120; + m_metaoffset = be_read(&rawheader[36], 8); + m_hunkbytes = be_read(&rawheader[76], 4); m_hunkcount = be_read(&rawheader[24], 4); // extract parent SHA-1 @@ -639,10 +1449,10 @@ void chd_file::parse_v3_header(UINT8 *rawheader, sha1_t &parentsha1) // determine compression switch (be_read(&rawheader[20], 4)) { - case 0: m_compression[0] = CHD_CODEC_NONE; break; - case 1: m_compression[0] = CHD_CODEC_ZLIB; break; - case 2: m_compression[0] = CHD_CODEC_ZLIB; break; - case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; + case 0: m_compression[0] = CHD_CODEC_NONE; break; + case 1: m_compression[0] = CHD_CODEC_ZLIB; break; + case 2: m_compression[0] = CHD_CODEC_ZLIB; break; + case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; default: throw CHDERR_UNKNOWN_COMPRESSION; } m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE; @@ -692,10 +1502,10 @@ void chd_file::parse_v4_header(UINT8 *rawheader, sha1_t &parentsha1) // determine compression switch (be_read(&rawheader[20], 4)) { - case 0: m_compression[0] = CHD_CODEC_NONE; break; - case 1: m_compression[0] = CHD_CODEC_ZLIB; break; - case 2: m_compression[0] = CHD_CODEC_ZLIB; break; - case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; + case 0: m_compression[0] = CHD_CODEC_NONE; break; + case 1: m_compression[0] = CHD_CODEC_ZLIB; break; + case 2: m_compression[0] = CHD_CODEC_ZLIB; break; + case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; default: throw CHDERR_UNKNOWN_COMPRESSION; } m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE; @@ -764,139 +1574,209 @@ void chd_file::parse_v5_header(UINT8 *rawheader, sha1_t &parentsha1) //------------------------------------------------- -// open_common - common path when opening an -// existing CHD file for input +// compress_v5_map - compress the v5 map and +// write it to the end of the file //------------------------------------------------- -chd_error chd_file::open_common(bool writeable) +chd_error chd_file::compress_v5_map() { - // wrap in try for proper error handling try { - // reads are always permitted - m_allow_reads = true; - - // read the raw header - UINT8 rawheader[MAX_HEADER_SIZE]; - file_read(0, rawheader, sizeof(rawheader)); + // first get a CRC-16 of the original rawmap + crc16_t mapcrc = crc16_creator::simple(m_rawmap, m_hunkcount * 12); + + // create a buffer to hold the RLE data + dynamic_buffer compression_rle(m_hunkcount); + UINT8 *dest = compression_rle; + + // use a huffman encoder for 16 different codes, maximum length is 8 bits + huffman_encoder<16, 8> encoder; + encoder.histo_reset(); + + // RLE-compress the compression type since we expect runs of the same + UINT32 max_self = 0; + UINT32 last_self = 0; + UINT64 max_parent = 0; + UINT64 last_parent = 0; + UINT32 max_complen = 0; + UINT8 lastcomp = 0; + int count = 0; + for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + { + UINT8 curcomp = m_rawmap[hunknum * 12 + 0]; - // verify the signature - if (memcmp(rawheader, "MComprHD", 8) != 0) - throw CHDERR_INVALID_FILE; + // promote self block references to more compact forms + if (curcomp == COMPRESSION_SELF) + { + UINT32 refhunk = be_read(&m_rawmap[hunknum * 12 + 4], 6); + if (refhunk == last_self) + curcomp = COMPRESSION_SELF_0; + else if (refhunk == last_self + 1) + curcomp = COMPRESSION_SELF_1; + else + max_self = MAX(max_self, refhunk); + last_self = refhunk; + } - // only allow writes to the most recent version - m_version = be_read(&rawheader[12], 4); - if (writeable && m_version < HEADER_VERSION) - throw CHDERR_UNSUPPORTED_VERSION; + // promote parent block references to more compact forms + else if (curcomp == COMPRESSION_PARENT) + { + UINT32 refunit = be_read(&m_rawmap[hunknum * 12 + 4], 6); + if (refunit == (UINT64(hunknum) * UINT64(m_hunkbytes)) / m_unitbytes) + curcomp = COMPRESSION_PARENT_SELF; + else if (refunit == last_parent) + curcomp = COMPRESSION_PARENT_0; + else if (refunit == last_parent + m_hunkbytes / m_unitbytes) + curcomp = COMPRESSION_PARENT_1; + else + max_parent = MAX(max_parent, refunit); + last_parent = refunit; + } - // read the header if we support it - sha1_t parentsha1 = sha1_t::null; - switch (m_version) - { - case 3: parse_v3_header(rawheader, parentsha1); break; - case 4: parse_v4_header(rawheader, parentsha1); break; - case 5: parse_v5_header(rawheader, parentsha1); break; - default: throw CHDERR_UNSUPPORTED_VERSION; - } + // track maximum compressed length + else //if (curcomp >= COMPRESSION_TYPE_0 && curcomp <= COMPRESSION_TYPE_3) + max_complen = MAX(max_complen, be_read(&m_rawmap[hunknum * 12 + 1], 3)); - if (writeable && !m_allow_writes) - throw CHDERR_FILE_NOT_WRITEABLE; + // track repeats + if (curcomp == lastcomp) + count++; - // make sure we have a parent if we need one (and don't if we don't) - if (parentsha1 != sha1_t::null) - { - if (m_parent == NULL) - m_parent_missing = true; - else if (m_parent->sha1() != parentsha1) - throw CHDERR_INVALID_PARENT; + // if no repeat, or we're at the end, flush it + if (curcomp != lastcomp || hunknum == m_hunkcount - 1) + { + while (count != 0) + { + if (count < 3) + encoder.histo_one(*dest++ = lastcomp), count--; + else if (count <= 3+15) + { + encoder.histo_one(*dest++ = COMPRESSION_RLE_SMALL); + encoder.histo_one(*dest++ = count - 3); + count = 0; + } + else + { + int this_count = MIN(count, 3+16+255); + encoder.histo_one(*dest++ = COMPRESSION_RLE_LARGE); + encoder.histo_one(*dest++ = (this_count - 3 - 16) >> 4); + encoder.histo_one(*dest++ = (this_count - 3 - 16) & 15); + count -= this_count; + } + } + if (curcomp != lastcomp) + encoder.histo_one(*dest++ = lastcomp = curcomp); + } } - else if (m_parent != NULL) - throw CHDERR_INVALID_PARAMETER; - - // finish opening the file - create_open_common(); - return CHDERR_NONE; - } - // handle errors by closing ourself - catch (chd_error &err) - { - close(); - return err; - } -} + // compute a tree and export it to the buffer + dynamic_buffer compressed(m_hunkcount * 6); + bitstream_out bitbuf(&compressed[16], compressed.count() - 16); + huffman_error err = encoder.compute_tree_from_histo(); + if (err != HUFFERR_NONE) + throw CHDERR_COMPRESSION_ERROR; + err = encoder.export_tree_rle(bitbuf); + if (err != HUFFERR_NONE) + throw CHDERR_COMPRESSION_ERROR; + + // encode the data + for (UINT8 *src = compression_rle; src < dest; src++) + encoder.encode_one(bitbuf, *src); + + // determine the number of bits we need to hold the a length + // and a hunk index + UINT8 lengthbits = bits_for_value(max_complen); + UINT8 selfbits = bits_for_value(max_self); + UINT8 parentbits = bits_for_value(max_parent); + + // for each compression type, output the relevant data + lastcomp = 0; + count = 0; + UINT8 *src = compression_rle; + UINT64 firstoffs = 0; + for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + { + UINT8 *rawmap = &m_rawmap[hunknum * 12]; + UINT32 length = be_read(&rawmap[1], 3); + UINT64 offset = be_read(&rawmap[4], 6); + UINT16 crc = be_read(&rawmap[10], 2); -//------------------------------------------------- -// create_open_common - common code for handling -// creation and opening of a file -//------------------------------------------------- + // if no count remaining, fetch the next entry + if (count == 0) + { + UINT8 val = *src++; + if (val == COMPRESSION_RLE_SMALL) + count = 2 + *src++; + else if (val == COMPRESSION_RLE_LARGE) + count = 2 + 16 + (*src++ << 4), count += *src++; + else + lastcomp = val; + } + else + count--; -void chd_file::create_open_common() -{ - // verify the compression types and initialize the codecs - for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_compression); decompnum++) - { - m_decompressor[decompnum] = chd_codec_list::new_decompressor(m_compression[decompnum], *this); - if (m_decompressor[decompnum] == NULL && m_compression[decompnum] != 0) - throw CHDERR_UNKNOWN_COMPRESSION; - } + // output additional data needed for this entry + switch (lastcomp) + { + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + assert(length < (1 << lengthbits)); + bitbuf.write(length, lengthbits); + bitbuf.write(crc, 16); + if (firstoffs == 0) + firstoffs = offset; + break; - // read the map; v5+ compressed drives need to read and decompress their map - m_rawmap.resize(m_hunkcount * m_mapentrybytes); - if (m_version >= 5 && compressed()) - decompress_v5_map(); - else - file_read(m_mapoffset, m_rawmap, m_rawmap.count()); + case COMPRESSION_NONE: + bitbuf.write(crc, 16); + if (firstoffs == 0) + firstoffs = offset; + break; - // allocate the temporary compressed buffer and a buffer for caching - m_compressed.resize(m_hunkbytes); - m_cache.resize(m_hunkbytes); -} + case COMPRESSION_SELF: + assert(offset < (UINT64(1) << selfbits)); + bitbuf.write(offset, selfbits); + break; + case COMPRESSION_PARENT: + assert(offset < (UINT64(1) << parentbits)); + bitbuf.write(offset, parentbits); + break; -//------------------------------------------------- -// metadata_find - find a metadata entry -//------------------------------------------------- + case COMPRESSION_SELF_0: + case COMPRESSION_SELF_1: + case COMPRESSION_PARENT_SELF: + case COMPRESSION_PARENT_0: + case COMPRESSION_PARENT_1: + break; + } + } -bool chd_file::metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume) -{ - // start at the beginning unless we're resuming a previous search - if (!resume) - { - metaentry.offset = m_metaoffset; - metaentry.prev = 0; - } - else - { - metaentry.prev = metaentry.offset; - metaentry.offset = metaentry.next; + // write the map header + UINT32 complen = bitbuf.flush(); + assert(!bitbuf.overflow()); + be_write(&compressed[0], complen, 4); + be_write(&compressed[4], firstoffs, 6); + be_write(&compressed[10], mapcrc, 2); + compressed[12] = lengthbits; + compressed[13] = selfbits; + compressed[14] = parentbits; + compressed[15] = 0; + + // write the result + m_mapoffset = file_append(compressed, complen + 16); + + // then write the map offset + UINT8 rawbuf[sizeof(UINT64)]; + be_write(rawbuf, m_mapoffset, 8); + file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf)); + return CHDERR_NONE; } - - // loop until we run out of options - while (metaentry.offset != 0) + catch (chd_error &err) { - // read the raw header - UINT8 raw_meta_header[METADATA_HEADER_SIZE]; - file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); - - // extract the data - metaentry.metatag = be_read(&raw_meta_header[0], 4); - metaentry.flags = raw_meta_header[4]; - metaentry.length = be_read(&raw_meta_header[5], 3); - metaentry.next = be_read(&raw_meta_header[8], 8); - - // if we got a match, proceed - if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag) - if (metaindex-- == 0) - return true; - - // no match, fetch the next link - metaentry.prev = metaentry.offset; - metaentry.offset = metaentry.next; + return err; } - - // if we get here, we didn't find it - return false; } @@ -1017,100 +1897,854 @@ void chd_file::decompress_v5_map() throw CHDERR_DECOMPRESSION_ERROR; } + //------------------------------------------------- -// sha1 - return our SHA1 value +// create_common - command path when creating a +// new CHD file //------------------------------------------------- -sha1_t chd_file::sha1() +chd_error chd_file::create_common() { + // wrap in try for proper error handling try { - // read the big-endian version - UINT8 rawbuf[sizeof(sha1_t)]; - file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); - return be_read_sha1(rawbuf); + m_version = HEADER_VERSION; + m_metaoffset = 0; + + // if we have a parent, it must be V3 or later + if (m_parent != NULL && m_parent->version() < 3) + throw CHDERR_UNSUPPORTED_VERSION; + + // must be an even number of units per hunk + if (m_hunkbytes % m_unitbytes != 0) + throw CHDERR_INVALID_PARAMETER; + if (m_parent != NULL && m_unitbytes != m_parent->unit_bytes()) + throw CHDERR_INVALID_PARAMETER; + + // verify the compression types + bool found_zero = false; + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compression); codecnum++) + { + // once we hit an empty slot, all later slots must be empty as well + if (m_compression[codecnum] == CHD_CODEC_NONE) + found_zero = true; + else if (found_zero) + throw CHDERR_INVALID_PARAMETER; + else if (!chd_codec_list::codec_exists(m_compression[codecnum])) + throw CHDERR_UNKNOWN_COMPRESSION; + } + + // create our V5 header + UINT8 rawheader[V5_HEADER_SIZE]; + memcpy(&rawheader[0], "MComprHD", 8); + be_write(&rawheader[8], V5_HEADER_SIZE, 4); + be_write(&rawheader[12], m_version, 4); + be_write(&rawheader[16], m_compression[0], 4); + be_write(&rawheader[20], m_compression[1], 4); + be_write(&rawheader[24], m_compression[2], 4); + be_write(&rawheader[28], m_compression[3], 4); + be_write(&rawheader[32], m_logicalbytes, 8); + be_write(&rawheader[40], compressed() ? 0 : V5_HEADER_SIZE, 8); + be_write(&rawheader[48], m_metaoffset, 8); + be_write(&rawheader[56], m_hunkbytes, 4); + be_write(&rawheader[60], m_unitbytes, 4); + be_write_sha1(&rawheader[64], sha1_t::null); + be_write_sha1(&rawheader[84], sha1_t::null); + be_write_sha1(&rawheader[104], (m_parent != NULL) ? m_parent->sha1() : sha1_t::null); + + // write the resulting header + file_write(0, rawheader, sizeof(rawheader)); + + // parse it back out to set up fields appropriately + sha1_t parentsha1; + parse_v5_header(rawheader, parentsha1); + + // writes are obviously permitted; reads only if uncompressed + m_allow_writes = true; + m_allow_reads = !compressed(); + + // write out the map (if not compressed) + if (!compressed()) + { + UINT32 mapsize = m_mapentrybytes * m_hunkcount; + UINT8 buffer[4096] = { 0 }; + UINT64 offset = m_mapoffset; + while (mapsize != 0) + { + UINT32 bytes_to_write = MIN(mapsize, sizeof(buffer)); + file_write(offset, buffer, bytes_to_write); + offset += bytes_to_write; + mapsize -= bytes_to_write; + } + } + + // finish opening the file + create_open_common(); } - catch (chd_error &) + + // handle errors by closing ourself + catch (chd_error &err) { - // on failure, return NULL - return sha1_t::null; + close(); + return err; } + catch (...) + { + close(); + throw; + } + return CHDERR_NONE; } //------------------------------------------------- -// raw_sha1 - return our raw SHA1 value +// open_common - common path when opening an +// existing CHD file for input //------------------------------------------------- -sha1_t chd_file::raw_sha1() +chd_error chd_file::open_common(bool writeable) { + // wrap in try for proper error handling try { - // determine offset within the file for data-only - if (m_rawsha1_offset == 0) + // reads are always permitted + m_allow_reads = true; + + // read the raw header + UINT8 rawheader[MAX_HEADER_SIZE]; + file_read(0, rawheader, sizeof(rawheader)); + + // verify the signature + if (memcmp(rawheader, "MComprHD", 8) != 0) + throw CHDERR_INVALID_FILE; + + // only allow writes to the most recent version + m_version = be_read(&rawheader[12], 4); + if (writeable && m_version < HEADER_VERSION) throw CHDERR_UNSUPPORTED_VERSION; - // read the big-endian version - UINT8 rawbuf[sizeof(sha1_t)]; - file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf)); - return be_read_sha1(rawbuf); + // read the header if we support it + sha1_t parentsha1 = sha1_t::null; + switch (m_version) + { + case 3: parse_v3_header(rawheader, parentsha1); break; + case 4: parse_v4_header(rawheader, parentsha1); break; + case 5: parse_v5_header(rawheader, parentsha1); break; + default: throw CHDERR_UNSUPPORTED_VERSION; + } + + if (writeable && !m_allow_writes) + throw CHDERR_FILE_NOT_WRITEABLE; + + // make sure we have a parent if we need one (and don't if we don't) + if (parentsha1 != sha1_t::null) + { + if (m_parent == NULL) + m_parent_missing = true; + else if (m_parent->sha1() != parentsha1) + throw CHDERR_INVALID_PARENT; + } + else if (m_parent != NULL) + throw CHDERR_INVALID_PARAMETER; + + // finish opening the file + create_open_common(); + return CHDERR_NONE; } - catch (chd_error &) + + // handle errors by closing ourself + catch (chd_error &err) { - // on failure, return NULL - return sha1_t::null; + close(); + return err; } } //------------------------------------------------- -// parent_sha1 - return our parent's SHA1 value +// create_open_common - common code for handling +// creation and opening of a file //------------------------------------------------- -sha1_t chd_file::parent_sha1() +void chd_file::create_open_common() { - try + // verify the compression types and initialize the codecs + for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_compression); decompnum++) { - // determine offset within the file - if (m_parentsha1_offset == 0) - throw CHDERR_UNSUPPORTED_VERSION; + m_decompressor[decompnum] = chd_codec_list::new_decompressor(m_compression[decompnum], *this); + if (m_decompressor[decompnum] == NULL && m_compression[decompnum] != 0) + throw CHDERR_UNKNOWN_COMPRESSION; + } - // read the big-endian version - UINT8 rawbuf[sizeof(sha1_t)]; - file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); - return be_read_sha1(rawbuf); + // read the map; v5+ compressed drives need to read and decompress their map + m_rawmap.resize(m_hunkcount * m_mapentrybytes); + if (m_version >= 5 && compressed()) + decompress_v5_map(); + else + file_read(m_mapoffset, m_rawmap, m_rawmap.count()); + + // allocate the temporary compressed buffer and a buffer for caching + m_compressed.resize(m_hunkbytes); + m_cache.resize(m_hunkbytes); +} + + +//------------------------------------------------- +// verify_proper_compression_append - verify that +// the given hunk is a proper candidate for +// appending to a compressed CHD +//------------------------------------------------- + +void chd_file::verify_proper_compression_append(UINT32 hunknum) +{ + // punt if no file + if (m_file == NULL) + throw CHDERR_NOT_OPEN; + + // return an error if out of range + if (hunknum >= m_hunkcount) + throw CHDERR_HUNK_OUT_OF_RANGE; + + // if not writeable, fail + if (!m_allow_writes) + throw CHDERR_FILE_NOT_WRITEABLE; + + // compressed writes only via this interface + if (!compressed()) + throw CHDERR_FILE_NOT_WRITEABLE; + + // only permitted to write new blocks + UINT8 *rawmap = &m_rawmap[hunknum * 12]; + if (rawmap[0] != 0xff) + throw CHDERR_COMPRESSION_ERROR; + + // if this isn't the first block, only permitted to write immediately + // after the previous one + if (hunknum != 0 && rawmap[-12] == 0xff) + throw CHDERR_COMPRESSION_ERROR; +} + + +//------------------------------------------------- +// hunk_write_compressed - write a hunk to a +// compressed CHD, discovering the best +// technique +//------------------------------------------------- + +void chd_file::hunk_write_compressed(UINT32 hunknum, INT8 compression, const UINT8 *compressed, UINT32 complength, crc16_t crc16) +{ + // verify that we are appending properly to a compressed file + verify_proper_compression_append(hunknum); + + // write the final result + UINT64 offset = file_append(compressed, complength); + + // update the map entry + UINT8 *rawmap = &m_rawmap[hunknum * 12]; + rawmap[0] = (compression == -1) ? COMPRESSION_NONE : compression; + be_write(&rawmap[1], complength, 3); + be_write(&rawmap[4], offset, 6); + be_write(&rawmap[10], crc16, 2); +} + + +//------------------------------------------------- +// hunk_copy_from_self - mark a hunk as being a +// copy of another hunk in the same CHD +//------------------------------------------------- + +void chd_file::hunk_copy_from_self(UINT32 hunknum, UINT32 otherhunk) +{ + // verify that we are appending properly to a compressed file + verify_proper_compression_append(hunknum); + + // only permitted to reference prior hunks + if (otherhunk >= hunknum) + throw CHDERR_INVALID_PARAMETER; + + // update the map entry + UINT8 *rawmap = &m_rawmap[hunknum * 12]; + rawmap[0] = COMPRESSION_SELF; + be_write(&rawmap[1], 0, 3); + be_write(&rawmap[4], otherhunk, 6); + be_write(&rawmap[10], 0, 2); +} + + +//------------------------------------------------- +// hunk_copy_from_parent - mark a hunk as being a +// copy of a hunk from a parent CHD +//------------------------------------------------- + +void chd_file::hunk_copy_from_parent(UINT32 hunknum, UINT64 parentunit) +{ + // verify that we are appending properly to a compressed file + verify_proper_compression_append(hunknum); + + // update the map entry + UINT8 *rawmap = &m_rawmap[hunknum * 12]; + rawmap[0] = COMPRESSION_PARENT; + be_write(&rawmap[1], 0, 3); + be_write(&rawmap[4], parentunit, 6); + be_write(&rawmap[10], 0, 2); +} + + +//------------------------------------------------- +// metadata_find - find a metadata entry +//------------------------------------------------- + +bool chd_file::metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume) +{ + // start at the beginning unless we're resuming a previous search + if (!resume) + { + metaentry.offset = m_metaoffset; + metaentry.prev = 0; } - catch (chd_error &) + else { - // on failure, return NULL - return sha1_t::null; + metaentry.prev = metaentry.offset; + metaentry.offset = metaentry.next; + } + + // loop until we run out of options + while (metaentry.offset != 0) + { + // read the raw header + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); + + // extract the data + metaentry.metatag = be_read(&raw_meta_header[0], 4); + metaentry.flags = raw_meta_header[4]; + metaentry.length = be_read(&raw_meta_header[5], 3); + metaentry.next = be_read(&raw_meta_header[8], 8); + + // if we got a match, proceed + if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag) + if (metaindex-- == 0) + return true; + + // no match, fetch the next link + metaentry.prev = metaentry.offset; + metaentry.offset = metaentry.next; + } + + // if we get here, we didn't find it + return false; +} + + +//------------------------------------------------- +// metadata_set_previous_next - set the 'next' +// offset of a piece of metadata +//------------------------------------------------- + +void chd_file::metadata_set_previous_next(UINT64 prevoffset, UINT64 nextoffset) +{ + UINT64 offset = 0; + + // if we were the first entry, make the next entry the first + if (prevoffset == 0) + { + offset = m_metaoffset_offset; + m_metaoffset = nextoffset; } + + // otherwise, update the link in the previous header + else + offset = prevoffset + 8; + + // create a big-endian version + UINT8 rawbuf[sizeof(UINT64)]; + be_write(rawbuf, nextoffset, 8); + + // write to the header and update our local copy + file_write(offset, rawbuf, sizeof(rawbuf)); +} + + +//------------------------------------------------- +// metadata_update_hash - compute the SHA1 +// hash of all metadata that requests it +//------------------------------------------------- + +void chd_file::metadata_update_hash() +{ + // only works for V4 and above, and only for compressed CHDs + if (m_version < 4 || !compressed()) + return; + + // compute the new overall hash + sha1_t fullsha1 = compute_overall_sha1(raw_sha1()); + + // create a big-endian version + UINT8 rawbuf[sizeof(sha1_t)]; + be_write_sha1(&rawbuf[0], fullsha1); + + // write to the header + file_write(m_sha1_offset, rawbuf, sizeof(rawbuf)); +} + + +//------------------------------------------------- +// metadata_hash_compare - compare two hash +// entries +//------------------------------------------------- + +int CLIB_DECL chd_file::metadata_hash_compare(const void *elem1, const void *elem2) +{ + return memcmp(elem1, elem2, sizeof(metadata_hash)); } + + //************************************************************************** -// CHD FILE MANAGEMENT +// CHD COMPRESSOR //************************************************************************** //------------------------------------------------- -// chd_file - constructor +// chd_file_compressor - constructor //------------------------------------------------- -chd_file::chd_file() - : m_file(NULL), - m_owns_file(false) +chd_file_compressor::chd_file_compressor() + : m_walking_parent(false), + m_total_in(0), + m_total_out(0), + m_read_queue(NULL), + m_read_queue_offset(0), + m_read_done_offset(0), + m_read_error(false), + m_work_queue(NULL), + m_write_hunk(0) +{ + // zap arrays + memset(m_codecs, 0, sizeof(m_codecs)); + + // allocate work queues + m_read_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_IO); + m_work_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_MULTI); +} + + +//------------------------------------------------- +// ~chd_file_compressor - destructor +//------------------------------------------------- + +chd_file_compressor::~chd_file_compressor() +{ + // free the work queues + osd_work_queue_free(m_read_queue); + osd_work_queue_free(m_work_queue); + + // delete allocated arrays + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_codecs); codecnum++) + delete m_codecs[codecnum]; +} + + +//------------------------------------------------- +// compress_begin - initiate compression +//------------------------------------------------- + +void chd_file_compressor::compress_begin() { // reset state - memset(m_decompressor, 0, sizeof(m_decompressor)); - close(); + m_walking_parent = (m_parent != NULL); + m_total_in = 0; + m_total_out = 0; + m_compsha1.reset(); + + // reset our maps + m_parent_map.reset(); + m_current_map.reset(); + + // reset read state + m_read_queue_offset = 0; + m_read_done_offset = 0; + m_read_error = false; + + // reset work item state + m_work_buffer.resize_and_clear(hunk_bytes() * (WORK_BUFFER_HUNKS + 1)); + m_compressed_buffer.resize(hunk_bytes() * WORK_BUFFER_HUNKS); + for (int itemnum = 0; itemnum < WORK_BUFFER_HUNKS; itemnum++) + { + work_item &item = m_work_item[itemnum]; + item.m_compressor = this; + item.m_data = m_work_buffer + hunk_bytes() * itemnum; + item.m_compressed = m_compressed_buffer + hunk_bytes() * itemnum; + item.m_hash.resize(hunk_bytes() / unit_bytes()); + } + + // initialize codec instances + for (int instance = 0; instance < ARRAY_LENGTH(m_codecs); instance++) + { + delete m_codecs[instance]; + m_codecs[instance] = new chd_compressor_group(*this, m_compression); + } + + // reset write state + m_write_hunk = 0; } //------------------------------------------------- -// ~chd_file - destructor +// compress_continue - continue compression //------------------------------------------------- -chd_file::~chd_file() +chd_error chd_file_compressor::compress_continue(double &progress, double &ratio) { - // close any open files - close(); + // if we got an error, return an error + if (m_read_error) + return CHDERR_READ_ERROR; + + // if done reading, queue some more + while (m_read_queue_offset < m_logicalbytes && osd_work_queue_items(m_read_queue) < 2) + { + // see if we have enough free work items to read the next half of a buffer + UINT32 startitem = m_read_queue_offset / hunk_bytes(); + UINT32 enditem = startitem + WORK_BUFFER_HUNKS / 2; + UINT32 curitem; + for (curitem = startitem; curitem < enditem; curitem++) + if (m_work_item[curitem % WORK_BUFFER_HUNKS].m_status != WS_READY) + break; + + // if it's not all clear, defer + if (curitem != enditem) + break; + + // if we're walking the parent, we want one more item to have cleared so we + // can read an extra hunk there + if (m_walking_parent && m_work_item[curitem % WORK_BUFFER_HUNKS].m_status != WS_READY) + break; + + // queue the next read + for (curitem = startitem; curitem < enditem; curitem++) + m_work_item[curitem % WORK_BUFFER_HUNKS].m_status = WS_READING; + osd_work_item_queue(m_read_queue, async_read_static, this, WORK_ITEM_FLAG_AUTO_RELEASE); + m_read_queue_offset += WORK_BUFFER_HUNKS * hunk_bytes() / 2; + } + + // flush out any finished items + while (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status == WS_COMPLETE) + { + work_item &item = m_work_item[m_write_hunk % WORK_BUFFER_HUNKS]; + + // free any OSD work item + if (item.m_osd != NULL) + osd_work_item_release(item.m_osd); + item.m_osd = NULL; + + // for parent walking, just add to the hashmap + if (m_walking_parent) + { + UINT32 uph = hunk_bytes() / unit_bytes(); + UINT32 units = uph; + if (item.m_hunknum == hunk_count() - 1 || !compressed()) + units = 1; + for (UINT32 unit = 0; unit < units; unit++) + if (m_parent_map.find(item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1) == hashmap::NOT_FOUND) + m_parent_map.add(item.m_hunknum * uph + unit, item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1); + } + + // if we're uncompressed, use regular writes + else if (!compressed()) + { + chd_error err = write_hunk(item.m_hunknum, item.m_data); + if (err != CHDERR_NONE) + return err; + + // writes of all-0 data don't actually take space, so see if we count this + chd_codec_type codec = CHD_CODEC_NONE; + UINT32 complen; + hunk_info(item.m_hunknum, codec, complen); + if (codec == CHD_CODEC_NONE) + m_total_out += m_hunkbytes; + } + + // for compressing, process the result + else do + { + // first see if the hunk is in the parent or self maps + UINT64 selfhunk = m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); + if (selfhunk != hashmap::NOT_FOUND) + { + hunk_copy_from_self(item.m_hunknum, selfhunk); + break; + } + + // if not, see if it's in the parent map + if (m_parent != NULL) + { + UINT64 parentunit = m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); + if (parentunit != hashmap::NOT_FOUND) + { + hunk_copy_from_parent(item.m_hunknum, parentunit); + break; + } + } + + // otherwise, append it compressed and add to the self map + hunk_write_compressed(item.m_hunknum, item.m_compression, item.m_compressed, item.m_complen, item.m_hash[0].m_crc16); + m_total_out += item.m_complen; + m_current_map.add(item.m_hunknum, item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); + } while (0); + + // reset the item and advance + item.m_status = WS_READY; + m_write_hunk++; + + // if we hit the end, finalize + if (m_write_hunk == m_hunkcount) + { + // if this is just walking the parent, reset and get ready for compression + if (m_walking_parent) + { + m_walking_parent = false; + m_read_queue_offset = m_read_done_offset = 0; + m_write_hunk = 0; + for (int itemnum = 0; itemnum < WORK_BUFFER_HUNKS; itemnum++) + m_work_item[itemnum].m_status = WS_READY; + } + + // wait for all reads to finish and if we're compressed, write the final SHA1 and map + else + { + osd_work_queue_wait(m_read_queue, 30 * osd_ticks_per_second()); + if (!compressed()) + return CHDERR_NONE; + set_raw_sha1(m_compsha1.finish()); + return compress_v5_map(); + } + } + } + + // update progress and ratio + if (m_walking_parent) + progress = double(m_read_done_offset) / double(logical_bytes()); + else + progress = double(m_write_hunk) / double(m_hunkcount); + ratio = (m_total_in == 0) ? 1.0 : double(m_total_out) / double(m_total_in); + + // if we're waiting for work, wait + while (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE && m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd != NULL) + osd_work_item_wait(m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd, osd_ticks_per_second()); + + return m_walking_parent ? CHDERR_WALKING_PARENT : CHDERR_COMPRESSING; +} + + +//------------------------------------------------- +// async_walk_parent - handle asynchronous parent +// walking operations +//------------------------------------------------- + +void *chd_file_compressor::async_walk_parent_static(void *param, int threadid) +{ + work_item *item = reinterpret_cast(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(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(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; } diff --git a/archivers/chd/chd.h b/archivers/chd/chd.h index c548a34b..d19a23ad 100644 --- a/archivers/chd/chd.h +++ b/archivers/chd/chd.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** chd.h MAME Compressed Hunks of Data file format -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -45,6 +16,7 @@ #include "osdcore.h" #include "coretmpl.h" #include "astring.h" +#include "bitmap.h" #include "corefile.h" #include "hashing.h" #include "chdcodec.h" @@ -220,9 +192,9 @@ //************************************************************************** // pseudo-codecs returned by hunk_info -const chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk -const chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk -const chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat +const chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk +const chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk +const chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat // core types typedef UINT32 chd_metadata_tag; @@ -232,7 +204,7 @@ const chd_metadata_tag CHDMETATAG_WILDCARD = 0; const UINT32 CHDMETAINDEX_APPEND = ~0; // metadata flags -const UINT8 CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed +const UINT8 CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed // standard hard disk metadata const chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D'); @@ -253,7 +225,8 @@ const chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R'); extern const char *CDROM_TRACK_METADATA_FORMAT; const chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2'); extern const char *CDROM_TRACK_METADATA2_FORMAT; -const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T'); +const chd_metadata_tag GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T'); +const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C', 'H', 'G', 'D'); extern const char *GDROM_TRACK_METADATA_FORMAT; // standard A/V metadata @@ -329,6 +302,9 @@ public: chd_file(); virtual ~chd_file(); + // operators + operator core_file *() { return m_file; } + // getters bool opened() const { return (m_file != NULL); } UINT32 version() const { return m_version; } @@ -345,23 +321,41 @@ public: sha1_t parent_sha1(); chd_error hunk_info(UINT32 hunknum, chd_codec_type &compressor, UINT32 &compbytes); + // setters + void set_raw_sha1(sha1_t rawdata); + void set_parent_sha1(sha1_t parent); + + // file create + chd_error create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]); + chd_error create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 unitbytes, chd_codec_type compression[4]); + chd_error create(const TCHAR *filename, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent); + chd_error create(core_file &file, UINT64 logicalbytes, UINT32 hunkbytes, chd_codec_type compression[4], chd_file &parent); + // file open - chd_error open(const char *filename, bool writeable = false, chd_file *parent = NULL); - chd_error open(struct zfile *file, bool writeable = false, chd_file *parent = NULL); + chd_error open(const TCHAR *filename, bool writeable = false, chd_file *parent = NULL); + chd_error open(core_file &file, bool writeable = false, chd_file *parent = NULL); // file close void close(); // read/write chd_error read_hunk(UINT32 hunknum, void *buffer); + chd_error write_hunk(UINT32 hunknum, const void *buffer); chd_error read_units(UINT64 unitnum, void *buffer, UINT32 count = 1); + chd_error write_units(UINT64 unitnum, const void *buffer, UINT32 count = 1); chd_error read_bytes(UINT64 offset, void *buffer, UINT32 bytes); + chd_error write_bytes(UINT64 offset, const void *buffer, UINT32 bytes); // metadata management chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, astring &output); chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output); chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 &resultlen); chd_error read_metadata(chd_metadata_tag searchtag, UINT32 searchindex, dynamic_buffer &output, chd_metadata_tag &resulttag, UINT8 &resultflags); + chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen, UINT8 flags = CHD_MDFLAGS_CHECKSUM); + chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const astring &input, UINT8 flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input.cstr(), input.len() + 1, flags); } + chd_error write_metadata(chd_metadata_tag metatag, UINT32 metaindex, const dynamic_buffer &input, UINT8 flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input, input.count(), flags); } + chd_error delete_metadata(chd_metadata_tag metatag, UINT32 metaindex); + chd_error clone_all_metadata(chd_file &source); // hashing helper sha1_t compute_overall_sha1(sha1_t rawsha1); @@ -403,45 +397,184 @@ private: bool metadata_find(chd_metadata_tag metatag, INT32 metaindex, metadata_entry &metaentry, bool resume = false); void metadata_set_previous_next(UINT64 prevoffset, UINT64 nextoffset); void metadata_update_hash(); -// static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2); + static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2); // file characteristics - struct zfile * m_file; // handle to the open core file - bool m_owns_file; // flag indicating if this file should be closed on chd_close() - bool m_allow_reads; // permit reads from this CHD? - bool m_allow_writes; // permit writes to this CHD? + core_file * m_file; // handle to the open core file + bool m_owns_file; // flag indicating if this file should be closed on chd_close() + bool m_allow_reads; // permit reads from this CHD? + bool m_allow_writes; // permit writes to this CHD? // core parameters from the header - UINT32 m_version; // version of the header - UINT64 m_logicalbytes; // logical size of the raw CHD data in bytes - UINT64 m_mapoffset; // offset of map - UINT64 m_metaoffset; // offset to first metadata bit - UINT32 m_hunkbytes; // size of each raw hunk in bytes - UINT32 m_hunkcount; // number of hunks represented - UINT32 m_unitbytes; // size of each unit in bytes - UINT64 m_unitcount; // number of units represented - chd_codec_type m_compression[4]; // array of compression types used - chd_file * m_parent; // pointer to parent file, or NULL if none - bool m_parent_missing; // are we missing our parent? + UINT32 m_version; // version of the header + UINT64 m_logicalbytes; // logical size of the raw CHD data in bytes + UINT64 m_mapoffset; // offset of map + UINT64 m_metaoffset; // offset to first metadata bit + UINT32 m_hunkbytes; // size of each raw hunk in bytes + UINT32 m_hunkcount; // number of hunks represented + UINT32 m_unitbytes; // size of each unit in bytes + UINT64 m_unitcount; // number of units represented + chd_codec_type m_compression[4]; // array of compression types used + chd_file * m_parent; // pointer to parent file, or NULL if none + bool m_parent_missing; // are we missing our parent? // key offsets within the header - UINT64 m_mapoffset_offset; // offset of map offset field - UINT64 m_metaoffset_offset;// offset of metaoffset field - UINT64 m_sha1_offset; // offset of SHA1 field - UINT64 m_rawsha1_offset; // offset of raw SHA1 field - UINT64 m_parentsha1_offset;// offset of paren SHA1 field + UINT64 m_mapoffset_offset; // offset of map offset field + UINT64 m_metaoffset_offset;// offset of metaoffset field + UINT64 m_sha1_offset; // offset of SHA1 field + UINT64 m_rawsha1_offset; // offset of raw SHA1 field + UINT64 m_parentsha1_offset;// offset of paren SHA1 field // map information - UINT32 m_mapentrybytes; // length of each entry in a map - dynamic_buffer m_rawmap; // raw map data + UINT32 m_mapentrybytes; // length of each entry in a map + dynamic_buffer m_rawmap; // raw map data // compression management - chd_decompressor * m_decompressor[4]; // array of decompression codecs - dynamic_buffer m_compressed; // temporary buffer for compressed data + chd_decompressor * m_decompressor[4]; // array of decompression codecs + dynamic_buffer m_compressed; // temporary buffer for compressed data // caching - dynamic_buffer m_cache; // single-hunk cache for partial reads/writes - UINT32 m_cachehunk; // which hunk is in the cache? + dynamic_buffer m_cache; // single-hunk cache for partial reads/writes + UINT32 m_cachehunk; // which hunk is in the cache? }; -#endif // __CHD_H__ \ No newline at end of file + +// ======================> chd_file_compressor + +// class for creating a new compressed CHD +class chd_file_compressor : public chd_file +{ +public: + // construction/destruction + chd_file_compressor(); + virtual ~chd_file_compressor(); + + // compression management + void compress_begin(); + chd_error compress_continue(double &progress, double &ratio); + +protected: + // required override: read more data + virtual UINT32 read_data(void *dest, UINT64 offset, UINT32 length) = 0; + +private: + // hash map for looking up values + class hashmap + { + public: + // construction/destruction + hashmap(); + ~hashmap(); + + // operations + void reset(); + UINT64 find(crc16_t crc16, sha1_t sha1); + void add(UINT64 itemnum, crc16_t crc16, sha1_t sha1); + + // constants + static const UINT64 NOT_FOUND = ~UINT64(0); + private: + // internal entry + struct entry_t + { + entry_t * m_next; // next entry in list + UINT64 m_itemnum; // item number + sha1_t m_sha1; // SHA-1 of the block + }; + + // block of entries + struct entry_block + { + entry_block(entry_block *prev) + : m_next(prev), m_nextalloc(0) { } + + entry_block * m_next; // next block in list + UINT32 m_nextalloc; // next to be allocated + entry_t m_array[16384]; // array of entries + }; + + // internal state + entry_t * m_map[65536]; // map, hashed by CRC-16 + entry_block * m_block_list; // list of allocated blocks + }; + + // status of a given work item + enum work_status + { + WS_READY = 0, + WS_READING, + WS_QUEUED, + WS_COMPLETE + }; + + // a CRC-16/SHA-1 pair + struct hash_pair + { + crc16_t m_crc16; // calculated CRC-16 + sha1_t m_sha1; // calculated SHA-1 + }; + + // a single work item + struct work_item + { + work_item() + : m_osd(NULL) + , m_compressor(NULL) + , m_status(WS_READY) + , m_data(NULL) + , m_compressed(NULL) + , m_complen(0) + , m_compression(0) + , m_codecs(NULL) + { } + + osd_work_item * m_osd; // OSD work item running on this block + chd_file_compressor *m_compressor; // pointer back to the compressor + volatile work_status m_status; // current status of this item + UINT32 m_hunknum; // number of the hunk we're working on + UINT8 * m_data; // pointer to the data we are working on + UINT8 * m_compressed; // pointer to the compressed data + UINT32 m_complen; // compressed data length + INT8 m_compression; // type of compression used + chd_compressor_group *m_codecs; // codec instance + dynamic_array m_hash; // array of hashes + }; + + // internal helpers + static void *async_walk_parent_static(void *param, int threadid); + void async_walk_parent(work_item &item); + static void *async_compress_hunk_static(void *param, int threadid); + void async_compress_hunk(work_item &item, int threadid); + static void *async_read_static(void *param, int threadid); + void async_read(); + + // current compression status + bool m_walking_parent; // are we building the parent map? + UINT64 m_total_in; // total bytes in + UINT64 m_total_out; // total bytes out + sha1_creator m_compsha1; // running SHA-1 on raw data + + // hash lookup maps + hashmap m_parent_map; // hash map for parent + hashmap m_current_map; // hash map for current + + // read I/O thread + osd_work_queue * m_read_queue; // work queue for reading + UINT64 m_read_queue_offset;// next offset to enqueue + UINT64 m_read_done_offset; // next offset that will complete + bool m_read_error; // error during reading? + + // work item thread + static const int WORK_BUFFER_HUNKS = 256; + osd_work_queue * m_work_queue; // queue for doing work on other threads + dynamic_buffer m_work_buffer; // buffer containing hunk data to work on + dynamic_buffer m_compressed_buffer;// buffer containing compressed data + work_item m_work_item[WORK_BUFFER_HUNKS]; // status of each hunk + chd_compressor_group * m_codecs[WORK_MAX_THREADS]; // codecs to use + + // output state + UINT32 m_write_hunk; // next hunk to write +}; + + +#endif // __CHD_H__ diff --git a/archivers/chd/chdcd.cpp b/archivers/chd/chdcd.cpp new file mode 100644 index 00000000..8690cc65 --- /dev/null +++ b/archivers/chd/chdcd.cpp @@ -0,0 +1,1180 @@ +/*************************************************************************** + + TOC parser for CHD compression frontend + Handles CDRDAO .toc, CDRWIN .cue, Nero .nrg, and Sega GDROM .gdi + + Copyright Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include +#include +#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;icdtoc.numtrks; track++) + { + if (loglba < file->cdtoc.tracks[track + 1].logframeofs) + { + // is this a no-pregap-data track? compensate for the logical offset pointing to the "wrong" sector. + if ((file->cdtoc.tracks[track].pgdatasize == 0) && (loglba > file->cdtoc.tracks[track].pregap)) + { + loglba -= file->cdtoc.tracks[track].pregap; + } + + // convert to physical and proceed + physlba = file->cdtoc.tracks[track].physframeofs + (loglba - file->cdtoc.tracks[track].logframeofs); + chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs; + tracknum = track; + return chdlba; + } + } + + return loglba; +} /*************************************************************************** BASE FUNCTIONALITY ***************************************************************************/ - #if 0 -cdrom_file *cdrom_open(const char *inputfile) +cdrom_file *cdrom_open(const TCHAR *inputfile) { int i; cdrom_file *file; - UINT32 physofs; + UINT32 physofs, logofs; /* allocate memory for the CD-ROM file */ file = new cdrom_file(); @@ -169,35 +169,51 @@ cdrom_file *cdrom_open(const char *inputfile) } } /* calculate the starting frame for each track, keeping in mind that CHDMAN - pads tracks out with extra frames to fit 4-frame size boundries - */ - physofs = 0; + pads tracks out with extra frames to fit 4-frame size boundries + */ + physofs = logofs = 0; for (i = 0; i < file->cdtoc.numtrks; i++) { file->cdtoc.tracks[i].physframeofs = physofs; file->cdtoc.tracks[i].chdframeofs = 0; + file->cdtoc.tracks[i].logframeofs = logofs; - physofs += file->cdtoc.tracks[i].frames; + // if the pregap sectors aren't in the track, add them to the track's logical length + if (file->cdtoc.tracks[i].pgdatasize == 0) + { + logofs += file->cdtoc.tracks[i].pregap; + } - LOG(("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d physofs %d chdofs %d\n", i+1, - file->cdtoc.tracks[i].trktype, - file->cdtoc.tracks[i].subtype, - file->cdtoc.tracks[i].datasize, - file->cdtoc.tracks[i].subsize, - file->cdtoc.tracks[i].frames, - file->cdtoc.tracks[i].extraframes, - file->cdtoc.tracks[i].physframeofs, - file->cdtoc.tracks[i].chdframeofs)); + // postgap adds to the track length + logofs += file->cdtoc.tracks[i].postgap; + + physofs += file->cdtoc.tracks[i].frames; + logofs += file->cdtoc.tracks[i].frames; + +/* printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d\n", i+1, + file->cdtoc.tracks[i].trktype, + file->cdtoc.tracks[i].subtype, + file->cdtoc.tracks[i].datasize, + file->cdtoc.tracks[i].subsize, + file->cdtoc.tracks[i].frames, + file->cdtoc.tracks[i].extraframes, + file->cdtoc.tracks[i].pregap, + file->cdtoc.tracks[i].pgtype, + file->cdtoc.tracks[i].pgdatasize, + file->cdtoc.tracks[i].postgap, + file->cdtoc.tracks[i].logframeofs, + file->cdtoc.tracks[i].physframeofs, + file->cdtoc.tracks[i].chdframeofs);*/ } /* fill out dummy entries for the last track to help our search */ file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].logframeofs = logofs; file->cdtoc.tracks[i].chdframeofs = 0; return file; } #endif - /*------------------------------------------------- cdrom_open - "open" a CD-ROM file from an already-opened CHD file @@ -207,7 +223,7 @@ cdrom_file *cdrom_open(chd_file *chd) { int i; cdrom_file *file; - UINT32 physofs, chdofs; + UINT32 physofs, chdofs, logofs; chd_error err; /* punt if no CHD */ @@ -239,31 +255,48 @@ cdrom_file *cdrom_open(chd_file *chd) LOG(("CD has %d tracks\n", file->cdtoc.numtrks)); /* calculate the starting frame for each track, keeping in mind that CHDMAN - pads tracks out with extra frames to fit 4-frame size boundries - */ - physofs = chdofs = 0; + pads tracks out with extra frames to fit 4-frame size boundries + */ + physofs = chdofs = logofs = 0; for (i = 0; i < file->cdtoc.numtrks; i++) { file->cdtoc.tracks[i].physframeofs = physofs; file->cdtoc.tracks[i].chdframeofs = chdofs; + file->cdtoc.tracks[i].logframeofs = logofs; + + // if the pregap sectors aren't in the track, add them to the track's logical length + if (file->cdtoc.tracks[i].pgdatasize == 0) + { + logofs += file->cdtoc.tracks[i].pregap; + } + + // postgap counts against the next track + logofs += file->cdtoc.tracks[i].postgap; physofs += file->cdtoc.tracks[i].frames; chdofs += file->cdtoc.tracks[i].frames; chdofs += file->cdtoc.tracks[i].extraframes; - - LOG(("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d physofs %d chdofs %d\n", i+1, - file->cdtoc.tracks[i].trktype, - file->cdtoc.tracks[i].subtype, - file->cdtoc.tracks[i].datasize, - file->cdtoc.tracks[i].subsize, - file->cdtoc.tracks[i].frames, - file->cdtoc.tracks[i].extraframes, - file->cdtoc.tracks[i].physframeofs, - file->cdtoc.tracks[i].chdframeofs)); + logofs += file->cdtoc.tracks[i].frames; + +/* printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d\n", i+1, + file->cdtoc.tracks[i].trktype, + file->cdtoc.tracks[i].subtype, + file->cdtoc.tracks[i].datasize, + file->cdtoc.tracks[i].subsize, + file->cdtoc.tracks[i].frames, + file->cdtoc.tracks[i].extraframes, + file->cdtoc.tracks[i].pregap, + file->cdtoc.tracks[i].pgtype, + file->cdtoc.tracks[i].pgdatasize, + file->cdtoc.tracks[i].postgap, + file->cdtoc.tracks[i].logframeofs, + file->cdtoc.tracks[i].physframeofs, + file->cdtoc.tracks[i].chdframeofs);*/ } /* fill out dummy entries for the last track to help our search */ file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].logframeofs = logofs; file->cdtoc.tracks[i].chdframeofs = chdofs; return file; @@ -283,7 +316,7 @@ void cdrom_close(cdrom_file *file) { for (int i = 0; i < file->cdtoc.numtrks; i++) { - zfile_fclose(file->fhandle[i]); + core_fclose(file->fhandle[i]); } } @@ -296,24 +329,46 @@ void cdrom_close(cdrom_file *file) CORE READ ACCESS ***************************************************************************/ -chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length) +chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 lbasector, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length) { + chd_error result = CHDERR_NONE; + bool needswap = false; + + // if this is pregap info that isn't actually in the file, just return blank data + if ((file->cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < (file->cdtoc.tracks[tracknum].logframeofs + file->cdtoc.tracks[tracknum].pregap))) + { +// printf("PG missing sector: LBA %d, trklog %d\n", lbasector, file->cdtoc.tracks[tracknum].logframeofs); + memset(dest, 0, length); + return result; + } + // if a CHD, just read if (file->chd != NULL) - return file->chd->read_bytes(UINT64(chdsector) * UINT64(CD_FRAME_SIZE) + startoffs, dest, length); -#if 0 - // else read from the appropriate file - core_file *srcfile = file->fhandle[tracknum]; + { + result = file->chd->read_bytes(UINT64(chdsector) * UINT64(CD_FRAME_SIZE) + startoffs, dest, length); + /* swap CDDA in the case of LE GDROMs */ + if ((file->cdtoc.flags & CD_FLAG_GDROMLE) && (file->cdtoc.tracks[tracknum].trktype == CD_TRACK_AUDIO)) + needswap = true; + } + else + { + // else read from the appropriate file + core_file *srcfile = file->fhandle[tracknum]; + + UINT64 sourcefileoffset = file->track_info.track[tracknum].offset; + int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize; - UINT64 sourcefileoffset = file->track_info.track[tracknum].offset; - int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize; + sourcefileoffset += chdsector * bytespersector + startoffs; - sourcefileoffset += chdsector * bytespersector + startoffs; + // printf("Reading sector %d from track %d at offset %lld\n", chdsector, tracknum, sourcefileoffset); - core_fseek(srcfile, sourcefileoffset, SEEK_SET); - core_fread(srcfile, dest, length); + core_fseek(srcfile, sourcefileoffset, SEEK_SET); + core_fread(srcfile, dest, length); + + needswap = file->track_info.track[tracknum].swap; + } - if (file->track_info.track[tracknum].swap) + if (needswap) { UINT8 *buffer = (UINT8 *)dest - startoffs; for (int swapindex = startoffs; swapindex < 2352; swapindex += 2 ) @@ -323,8 +378,7 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UI buffer[ swapindex + 1 ] = swaptemp; } } -#endif - return CHDERR_NONE; + return result; } @@ -333,27 +387,37 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UI from a CD-ROM -------------------------------------------------*/ -UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype) +UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype, bool phys) { if (file == NULL) return 0; // compute CHD sector and tracknumber UINT32 tracknum = 0; - UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum); + UINT32 chdsector; + + if (phys) + { + chdsector = physical_to_chd_lba(file, lbasector, tracknum); + } + else + { + chdsector = logical_to_chd_lba(file, lbasector, tracknum); + } /* copy out the requested sector */ UINT32 tracktype = file->cdtoc.tracks[tracknum].trktype; + if ((datatype == tracktype) || (datatype == CD_TRACK_RAW_DONTCARE)) { - return (read_partial_sector(file, buffer, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize) == CHDERR_NONE); + return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize) == CHDERR_NONE); } else { /* return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector */ if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE1_RAW)) { - return (read_partial_sector(file, buffer, chdsector, tracknum, 16, 2048) == CHDERR_NONE); + return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2048) == CHDERR_NONE); } /* return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data */ @@ -367,21 +431,21 @@ UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 bufptr[12] = msf>>16; bufptr[13] = msf>>8; bufptr[14] = msf&0xff; - bufptr[15] = 1; // mode 1 + bufptr[15] = 1; // mode 1 LOG(("CDROM: promotion of mode1/form1 sector to mode1 raw is not complete!\n")); - return (read_partial_sector(file, bufptr+16, chdsector, tracknum, 0, 2048) == CHDERR_NONE); + return (read_partial_sector(file, bufptr+16, lbasector, chdsector, tracknum, 0, 2048) == CHDERR_NONE); } /* return 2048 bytes of mode 1 data from a mode2 form1 or raw sector */ if ((datatype == CD_TRACK_MODE1) && ((tracktype == CD_TRACK_MODE2_FORM1)||(tracktype == CD_TRACK_MODE2_RAW))) { - return (read_partial_sector(file, buffer, chdsector, tracknum, 24, 2048) == CHDERR_NONE); + return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 24, 2048) == CHDERR_NONE); } /* return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header) */ if ((datatype == CD_TRACK_MODE2) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW))) { - return (read_partial_sector(file, buffer, chdsector, tracknum, 16, 2336) == CHDERR_NONE); + return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2336) == CHDERR_NONE); } LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype)); @@ -395,19 +459,29 @@ UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 a sector -------------------------------------------------*/ -UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer) +UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer, bool phys) { if (file == NULL) return ~0; // compute CHD sector and tracknumber UINT32 tracknum = 0; - UINT32 chdsector = physical_to_chd_lba(file, lbasector, tracknum); + UINT32 chdsector; + + if (phys) + { + chdsector = physical_to_chd_lba(file, lbasector, tracknum); + } + else + { + chdsector = logical_to_chd_lba(file, lbasector, tracknum); + } + if (file->cdtoc.tracks[tracknum].subsize == 0) - return 1; + return 0; // read the data - chd_error err = read_partial_sector(file, buffer, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize); + chd_error err = read_partial_sector(file, buffer, lbasector, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize); return (err == CHDERR_NONE); } @@ -430,7 +504,8 @@ UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame) return ~0; /* convert to a CHD sector offset and get track information */ - physical_to_chd_lba(file, frame, track); + logical_to_chd_lba(file, frame, track); + return track; } @@ -449,10 +524,25 @@ UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track) if (track == 0xaa) track = file->cdtoc.numtrks; - return file->cdtoc.tracks[track].physframeofs; + return file->cdtoc.tracks[track].logframeofs; } +/*------------------------------------------------- + cdrom_get_track_start_phys - get the + physical frame number that a track starts at +-------------------------------------------------*/ + +UINT32 cdrom_get_track_start_phys(cdrom_file *file, UINT32 track) +{ + if (file == NULL) + return ~0; + + /* handle lead-out specially */ + if (track == 0xaa) + track = file->cdtoc.numtrks; + return file->cdtoc.tracks[track].physframeofs; +} /*************************************************************************** TOC UTILITIES @@ -484,10 +574,10 @@ int cdrom_get_adr_control(cdrom_file *file, int track) if (track == 0xaa || file->cdtoc.tracks[track].trktype == CD_TRACK_AUDIO) { - return 0x10; // audio track, subchannel is position + return 0x10; // audio track, subchannel is position } - return 0x14; // data track, subchannel is position + return 0x14; // data track, subchannel is position } @@ -679,15 +769,15 @@ const char *cdrom_get_type_string(UINT32 trktype) { switch (trktype) { - case CD_TRACK_MODE1: return "MODE1"; - case CD_TRACK_MODE1_RAW: return "MODE1_RAW"; - case CD_TRACK_MODE2: return "MODE2"; - case CD_TRACK_MODE2_FORM1: return "MODE2_FORM1"; - case CD_TRACK_MODE2_FORM2: return "MODE2_FORM2"; - case CD_TRACK_MODE2_FORM_MIX: return "MODE2_FORM_MIX"; - case CD_TRACK_MODE2_RAW: return "MODE2_RAW"; - case CD_TRACK_AUDIO: return "AUDIO"; - default: return "UNKNOWN"; + case CD_TRACK_MODE1: return "MODE1"; + case CD_TRACK_MODE1_RAW: return "MODE1_RAW"; + case CD_TRACK_MODE2: return "MODE2"; + case CD_TRACK_MODE2_FORM1: return "MODE2_FORM1"; + case CD_TRACK_MODE2_FORM2: return "MODE2_FORM2"; + case CD_TRACK_MODE2_FORM_MIX: return "MODE2_FORM_MIX"; + case CD_TRACK_MODE2_RAW: return "MODE2_RAW"; + case CD_TRACK_AUDIO: return "AUDIO"; + default: return "UNKNOWN"; } } @@ -701,9 +791,9 @@ const char *cdrom_get_subtype_string(UINT32 subtype) { switch (subtype) { - case CD_SUB_NORMAL: return "RW"; - case CD_SUB_RAW: return "RW_RAW"; - default: return "NONE"; + case CD_SUB_NORMAL: return "RW"; + case CD_SUB_RAW: return "RW_RAW"; + default: return "NONE"; } } @@ -724,6 +814,8 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) chd_error err; int i; + toc->flags = 0; + /* start with no tracks */ for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++) { @@ -739,7 +831,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) { /* parse the metadata */ type[0] = subtype[0] = 0; - pgtype[0] = pgsub[0] = 0; + pgtype[0] = pgsub[0] = 0; if (sscanf(metadata, CDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames) != 4) return CHDERR_INVALID_DATA; if (tracknum == 0 || tracknum > CD_MAX_TRACKS) @@ -750,36 +842,42 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) { err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata); if (err == CHDERR_NONE) - { - /* parse the metadata */ - type[0] = subtype[0] = 0; - pregap = postgap = 0; - if (sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8) - return CHDERR_INVALID_DATA; - if (tracknum == 0 || tracknum > CD_MAX_TRACKS) - return CHDERR_INVALID_DATA; - track = &toc->tracks[tracknum - 1]; - } - else - { - err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc->numtrks, metadata); - - if (err == CHDERR_NONE) - { - /* parse the metadata */ - type[0] = subtype[0] = 0; - pregap = postgap = 0; - if (sscanf(metadata, GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9) - return CHDERR_INVALID_DATA; - if (tracknum == 0 || tracknum > CD_MAX_TRACKS) - return CHDERR_INVALID_DATA; - track = &toc->tracks[tracknum - 1]; - } - else - { - break; - } - } + { + /* parse the metadata */ + type[0] = subtype[0] = 0; + pregap = postgap = 0; + if (sscanf(metadata, CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8) + return CHDERR_INVALID_DATA; + if (tracknum == 0 || tracknum > CD_MAX_TRACKS) + return CHDERR_INVALID_DATA; + track = &toc->tracks[tracknum - 1]; + } + else + { + err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc->numtrks, metadata); + if (err == CHDERR_NONE) + /* legacy GDROM track was detected */ + toc->flags |= CD_FLAG_GDROMLE; + else + err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc->numtrks, metadata); + + if (err == CHDERR_NONE) + { + /* parse the metadata */ + type[0] = subtype[0] = 0; + pregap = postgap = 0; + if (sscanf(metadata, GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9) + return CHDERR_INVALID_DATA; + if (tracknum == 0 || tracknum > CD_MAX_TRACKS) + return CHDERR_INVALID_DATA; + track = &toc->tracks[tracknum - 1]; + toc->flags |= CD_FLAG_GDROM; + } + else + { + break; + } + } } /* extract the track type and determine the data size */ @@ -796,7 +894,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) /* set the frames and extra frames data */ track->frames = frames; - track->padframes = padframes; + track->padframes = padframes; int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING; track->extraframes = padded * CD_TRACK_PADDING - frames; @@ -806,18 +904,25 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) track->pgsub = CD_SUB_NONE; track->pgdatasize = 0; track->pgsubsize = 0; - cdrom_convert_type_string_to_pregap_info(pgtype, track); - cdrom_convert_subtype_string_to_pregap_info(pgsub, track); + if (track->pregap > 0) + { + if (pgtype[0] == 'V') + { + cdrom_convert_type_string_to_pregap_info(&pgtype[1], track); + } + + cdrom_convert_subtype_string_to_pregap_info(pgsub, track); + } - /* set the postgap info */ - track->postgap = postgap; + /* set the postgap info */ + track->postgap = postgap; } /* if we got any tracks this way, we're done */ if (toc->numtrks > 0) return CHDERR_NONE; - //printf("toc->numtrks = %d?!\n", toc->numtrks); + printf("toc->numtrks = %d?!\n", toc->numtrks); /* look for old-style metadata */ dynamic_buffer oldmetadata; @@ -868,7 +973,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) /*------------------------------------------------- cdrom_write_metadata - write metadata -------------------------------------------------*/ -#if 0 + chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) { chd_error err; @@ -877,31 +982,42 @@ chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) /* write the metadata */ for (i = 0; i < toc->numtrks; i++) { - astring metadata; - if (!(toc->flags & CD_FLAG_GDROM)) - { - metadata.format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype), - cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap, - cdrom_get_type_string(toc->tracks[i].pgtype), cdrom_get_subtype_string(toc->tracks[i].pgsub), - toc->tracks[i].postgap); - - err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata); - } - else - { - metadata.format(GDROM_TRACK_METADATA_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype), - cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].padframes, - toc->tracks[i].pregap, cdrom_get_type_string(toc->tracks[i].pgtype), - cdrom_get_subtype_string(toc->tracks[i].pgsub), toc->tracks[i].postgap); - - err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata); - } + astring metadata; + if (!(toc->flags & CD_FLAG_GDROM)) + { + char submode[32]; + + if (toc->tracks[i].pgdatasize > 0) + { + strcpy(&submode[1], cdrom_get_type_string(toc->tracks[i].pgtype)); + submode[0] = 'V'; // indicate valid submode + } + else + { + strcpy(submode, cdrom_get_type_string(toc->tracks[i].pgtype)); + } + + metadata.format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype), + cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap, + submode, cdrom_get_subtype_string(toc->tracks[i].pgsub), + toc->tracks[i].postgap); + err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata); + } + else + { + metadata.format(GDROM_TRACK_METADATA_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype), + cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].padframes, + toc->tracks[i].pregap, cdrom_get_type_string(toc->tracks[i].pgtype), + cdrom_get_subtype_string(toc->tracks[i].pgsub), toc->tracks[i].postgap); + + err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata); + } if (err != CHDERR_NONE) return err; } return CHDERR_NONE; } -#endif + //------------------------------------------------- // ECC lookup tables @@ -1134,11 +1250,11 @@ void ecc_compute_bytes(const UINT8 *sector, const UINT16 *row, int rowlen, UINT8 val1 = val2 = 0; for (int component = 0; component < rowlen; component++) { - val1 ^= ecc_source_byte(sector, row[component]); - val2 ^= ecc_source_byte(sector, row[component]); - val1 = ecclow[val1]; - } - val1 = ecchigh[ecclow[val1] ^ val2]; + val1 ^= ecc_source_byte(sector, row[component]); + val2 ^= ecc_source_byte(sector, row[component]); + val1 = ecclow[val1]; + } + val1 = ecchigh[ecclow[val1] ^ val2]; val2 ^= val1; } @@ -1154,16 +1270,16 @@ bool ecc_verify(const UINT8 *sector) for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++) { UINT8 val1, val2; - ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, val1, val2); + ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, val1, val2); if (sector[ECC_P_OFFSET + byte] != val1 || sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte] != val2) return false; } // then verify Q bytes - for (int byte = 0; byte < ECC_Q_NUM_BYTES; byte++) + for (int byte = 0; byte < ECC_Q_NUM_BYTES; byte++) { UINT8 val1, val2; - ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, val1, val2); + ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, val1, val2); if (sector[ECC_Q_OFFSET + byte] != val1 || sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte] != val2) return false; } @@ -1180,11 +1296,11 @@ void ecc_generate(UINT8 *sector) { // first verify P bytes for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++) - ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, sector[ECC_P_OFFSET + byte], sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]); + ecc_compute_bytes(sector, poffsets[byte], ECC_P_COMP, sector[ECC_P_OFFSET + byte], sector[ECC_P_OFFSET + ECC_P_NUM_BYTES + byte]); // then verify Q bytes - for (int byte = 0; byte < ECC_Q_NUM_BYTES; byte++) - ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, sector[ECC_Q_OFFSET + byte], sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]); + for (int byte = 0; byte < ECC_Q_NUM_BYTES; byte++) + ecc_compute_bytes(sector, qoffsets[byte], ECC_Q_COMP, sector[ECC_Q_OFFSET + byte], sector[ECC_Q_OFFSET + ECC_Q_NUM_BYTES + byte]); } diff --git a/archivers/chd/chdcdrom.h b/archivers/chd/chdcdrom.h index dab39178..c0115701 100644 --- a/archivers/chd/chdcdrom.h +++ b/archivers/chd/chdcdrom.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** cdrom.h Generic MAME cd-rom implementation -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -54,37 +25,38 @@ // tracks are padded to a multiple of this many frames const UINT32 CD_TRACK_PADDING = 4; -#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */ -#define CD_MAX_SECTOR_DATA (2352) -#define CD_MAX_SUBCODE_DATA (96) +#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */ +#define CD_MAX_SECTOR_DATA (2352) +#define CD_MAX_SUBCODE_DATA (96) -#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA) -#define CD_FRAMES_PER_HUNK (8) +#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA) +#define CD_FRAMES_PER_HUNK (8) -#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6)) +#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6)) enum { - CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */ - CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */ - CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */ - CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */ - CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */ - CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */ - CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */ - CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */ - - CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */ + CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */ + CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */ + CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */ + CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */ + CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */ + CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */ + CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */ + CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */ + + CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */ }; enum { - CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */ - CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */ - CD_SUB_NONE /* no subcode data stored */ + CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */ + CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */ + CD_SUB_NONE /* no subcode data stored */ }; -#define CD_FLAG_GDROM 0x00000001 // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata +#define CD_FLAG_GDROM 0x00000001 // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata +#define CD_FLAG_GDROMLE 0x00000002 // legacy GD-ROM, with little-endian CDDA data /*************************************************************************** TYPE DEFINITIONS @@ -95,32 +67,33 @@ struct cdrom_file; struct cdrom_track_info { /* fields used by CHDMAN and in MAME */ - UINT32 trktype; /* track type */ - UINT32 subtype; /* subcode data type */ - UINT32 datasize; /* size of data in each sector of this track */ - UINT32 subsize; /* size of subchannel data in each sector of this track */ - UINT32 frames; /* number of frames in this track */ - UINT32 extraframes; /* number of "spillage" frames in this track */ - UINT32 pregap; /* number of pregap frames */ - UINT32 postgap; /* number of postgap frames */ - UINT32 pgtype; /* type of sectors in pregap */ - UINT32 pgsub; /* type of subchannel data in pregap */ - UINT32 pgdatasize; /* size of data in each sector of the pregap */ - UINT32 pgsubsize; /* size of subchannel data in each sector of the pregap */ + UINT32 trktype; /* track type */ + UINT32 subtype; /* subcode data type */ + UINT32 datasize; /* size of data in each sector of this track */ + UINT32 subsize; /* size of subchannel data in each sector of this track */ + UINT32 frames; /* number of frames in this track */ + UINT32 extraframes; /* number of "spillage" frames in this track */ + UINT32 pregap; /* number of pregap frames */ + UINT32 postgap; /* number of postgap frames */ + UINT32 pgtype; /* type of sectors in pregap */ + UINT32 pgsub; /* type of subchannel data in pregap */ + UINT32 pgdatasize; /* size of data in each sector of the pregap */ + UINT32 pgsubsize; /* size of subchannel data in each sector of the pregap */ /* fields used in CHDMAN only */ - UINT32 padframes; /* number of frames of padding to add to the end of the track; needed for GDI */ + UINT32 padframes; /* number of frames of padding to add to the end of the track; needed for GDI */ - /* fields used in MAME only */ - UINT32 physframeofs; /* frame number on the real CD this track starts at */ - UINT32 chdframeofs; /* frame number this track starts at on the CHD */ + /* fields used in MAME/MESS only */ + UINT32 logframeofs; /* logical frame of actual track data - offset by pregap size if pregap not physically present */ + UINT32 physframeofs; /* physical frame of actual track data in CHD data */ + UINT32 chdframeofs; /* frame number this track starts at on the CHD */ }; struct cdrom_toc { - UINT32 numtrks; /* number of tracks */ - UINT32 flags; /* see FLAG_ above */ + UINT32 numtrks; /* number of tracks */ + UINT32 flags; /* see FLAG_ above */ cdrom_track_info tracks[CD_MAX_TRACKS]; }; @@ -137,13 +110,13 @@ void cdrom_close(cdrom_file *file); cdrom_file *cdrom_open(const char *inputfile); /* core read access */ -UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype); -UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer); -chd_error read_partial_sector(cdrom_file *file, void *dest, UINT32 chdsector, UINT32 tracknum, UINT32 startoffs, UINT32 length); +UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype, bool phys=false); +UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer, bool phys=false); /* handy utilities */ UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame); UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track); +UINT32 cdrom_get_track_start_phys(cdrom_file *file, UINT32 track); /* TOC utilities */ int cdrom_get_last_track(cdrom_file *file); @@ -187,8 +160,8 @@ INLINE UINT32 lba_to_msf(UINT32 lba) f = lba % 75; return ((m / 10) << 20) | ((m % 10) << 16) | - ((s / 10) << 12) | ((s % 10) << 8) | - ((f / 10) << 4) | ((f % 10) << 0); + ((s / 10) << 12) | ((s % 10) << 8) | + ((f / 10) << 4) | ((f % 10) << 0); } // segacd needs it like this.. investigate @@ -208,4 +181,4 @@ INLINE UINT32 lba_to_msf_alt(int lba) -#endif // __CDROM_H__ +#endif // __CDROM_H__ diff --git a/archivers/chd/chdcodec.cpp b/archivers/chd/chdcodec.cpp index f1234462..fdc7c1cb 100644 --- a/archivers/chd/chdcodec.cpp +++ b/archivers/chd/chdcodec.cpp @@ -1,49 +1,19 @@ #include "chdtypes.h" - +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** chdcodec.c Codecs used by the CHD format -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #include "chd.h" #include "hashing.h" +#include "avhuff.h" #include "flac.h" #include "chdcdrom.h" -#include "huffman.h" #include #include "7z/LzmaEnc.h" #include "7z/LzmaDec.h" @@ -81,7 +51,7 @@ private: static void fast_free(voidpf opaque, voidpf address); static const int MAX_ZLIB_ALLOCS = 64; - UINT32 * m_allocptr[MAX_ZLIB_ALLOCS]; + UINT32 * m_allocptr[MAX_ZLIB_ALLOCS]; }; @@ -100,8 +70,8 @@ public: private: // internal state - z_stream m_deflater; - chd_zlib_allocator m_allocator; + z_stream m_deflater; + chd_zlib_allocator m_allocator; }; @@ -120,8 +90,8 @@ public: private: // internal state - z_stream m_inflater; - chd_zlib_allocator m_allocator; + z_stream m_inflater; + chd_zlib_allocator m_allocator; }; @@ -141,7 +111,7 @@ private: static void fast_free(void *p, void *address); static const int MAX_LZMA_ALLOCS = 64; - UINT32 * m_allocptr[MAX_LZMA_ALLOCS]; + UINT32 * m_allocptr[MAX_LZMA_ALLOCS]; }; @@ -163,8 +133,8 @@ public: private: // internal state - CLzmaEncProps m_props; - chd_lzma_allocator m_allocator; + CLzmaEncProps m_props; + chd_lzma_allocator m_allocator; }; @@ -183,12 +153,11 @@ public: private: // internal state - CLzmaProps m_props; - CLzmaDec m_decoder; - chd_lzma_allocator m_allocator; + CLzmaDec m_decoder; + chd_lzma_allocator m_allocator; }; - +#if 0 // ======================> chd_huffman_compressor // Huffman compressor @@ -203,9 +172,9 @@ public: private: // internal state - huffman_8bit_encoder m_encoder; + huffman_8bit_encoder m_encoder; }; - +#endif // ======================> chd_huffman_decompressor @@ -221,10 +190,9 @@ public: private: // internal state - huffman_8bit_decoder m_decoder; + huffman_8bit_decoder m_decoder; }; - // ======================> chd_flac_compressor // FLAC compressor @@ -242,11 +210,10 @@ public: private: // internal state - bool m_big_endian; - flac_encoder m_encoder; + bool m_big_endian; + flac_encoder m_encoder; }; - // ======================> chd_flac_decompressor // FLAC decompressor @@ -261,8 +228,8 @@ public: private: // internal state - bool m_big_endian; - flac_decoder m_decoder; + bool m_big_endian; + flac_decoder m_decoder; }; @@ -284,11 +251,11 @@ public: private: // internal state - bool m_swap_endian; - flac_encoder m_encoder; - z_stream m_deflater; - chd_zlib_allocator m_allocator; - dynamic_buffer m_buffer; + bool m_swap_endian; + flac_encoder m_encoder; + z_stream m_deflater; + chd_zlib_allocator m_allocator; + dynamic_buffer m_buffer; }; @@ -307,11 +274,11 @@ public: private: // internal state - bool m_swap_endian; - flac_decoder m_decoder; - z_stream m_inflater; - chd_zlib_allocator m_allocator; - dynamic_buffer m_buffer; + bool m_swap_endian; + flac_decoder m_decoder; + z_stream m_inflater; + chd_zlib_allocator m_allocator; + dynamic_buffer m_buffer; }; @@ -324,9 +291,9 @@ public: // construction/destruction chd_cd_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) : chd_compressor(chd, hunkbytes, lossy), - m_base_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy), - m_subcode_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy), - m_buffer(hunkbytes + (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA) + m_base_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy), + m_subcode_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy), + m_buffer(hunkbytes + (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA) { // make sure the CHD's hunk size is an even multiple of the frame size if (hunkbytes % CD_FRAME_SIZE != 0) @@ -378,9 +345,9 @@ public: private: // internal state - _BaseCompressor m_base_compressor; - _SubcodeCompressor m_subcode_compressor; - dynamic_buffer m_buffer; + _BaseCompressor m_base_compressor; + _SubcodeCompressor m_subcode_compressor; + dynamic_buffer m_buffer; }; @@ -393,9 +360,9 @@ public: // construction/destruction chd_cd_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) : chd_decompressor(chd, hunkbytes, lossy), - m_base_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy), - m_subcode_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy), - m_buffer(hunkbytes) + m_base_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy), + m_subcode_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy), + m_buffer(hunkbytes) { // make sure the CHD's hunk size is an even multiple of the frame size if (hunkbytes % CD_FRAME_SIZE != 0) @@ -438,9 +405,9 @@ public: private: // internal state - _BaseDecompressor m_base_decompressor; + _BaseDecompressor m_base_decompressor; _SubcodeDecompressor m_subcode_decompressor; - dynamic_buffer m_buffer; + dynamic_buffer m_buffer; }; @@ -462,8 +429,8 @@ private: void postinit(); // internal state - avhuff_encoder m_encoder; - bool m_postinit; + avhuff_encoder m_encoder; + bool m_postinit; }; @@ -482,7 +449,7 @@ public: private: // internal state - avhuff_decoder m_decoder; + avhuff_decoder m_decoder; }; #endif @@ -495,18 +462,21 @@ private: const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] = { // general codecs - { CHD_CODEC_ZLIB, false, "Deflate", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, - { CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, - { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, - { CHD_CODEC_FLAC, false, "FLAC", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, - + { CHD_CODEC_ZLIB, false, "Deflate", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + { CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, +#if 0 + { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + { CHD_CODEC_FLAC, false, "FLAC", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, +#endif // general codecs with CD frontend - { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &chd_codec_list::construct_compressor >, &chd_codec_list::construct_decompressor > }, - { CHD_CODEC_CD_LZMA, false, "CD LZMA", &chd_codec_list::construct_compressor >, &chd_codec_list::construct_decompressor > }, - { CHD_CODEC_CD_FLAC, false, "CD FLAC", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &chd_codec_list::construct_compressor >, &chd_codec_list::construct_decompressor > }, + { CHD_CODEC_CD_LZMA, false, "CD LZMA", &chd_codec_list::construct_compressor >, &chd_codec_list::construct_decompressor > }, + { CHD_CODEC_CD_FLAC, false, "CD FLAC", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, +#if 0 // A/V codecs -// { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, + { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor, &chd_codec_list::construct_decompressor }, +#endif }; @@ -521,8 +491,8 @@ const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] = chd_codec::chd_codec(chd_file &chd, UINT32 hunkbytes, bool lossy) : m_chd(chd), - m_hunkbytes(hunkbytes), - m_lossy(lossy) + m_hunkbytes(hunkbytes), + m_lossy(lossy) { } @@ -647,9 +617,9 @@ const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type t chd_compressor_group::chd_compressor_group(chd_file &chd, UINT32 compressor_list[4]) : m_hunkbytes(chd.hunk_bytes()), - m_compress_test(m_hunkbytes) + m_compress_test(m_hunkbytes) #if CHDCODEC_VERIFY_COMPRESSION - ,m_decompressed(m_hunkbytes) + ,m_decompressed(m_hunkbytes) #endif { // verify the compression types and initialize the codecs @@ -791,9 +761,6 @@ void chd_zlib_allocator::install(z_stream &stream) voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size) { -#if 0 - return calloc(items, size); -#else chd_zlib_allocator *codec = reinterpret_cast(opaque); // compute the size, rounding to the nearest 1k @@ -823,7 +790,6 @@ voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size) // set the low bit of the size so we don't match next time *ptr = size | 1; return ptr + 1; -#endif } @@ -834,9 +800,6 @@ voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size) void chd_zlib_allocator::fast_free(voidpf opaque, voidpf address) { -#if 0 - free (address); -#else chd_zlib_allocator *codec = reinterpret_cast(opaque); // find the hunk @@ -848,7 +811,6 @@ void chd_zlib_allocator::fast_free(voidpf opaque, voidpf address) *ptr &= ~1; return; } -#endif } @@ -865,7 +827,7 @@ chd_zlib_compressor::chd_zlib_compressor(chd_file &chd, UINT32 hunkbytes, bool l : chd_compressor(chd, hunkbytes, lossy) { // initialize the deflater - m_deflater.next_in = (Bytef *)this; // bogus, but that's ok + m_deflater.next_in = (Bytef *)this; // bogus, but that's ok m_deflater.avail_in = 0; m_allocator.install(m_deflater); int zerr = deflateInit2(&m_deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); @@ -930,8 +892,7 @@ chd_zlib_decompressor::chd_zlib_decompressor(chd_file &chd, UINT32 hunkbytes, bo : chd_decompressor(chd, hunkbytes, lossy) { // init the inflater - memset (&m_inflater, 0, sizeof (z_stream)); - m_inflater.next_in = (Bytef *)this; // bogus, but that's ok + m_inflater.next_in = (Bytef *)this; // bogus, but that's ok m_inflater.avail_in = 0; m_allocator.install(m_inflater); int zerr = inflateInit2(&m_inflater, -MAX_WBITS); @@ -1217,7 +1178,7 @@ void chd_lzma_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 * } - +#if 0 //************************************************************************** // HUFFMAN COMPRESSOR //************************************************************************** @@ -1239,16 +1200,12 @@ chd_huffman_compressor::chd_huffman_compressor(chd_file &chd, UINT32 hunkbytes, UINT32 chd_huffman_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) { -#if 0 UINT32 complen; if (m_encoder.encode(src, srclen, dest, srclen, complen) != HUFFERR_NONE) throw CHDERR_COMPRESSION_ERROR; return complen; -#else - return 0; -#endif } - +#endif //************************************************************************** @@ -1277,7 +1234,6 @@ void chd_huffman_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT } - //************************************************************************** // FLAC COMPRESSOR //************************************************************************** @@ -1417,7 +1373,7 @@ void chd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 * chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) : chd_compressor(chd, hunkbytes, lossy), - m_buffer(hunkbytes) + m_buffer(hunkbytes) { #if 0 // make sure the CHD's hunk size is an even multiple of the frame size @@ -1436,7 +1392,7 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes, m_encoder.set_strip_metadata(true); // initialize the deflater - m_deflater.next_in = (Bytef *)this; // bogus, but that's ok + m_deflater.next_in = (Bytef *)this; // bogus, but that's ok m_deflater.avail_in = 0; m_allocator.install(m_deflater); int zerr = deflateInit2(&m_deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); @@ -1446,6 +1402,8 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, UINT32 hunkbytes, throw std::bad_alloc(); else if (zerr != Z_OK) throw CHDERR_CODEC_ERROR; +#else + throw CHDERR_CODEC_ERROR; #endif } @@ -1524,7 +1482,6 @@ UINT32 chd_cd_flac_compressor::blocksize(UINT32 bytes) } - //************************************************************************** // CD FLAC DECOMPRESSOR //************************************************************************** @@ -1535,7 +1492,7 @@ UINT32 chd_cd_flac_compressor::blocksize(UINT32 bytes) chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, UINT32 hunkbytes, bool lossy) : chd_decompressor(chd, hunkbytes, lossy), - m_buffer(hunkbytes) + m_buffer(hunkbytes) { // make sure the CHD's hunk size is an even multiple of the frame size if (hunkbytes % CD_FRAME_SIZE != 0) @@ -1547,7 +1504,7 @@ chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, UINT32 hunkbyt m_swap_endian = (native_endian == 1); // init the inflater - m_inflater.next_in = (Bytef *)this; // bogus, but that's ok + m_inflater.next_in = (Bytef *)this; // bogus, but that's ok m_inflater.avail_in = 0; m_allocator.install(m_inflater); int zerr = inflateInit2(&m_inflater, -MAX_WBITS); @@ -1624,7 +1581,7 @@ void chd_cd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT chd_avhuff_compressor::chd_avhuff_compressor(chd_file &chd, UINT32 hunkbytes, bool lossy) : chd_compressor(chd, hunkbytes, lossy), - m_postinit(false) + m_postinit(false) { try { @@ -1697,7 +1654,6 @@ void chd_avhuff_compressor::postinit() } - //************************************************************************** // AVHUFF DECOMPRESSOR //************************************************************************** @@ -1719,7 +1675,6 @@ chd_avhuff_decompressor::chd_avhuff_decompressor(chd_file &chd, UINT32 hunkbytes void chd_avhuff_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) { - // decode the audio and video avhuff_error averr = m_decoder.decode_data(src, complen, dest); if (averr != AVHERR_NONE) @@ -1734,6 +1689,7 @@ void chd_avhuff_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 } } + //------------------------------------------------- // config - codec-specific configuration for the // A/V codec @@ -1749,4 +1705,4 @@ void chd_avhuff_decompressor::configure(int param, void *config) else throw CHDERR_INVALID_PARAMETER; } -#endif \ No newline at end of file +#endif diff --git a/archivers/chd/chdcodec.h b/archivers/chd/chdcodec.h index a8d6f538..144fba86 100644 --- a/archivers/chd/chdcodec.h +++ b/archivers/chd/chdcodec.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** chdcodec.h Codecs used by the CHD format -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -52,7 +23,7 @@ // MACROS //************************************************************************** -#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) @@ -90,9 +61,9 @@ public: private: // internal state - chd_file & m_chd; - UINT32 m_hunkbytes; - bool m_lossy; + chd_file & m_chd; + UINT32 m_hunkbytes; + bool m_lossy; }; @@ -144,11 +115,11 @@ private: // an entry in the list struct codec_entry { - chd_codec_type m_type; - bool m_lossy; - const char * m_name; - chd_compressor * (*m_construct_compressor)(chd_file &, UINT32, bool); - chd_decompressor * (*m_construct_decompressor)(chd_file &, UINT32, bool); + chd_codec_type m_type; + bool m_lossy; + const char * m_name; + chd_compressor * (*m_construct_compressor)(chd_file &, UINT32, bool); + chd_decompressor * (*m_construct_decompressor)(chd_file &, UINT32, bool); }; // internal helper functions @@ -180,12 +151,12 @@ public: private: // internal state - UINT32 m_hunkbytes; // number of bytes in a hunk - chd_compressor * m_compressor[4]; // array of active codecs - dynamic_buffer m_compress_test; // test buffer for compression + UINT32 m_hunkbytes; // number of bytes in a hunk + chd_compressor * m_compressor[4]; // array of active codecs + dynamic_buffer m_compress_test; // test buffer for compression #if CHDCODEC_VERIFY_COMPRESSION - chd_decompressor * m_decompressor[4]; // array of active codecs - dynamic_buffer m_decompressed; // verification buffer + chd_decompressor * m_decompressor[4]; // array of active codecs + dynamic_buffer m_decompressed; // verification buffer #endif }; @@ -196,21 +167,21 @@ private: //************************************************************************** // currently-defined codecs -const chd_codec_type CHD_CODEC_NONE = 0; +const chd_codec_type CHD_CODEC_NONE = 0; // general codecs -const chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b'); -const chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a'); -const chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f'); -const chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f','l','a','c'); +const chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b'); +const chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a'); +const chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f'); +const chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f','l','a','c'); // general codecs with CD frontend -const chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c','d','z','l'); -const chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c','d','l','z'); -const chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l'); +const chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c','d','z','l'); +const chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c','d','l','z'); +const chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l'); // A/V codecs -const chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u'); +const chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u'); // A/V codec configuration parameters enum diff --git a/archivers/chd/chdglue.cpp b/archivers/chd/chdglue.cpp new file mode 100644 index 00000000..2bf8af80 --- /dev/null +++ b/archivers/chd/chdglue.cpp @@ -0,0 +1,88 @@ + +#include "sysconfig.h" +#include "sysdeps.h" + +#include "zfile.h" +#include "fsdb.h" +#include "threaddep/thread.h" + +#include "chdtypes.h" +#include "corefile.h" + +int core_fseek(core_file *file, INT64 offset, int whence) +{ + return zfile_fseek(file, offset, whence); +} +file_error core_fopen(const TCHAR *filename, UINT32 openflags, core_file **file) +{ + TCHAR *mode; + + if (openflags & OPEN_FLAG_CREATE) + mode = _T("wb"); + else if (openflags & OPEN_FLAG_WRITE) + mode = _T("rb+"); + else + mode = _T("rb"); + struct zfile *z = zfile_fopen(filename, mode); + *file = z; + return z == NULL ? FILERR_NOT_FOUND : FILERR_NONE; +} +void core_fclose(core_file *file) +{ + zfile_fclose(file); +} +UINT32 core_fread(core_file *file, void *buffer, UINT32 length) +{ + return zfile_fread(buffer, 1, length, file); +} +UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length) +{ + return zfile_fwrite(buffer, 1, length, file); +} +UINT64 core_ftell(core_file *file) +{ + return zfile_ftell(file); +} + + +file_error osd_rmfile(const TCHAR *filename) +{ + my_rmdir(filename); + return FILERR_NONE; +} + +void osd_break_into_debugger(const char *message) +{ + write_log(message); +} + +void *osd_malloc(size_t size) +{ + return malloc(size); +} +void *osd_malloc_array(size_t size) +{ + return malloc(size); +} +void osd_free(void *ptr) +{ + free(ptr); +} +osd_lock *osd_lock_alloc(void) +{ + uae_sem_t *t = xmalloc(uae_sem_t, 1); + uae_sem_init(t, 0, 1); + return (osd_lock*)t; +} +void osd_lock_free(osd_lock *lock) +{ + uae_sem_destroy((uae_sem_t*)lock); +} +void osd_lock_release(osd_lock *lock) +{ + uae_sem_post((uae_sem_t*)lock); +} +void osd_lock_acquire(osd_lock *lock) +{ + uae_sem_wait((uae_sem_t*)lock); +} \ No newline at end of file diff --git a/archivers/chd/chdtypes.h b/archivers/chd/chdtypes.h index cad86c91..941caf5e 100644 --- a/archivers/chd/chdtypes.h +++ b/archivers/chd/chdtypes.h @@ -9,6 +9,8 @@ #include "zfile.h" #endif +#define NO_MEM_TRACKING + #if defined(_MSC_VER) && (_MSC_VER >= 1200) #define DECL_NORETURN __declspec(noreturn) #else @@ -27,8 +29,7 @@ #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0])) #define CLIB_DECL __cdecl - -#define FLAC__NO_DLL +#define FLAC_API_EXPORTS /* Macros for normalizing data into big or little endian formats */ #define FLIPENDIAN_INT16(x) (((((UINT16) (x)) >> 8) | ((x) << 8)) & 0xffff) diff --git a/archivers/chd/corealloc.cpp b/archivers/chd/corealloc.cpp new file mode 100644 index 00000000..9c7229e2 --- /dev/null +++ b/archivers/chd/corealloc.cpp @@ -0,0 +1,385 @@ +#include "chdtypes.h" +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + corealloc.c + + Memory allocation helpers for the helper library. + +***************************************************************************/ + +#include "corealloc.h" +#include "osdcore.h" + + +//************************************************************************** +// DEBUGGING +//************************************************************************** + +#define LOG_ALLOCS (0) + +// define this to initialize allocated memory to a fixed non-0 value +#ifdef MAME_DEBUG +#define INITIALIZE_ALLOCATED_MEMORY +#endif + +// define this to zap memory to a fixed non-0 value before freeing +//#define OVERWRITE_FREED_MEMORY + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// number of memory_entries to allocate in a block +const int memory_block_alloc_chunk = 256; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// this struct is allocated in pools to track memory allocations +// it must be a POD type!! +class memory_entry +{ +public: + // internal state + memory_entry * m_next; // link to the next entry + memory_entry * m_prev; // link to the previous entry + size_t m_size; // size of the allocation (not including this header) + void * m_base; // base of the allocation + const char * m_file; // file the allocation was made from + int m_line; // line number within that file + UINT64 m_id; // unique id + bool m_array; // array? + + // hashing prime number + static const int k_hash_prime = 6151; + + // global state + static UINT64 s_curid; // current ID + static osd_lock * s_lock; // lock for managing the list + static bool s_lock_alloc; // set to true temporarily during lock allocation + static bool s_tracking; // set to true when tracking is live + static memory_entry *s_hash[k_hash_prime];// hash table based on pointer + static memory_entry *s_freehead; // pointer to the head of the free list + + // static helpers + static memory_entry *allocate(size_t size, void *base, const char *file, int line, bool array); + static memory_entry *find(void *ptr); + static void release(memory_entry *entry, const char *file, int line); + static void report_unfreed(UINT64 start); + +private: + static void acquire_lock(); + static void release_lock(); +}; + + + +//************************************************************************** +// GLOBALS +//************************************************************************** + +// dummy zeromem object +const zeromem_t zeromem = { }; + +// globals for memory_entry +UINT64 memory_entry::s_curid = 1; +osd_lock *memory_entry::s_lock = NULL; +bool memory_entry::s_lock_alloc = false; +bool memory_entry::s_tracking = false; +memory_entry *memory_entry::s_hash[memory_entry::k_hash_prime] = { NULL }; +memory_entry *memory_entry::s_freehead = NULL; + + + +//************************************************************************** +// GLOBAL HELPERS +//************************************************************************** + +//------------------------------------------------- +// malloc_file_line - allocate memory with file +// and line number information +//------------------------------------------------- + +void *malloc_file_line(size_t size, const char *file, int line, bool array, bool throw_on_fail, bool clear) +{ + // allocate the memory and fail if we can't + void *result = array ? osd_malloc_array(size) : osd_malloc(size); + if (result == NULL) + { + fprintf(stderr, "Failed to allocate %d bytes (%s:%d)\n", UINT32(size), file, line); + osd_break_into_debugger("Failed to allocate RAM"); + if (throw_on_fail) + throw std::bad_alloc(); + return NULL; + } + + // zap the memory if requested + if (clear) + memset(result, 0, size); + else + { +#if !__has_feature(memory_sanitizer) && defined(INITIALIZE_ALLOCATED_MEMORY) && !defined(MAME_DEBUG_FAST) + memset(result, 0xdd, size); +#endif + } + + // add a new entry + memory_entry::allocate(size, result, file, line, array); + + return result; +} + + +//------------------------------------------------- +// free_file_line - free memory with file +// and line number information +//------------------------------------------------- + +void free_file_line(void *memory, const char *file, int line, bool array) +{ + // find the memory entry + memory_entry *entry = memory_entry::find(memory); + + // warn about untracked frees + if (entry == NULL) + { + fprintf(stderr, "Error: attempt to free untracked memory %p in %s(%d)!\n", memory, file, line); + osd_break_into_debugger("Error: attempt to free untracked memory"); + return; + } + + // warn about mismatched arrays + if (!array && entry->m_array) + { + fprintf(stderr, "Error: attempt to free array %p with global_free in %s(%d)!\n", memory, file, line); + osd_break_into_debugger("Error: attempt to free array with global_free"); + } + if (array && !entry->m_array) + { +#ifndef __INTEL_COMPILER // todo: fix this properly, it appears some optimization the compiler applies breaks our logic here + fprintf(stderr, "Error: attempt to free single object %p with global_free_array in %s(%d)!\n", memory, file, line); + osd_break_into_debugger("Error: attempt to free single object with global_free_array"); +#endif + } + +#ifdef OVERWRITE_FREED_MEMORY + // clear memory to a bogus value + memset(memory, 0xfc, entry->m_size); +#endif + + // free the entry and the memory + memory_entry::release(entry, file, line); + osd_free(memory); +} + + +//------------------------------------------------- +// track_memory - enables or disables the memory +// tracking +//------------------------------------------------- + +void track_memory(bool track) +{ + memory_entry::s_tracking = track; +} + + +//------------------------------------------------- +// next_memory_id - return the ID of the next +// allocated block +//------------------------------------------------- + +UINT64 next_memory_id() +{ + return memory_entry::s_curid; +} + + +//------------------------------------------------- +// dump_unfreed_mem - called from the exit path +// of any code that wants to check for unfreed +// memory +//------------------------------------------------- + +void dump_unfreed_mem(UINT64 start) +{ + memory_entry::report_unfreed(start); +} + + + +//************************************************************************** +// MEMORY ENTRY +//************************************************************************** + +//------------------------------------------------- +// acquire_lock - acquire the memory entry lock, +// creating a new one if needed +//------------------------------------------------- + +void memory_entry::acquire_lock() +{ + // allocate a lock on first usage + // note that osd_lock_alloc() may re-enter this path, so protect against recursion! + if (s_lock == NULL) + { + if (s_lock_alloc) + return; + s_lock_alloc = true; + s_lock = osd_lock_alloc(); + s_lock_alloc = false; + } + osd_lock_acquire(s_lock); +} + + +//------------------------------------------------- +// release_lock - release the memory entry lock +//------------------------------------------------- + +void memory_entry::release_lock() +{ + osd_lock_release(s_lock); +} + + +//------------------------------------------------- +// allocate - allocate a new memory entry +//------------------------------------------------- + +memory_entry *memory_entry::allocate(size_t size, void *base, const char *file, int line, bool array) +{ + acquire_lock(); + + // if we're out of free entries, allocate a new chunk + if (s_freehead == NULL) + { + // create a new chunk, and fail if we can't + memory_entry *entry = reinterpret_cast(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(entry->m_size), entry->m_file, (int)entry->m_line); + + // add it to the alloc list + int hashval = reinterpret_cast(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(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(entry->m_size), file, line); + + // remove ourselves from the alloc list + int hashval = reinterpret_cast(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(entry->m_size), entry->m_file, (int)entry->m_line); + } + + release_lock(); + + if (total > 0) + fprintf(stderr, "a total of %u bytes were not freed\n", total); +} diff --git a/archivers/chd/corealloc.h b/archivers/chd/corealloc.h new file mode 100644 index 00000000..5abddda3 --- /dev/null +++ b/archivers/chd/corealloc.h @@ -0,0 +1,112 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + corealloc.h + + Memory allocation helpers for the helper library. + +***************************************************************************/ + +#pragma once + +#ifndef __COREALLOC_H__ +#define __COREALLOC_H__ + +#include +#include +#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(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__ */ diff --git a/archivers/chd/corefile.h b/archivers/chd/corefile.h index e69de29b..87ca8e2b 100644 --- a/archivers/chd/corefile.h +++ b/archivers/chd/corefile.h @@ -0,0 +1,132 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + corefile.h + + Core file I/O interface functions and definitions. + +***************************************************************************/ + +#pragma once + +#ifndef __COREFILE_H__ +#define __COREFILE_H__ + +#include +#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__ */ diff --git a/archivers/chd/coretmpl.h b/archivers/chd/coretmpl.h index 30d181ed..11c43da0 100644 --- a/archivers/chd/coretmpl.h +++ b/archivers/chd/coretmpl.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** coretmpl.h Core templates for basic non-string types. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -44,6 +15,47 @@ #include #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 auto_pointer +{ +private: + // we don't support deep copying + auto_pointer(const auto_pointer &); + auto_pointer &operator=(const auto_pointer &); + +public: + // construction/destruction + auto_pointer(_ElementType *value = NULL) + : m_ptr(value) { } + virtual ~auto_pointer() { reset(); } + + // operators + operator _ElementType *() const { return m_ptr; } + _ElementType &operator*() const { assert(m_ptr != NULL); return *m_ptr; } + _ElementType *operator->() const { return m_ptr; } + + // simple getters + _ElementType *get() const { return m_ptr; } + + // core operations + void reset(_ElementType *ptr = NULL) { if (m_ptr != ptr) { global_free(m_ptr); m_ptr = ptr; } } + +private: + // internal state + _ElementType * m_ptr; // pointer we are tracking +}; // ======================> dynamic_array @@ -59,10 +71,10 @@ private: public: // construction/destruction - dynamic_array(int initial = 0) + dynamic_array(int initial = 0, int clearvalue = -1) : m_array(NULL), - m_count(0), - m_allocated(0) { if (initial != 0) expand_internal(initial); m_count = initial; } + m_count(0), + m_allocated(0) { if (initial != 0) expand_internal(initial); m_count = initial; if (clearvalue != -1) clear(clearvalue); } virtual ~dynamic_array() { reset(); } // operators @@ -73,37 +85,337 @@ public: // simple getters int count() const { return m_count; } + int bytes() const { return m_count * sizeof(_ElementType); } + + // core operations + _ElementType &append() { if (m_count == m_allocated) expand_and_keep_internal((m_allocated == 0) ? 16 : (m_allocated << 1)); return m_array[m_count++]; } + const _ElementType &append(const _ElementType &element) { return (append() = element); } + void reset() { global_free_array(m_array); m_array = NULL; m_count = m_allocated = 0; } + void resize(int count) { if (count > m_allocated) expand_internal(count); m_count = count; } + void resize_keep(int count) { if (count > m_allocated) expand_and_keep_internal(count); m_count = count; } + void clear(UINT8 data = 0) { clear_internal(0, m_count, data); } - // helpers - void append(const _ElementType &element) { if (m_count == m_allocated) expand_internal((m_allocated == 0) ? 16 : (m_allocated << 1), true); m_array[m_count++] = element; } - void reset() { delete[] m_array; m_array = NULL; m_count = m_allocated = 0; } - void resize(int count, bool keepdata = false) { if (count > m_allocated) expand_internal(count, keepdata); m_count = count; } + // compound operations + void resize_and_clear(int count, UINT8 data = 0) { resize(count); clear(data); } + void resize_keep_and_clear_new(int count, UINT8 data = 0) { int oldcount = m_count; resize_keep(count); if (oldcount < m_count) clear_internal(oldcount, m_count - oldcount, data); } private: // internal helpers - void expand_internal(int count, bool keepdata = true) + void expand_internal(int count) { - // allocate a new array, copy the old one, and proceed + global_free_array(m_array); + m_array = global_alloc_array(_ElementType, count); m_allocated = count; - _ElementType *newarray = new _ElementType[m_allocated]; - if (keepdata) - for (int index = 0; index < m_count; index++) - newarray[index] = m_array[index]; - delete[] m_array; - m_array = newarray; } + void expand_and_keep_internal(int count) + { + _ElementType *oldarray = m_array; + int oldcount = m_count; + m_array = global_alloc_array(_ElementType, count); + m_allocated = count; + for (int index = 0; index < oldcount; index++) + m_array[index] = oldarray[index]; + global_free_array(oldarray); + } + +#ifdef __GNUC__ + void clear_internal(UINT32 start, UINT32 count, UINT8 data) { assert(__is_pod(_ElementType)); memset(&m_array[start], data, count * sizeof(*m_array)); } +#else + void clear_internal(UINT32 start, UINT32 count, UINT8 data) { memset(&m_array[start], data, count * sizeof(*m_array)); } +#endif + // internal state - _ElementType * m_array; // allocated array - int m_count; // number of objects accessed in the list - int m_allocated; // amount of space allocated for the array + _ElementType * m_array; // allocated array + int m_count; // number of objects accessed in the list + int m_allocated; // amount of space allocated for the array }; +typedef dynamic_array dynamic_buffer; -// ======================> dynamic_buffer -typedef dynamic_array dynamic_buffer; +// ======================> simple_list + +// a simple_list is a singly-linked list whose 'next' pointer is owned +// by the object +template +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 simple_list_wrapper +{ +public: + template 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 fixed_allocator +{ + // we don't support deep copying + fixed_allocator(const fixed_allocator &); + fixed_allocator &operator=(const fixed_allocator &); + +public: + // construction/destruction + fixed_allocator() { } + + // allocate a new item, either by recycling an old one, or by allocating a new one + _ItemType *alloc() + { + _ItemType *result = m_freelist.detach_head(); + if (result == NULL) + result = global_alloc(_ItemType); + return result; + } + + // reclaim an item by adding it to the free list + void reclaim(_ItemType *item) { if (item != NULL) m_freelist.append(*item); } + void reclaim(_ItemType &item) { m_freelist.append(item); } + + // reclaim all items from a list + void reclaim_all(simple_list<_ItemType> &list) { m_freelist.append_list(list); } + +private: + // internal state + simple_list<_ItemType> m_freelist; // list of free objects +}; #endif diff --git a/archivers/chd/delegate.h b/archivers/chd/delegate.h new file mode 100644 index 00000000..73360079 --- /dev/null +++ b/archivers/chd/delegate.h @@ -0,0 +1,943 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + delegate.h + + Templates and classes to enable delegates for callbacks. + +**************************************************************************** + + There are many implementations of delegate-like functionality for + C++ code, but none of them is a perfect drop-in fit for use in MAME. + In order to be useful in MAME, we need the following properties: + + * No significant overhead; we want to use these for memory + accessors, and memory accessor overhead is already the dominant + performance aspect for most drivers. + + * Existing static functions need to be bound with an additional + pointer parameter as the first argument. All existing + implementations that allow static function binding assume the + same signature as the member functions. + + * We must be able to bind the function separately from the + object. This is to allow configurations to bind functions + before the objects are created. + + Thus, the implementations below are based on existing works but are + really a new implementation that is specific to MAME. + + -------------------------------------------------------------------- + + The "compatible" version of delegates is based on an implementation + from Sergey Ryazanov, found here: + + http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx + + These delegates essentially generate a templated static stub function + for each target function. The static function takes the first + parameter, uses it as the object pointer, and calls through the + member function. For static functions, the stub is compatible with + the signature of a static function, so we just set the stub directly. + + Pros: + * should work with any modern compiler + * static bindings are just as fast as direct calls + + Cons: + * lots of little stub functions generated + * double-hops on member function calls means more overhead + * calling through stub functions repackages parameters + + -------------------------------------------------------------------- + + The "internal" version of delegates makes use of the internal + structure of member function pointers in order to convert them at + binding time into simple static function pointers. This only works + on platforms where object->func(p1, p2) is equivalent in calling + convention to func(object, p1, p2). + + Most of the information on how this works comes from Don Clugston + in this article: + + http://www.codeproject.com/KB/cpp/FastDelegate.aspx + + Pros: + * as fast as a standard function call in static and member cases + * no stub functions or double-hops needed + + Cons: + * requires internal knowledge of the member function pointer + * only works for GCC (for now; MSVC info is also readily available) + +***************************************************************************/ + +#pragma once + +#ifndef __DELEGATE_H__ +#define __DELEGATE_H__ + +// standard C++ includes +#include +#include +#include + + +//************************************************************************** +// 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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 +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 + 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 + void update_after_bind(_FunctionType &funcptr, delegate_generic_class *&object) + { + m_realobject = object; + object = reinterpret_cast(this); + funcptr = reinterpret_cast<_FunctionType>(m_stubfunction); + } + +private: + // helper stubs for calling encased member function pointers + template + static _ReturnType method_stub(delegate_generic_class *object) + { + delegate_mfp *_this = reinterpret_cast(object); + typedef _ReturnType (_FunctionClass::*mfptype)(); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(); + } + + template + static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1) + { + delegate_mfp *_this = reinterpret_cast(object); + typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1); + } + + template + static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2) + { + delegate_mfp *_this = reinterpret_cast(object); + typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2); + } + + template + static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3) + { + delegate_mfp *_this = reinterpret_cast(object); + typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3); + } + + template + static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4) + { + delegate_mfp *_this = reinterpret_cast(object); + typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4); + } + + template + static _ReturnType method_stub(delegate_generic_class *object, _P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5) + { + delegate_mfp *_this = reinterpret_cast(object); + typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5); + } + + template + 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(object); + typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6); + } + + template + 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(object); + typedef _ReturnType (_FunctionClass::*mfptype)(_P1Type p1, _P2Type p2, _P3Type p3, _P4Type p4, _P5Type p5, _P6Type p6, _P7Type p7); + mfptype &mfp = *reinterpret_cast(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7); + } + + template + 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(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(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8); + } + + template + 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(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(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8, p9); + } + + template + 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(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(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); + } + + template + 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(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(&_this->m_rawdata); + return (reinterpret_cast<_FunctionClass *>(_this->m_realobject)->*mfp)(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11); + } + + template + 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(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(&_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 + delegate_generic_function make_generic(_SourceType funcptr) + { + return reinterpret_cast(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 + 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 + 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 +class delegate_base +{ +public: + // define our traits + template + 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::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 + 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(object)); + } + + // construct from static function with object pointer + template + delegate_base(typename traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) + : m_function(reinterpret_cast(funcptr)), + m_object(NULL), + m_name(name), + m_latebinder(&late_bind_helper<_FunctionClass>), + m_raw_function(reinterpret_cast(funcptr)) + { + bind(reinterpret_cast(object)); + } + + // construct from static reference function with object reference + template + delegate_base(typename traits<_FunctionClass>::static_ref_func_type funcptr, const char *name, _FunctionClass *object) + : m_function(reinterpret_cast(funcptr)), + m_object(NULL), + m_name(name), + m_latebinder(&late_bind_helper<_FunctionClass>), + m_raw_function(reinterpret_cast(funcptr)) + { + bind(reinterpret_cast(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 + 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(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 +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 +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 1 parameter +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 2 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 3 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 4 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 5 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 6 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 7 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 8 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 9 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 10 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 11 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +// specialize for 12 parameters +template +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 delegate(typename basetype::template traits<_FunctionClass>::member_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template delegate(typename basetype::template traits<_FunctionClass>::static_func_type funcptr, const char *name, _FunctionClass *object) : basetype(funcptr, name, object) { } + template 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(this) = src; return *this; } +}; + +#endif /* __DELEGATE_H__ */ diff --git a/archivers/chd/eminline.h b/archivers/chd/eminline.h new file mode 100644 index 00000000..3e3f8398 --- /dev/null +++ b/archivers/chd/eminline.h @@ -0,0 +1,457 @@ +/*************************************************************************** + + eminline.h + + Definitions for inline functions that can be overriden by OSD- + specific code. + + Copyright Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __EMINLINE__ +#define __EMINLINE__ + +#if !defined(SDLMAME_NOASM) +/* we come with implementations for GCC x86 and PPC */ +#if defined(__GNUC__) + +#if defined(__i386__) || defined(__x86_64__) +#include "eigccx86.h" +#elif defined(__ppc__) || defined (__PPC__) || defined(__ppc64__) || defined(__PPC64__) +#include "eigccppc.h" +#else +#include "osinline.h" +#endif + +#else + +#include "osinline.h" + +#endif +#endif + + +/*************************************************************************** + INLINE MATH FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + mul_32x32 - perform a signed 32 bit x 32 bit + multiply and return the full 64 bit result +-------------------------------------------------*/ + +#ifndef mul_32x32 +INLINE INT64 mul_32x32(INT32 a, INT32 b) +{ + return (INT64)a * (INT64)b; +} +#endif + + +/*------------------------------------------------- + mulu_32x32 - perform an unsigned 32 bit x + 32 bit multiply and return the full 64 bit + result +-------------------------------------------------*/ + +#ifndef mulu_32x32 +INLINE UINT64 mulu_32x32(UINT32 a, UINT32 b) +{ + return (UINT64)a * (UINT64)b; +} +#endif + + +/*------------------------------------------------- + mul_32x32_hi - perform a signed 32 bit x 32 bit + multiply and return the upper 32 bits of the + result +-------------------------------------------------*/ + +#ifndef mul_32x32_hi +INLINE INT32 mul_32x32_hi(INT32 a, INT32 b) +{ + return (UINT32)(((INT64)a * (INT64)b) >> 32); +} +#endif + + +/*------------------------------------------------- + mulu_32x32_hi - perform an unsigned 32 bit x + 32 bit multiply and return the upper 32 bits + of the result +-------------------------------------------------*/ + +#ifndef mulu_32x32_hi +INLINE UINT32 mulu_32x32_hi(UINT32 a, UINT32 b) +{ + return (UINT32)(((UINT64)a * (UINT64)b) >> 32); +} +#endif + + +/*------------------------------------------------- + mul_32x32_shift - perform a signed 32 bit x + 32 bit multiply and shift the result by the + given number of bits before truncating the + result to 32 bits +-------------------------------------------------*/ + +#ifndef mul_32x32_shift +INLINE INT32 mul_32x32_shift(INT32 a, INT32 b, UINT8 shift) +{ + return (INT32)(((INT64)a * (INT64)b) >> shift); +} +#endif + + +/*------------------------------------------------- + mulu_32x32_shift - perform an unsigned 32 bit x + 32 bit multiply and shift the result by the + given number of bits before truncating the + result to 32 bits +-------------------------------------------------*/ + +#ifndef mulu_32x32_shift +INLINE UINT32 mulu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift) +{ + return (UINT32)(((UINT64)a * (UINT64)b) >> shift); +} +#endif + + +/*------------------------------------------------- + div_64x32 - perform a signed 64 bit x 32 bit + divide and return the 32 bit quotient +-------------------------------------------------*/ + +#ifndef div_64x32 +INLINE INT32 div_64x32(INT64 a, INT32 b) +{ + return a / (INT64)b; +} +#endif + + +/*------------------------------------------------- + divu_64x32 - perform an unsigned 64 bit x 32 bit + divide and return the 32 bit quotient +-------------------------------------------------*/ + +#ifndef divu_64x32 +INLINE UINT32 divu_64x32(UINT64 a, UINT32 b) +{ + return a / (UINT64)b; +} +#endif + + +/*------------------------------------------------- + div_64x32_rem - perform a signed 64 bit x 32 + bit divide and return the 32 bit quotient and + 32 bit remainder +-------------------------------------------------*/ + +#ifndef div_64x32_rem +INLINE INT32 div_64x32_rem(INT64 a, INT32 b, INT32 *remainder) +{ + INT32 res = div_64x32(a, b); + *remainder = a - ((INT64)b * res); + return res; +} +#endif + + +/*------------------------------------------------- + divu_64x32_rem - perform an unsigned 64 bit x + 32 bit divide and return the 32 bit quotient + and 32 bit remainder +-------------------------------------------------*/ + +#ifndef divu_64x32_rem +INLINE UINT32 divu_64x32_rem(UINT64 a, UINT32 b, UINT32 *remainder) +{ + UINT32 res = divu_64x32(a, b); + *remainder = a - ((UINT64)b * res); + return res; +} +#endif + + +/*------------------------------------------------- + div_32x32_shift - perform a signed divide of + two 32 bit values, shifting the first before + division, and returning the 32 bit quotient +-------------------------------------------------*/ + +#ifndef div_32x32_shift +INLINE INT32 div_32x32_shift(INT32 a, INT32 b, UINT8 shift) +{ + return ((INT64)a << shift) / (INT64)b; +} +#endif + + +/*------------------------------------------------- + divu_32x32_shift - perform an unsigned divide of + two 32 bit values, shifting the first before + division, and returning the 32 bit quotient +-------------------------------------------------*/ + +#ifndef divu_32x32_shift +INLINE UINT32 divu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift) +{ + return ((UINT64)a << shift) / (UINT64)b; +} +#endif + + +/*------------------------------------------------- + mod_64x32 - perform a signed 64 bit x 32 bit + divide and return the 32 bit remainder +-------------------------------------------------*/ + +#ifndef mod_64x32 +INLINE INT32 mod_64x32(INT64 a, INT32 b) +{ + return a - (b * div_64x32(a, b)); +} +#endif + + +/*------------------------------------------------- + modu_64x32 - perform an unsigned 64 bit x 32 bit + divide and return the 32 bit remainder +-------------------------------------------------*/ + +#ifndef modu_64x32 +INLINE UINT32 modu_64x32(UINT64 a, UINT32 b) +{ + return a - (b * divu_64x32(a, b)); +} +#endif + + +/*------------------------------------------------- + recip_approx - compute an approximate floating + point reciprocal +-------------------------------------------------*/ + +#ifndef recip_approx +INLINE float recip_approx(float value) +{ + return 1.0f / value; +} +#endif + + + +/*************************************************************************** + INLINE BIT MANIPULATION FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + count_leading_zeros - return the number of + leading zero bits in a 32-bit value +-------------------------------------------------*/ + +#ifndef count_leading_zeros +INLINE UINT8 count_leading_zeros(UINT32 val) +{ + UINT8 count; + for (count = 0; (INT32)val >= 0; count++) val <<= 1; + return count; +} +#endif + + +/*------------------------------------------------- + count_leading_ones - return the number of + leading one bits in a 32-bit value +-------------------------------------------------*/ + +#ifndef count_leading_ones +INLINE UINT8 count_leading_ones(UINT32 val) +{ + UINT8 count; + for (count = 0; (INT32)val < 0; count++) val <<= 1; + return count; +} +#endif + + + +/*************************************************************************** + INLINE SYNCHRONIZATION FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + compare_exchange32 - compare the 'compare' + value against the memory at 'ptr'; if equal, + swap in the 'exchange' value. Regardless, + return the previous value at 'ptr'. + + Note that the default implementation does + no synchronization. You MUST override this + in osinline.h for it to be useful in a + multithreaded environment! +-------------------------------------------------*/ + +#ifndef compare_exchange32 +INLINE INT32 compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange) +{ + INT32 oldval = *ptr; + if (*ptr == compare) + *ptr = exchange; + return oldval; +} +#endif + + +/*------------------------------------------------- + compare_exchange64 - compare the 'compare' + value against the memory at 'ptr'; if equal, + swap in the 'exchange' value. Regardless, + return the previous value at 'ptr'. + + Note that the default implementation does + no synchronization. You MUST override this + in osinline.h for it to be useful in a + multithreaded environment! +-------------------------------------------------*/ + +#ifdef PTR64 +#ifndef compare_exchange64 +INLINE INT64 compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange) +{ + INT64 oldval = *ptr; + if (*ptr == compare) + *ptr = exchange; + return oldval; +} +#endif +#endif + + +/*------------------------------------------------- + compare_exchange_ptr - compare the 'compare' + value against the memory at 'ptr'; if equal, + swap in the 'exchange' value. Regardless, + return the previous value at 'ptr'. +-------------------------------------------------*/ + +#ifndef compare_exchange_ptr +INLINE void *compare_exchange_ptr(void * volatile *ptr, void *compare, void *exchange) +{ +#ifdef PTR64 + INT64 result; + result = compare_exchange64((INT64 volatile *)ptr, (INT64)compare, (INT64)exchange); +#else + INT32 result; + result = compare_exchange32((INT32 volatile *)ptr, (INT32)compare, (INT32)exchange); +#endif + return (void *)result; +} +#endif + + +/*------------------------------------------------- + atomic_exchange32 - atomically exchange the + exchange value with the memory at 'ptr', + returning the original value. + + Note that the default implementation does + no synchronization. You MUST override this + in osinline.h for it to be useful in a + multithreaded environment! +-------------------------------------------------*/ + +#ifndef atomic_exchange32 +INLINE INT32 atomic_exchange32(INT32 volatile *ptr, INT32 exchange) +{ + INT32 oldval = *ptr; + *ptr = exchange; + return oldval; +} +#endif + + +/*------------------------------------------------- + atomic_add32 - atomically add the delta value + to the memory at 'ptr', returning the final + result. + + Note that the default implementation does + no synchronization. You MUST override this + in osinline.h for it to be useful in a + multithreaded environment! +-------------------------------------------------*/ + +#ifndef atomic_add32 +INLINE INT32 atomic_add32(INT32 volatile *ptr, INT32 delta) +{ + return (*ptr += delta); +} +#endif + + +/*------------------------------------------------- + atomic_increment32 - atomically increment the + 32-bit value in memory at 'ptr', returning the + final result. + + Note that the default implementation does + no synchronization. You MUST override this + in osinline.h for it to be useful in a + multithreaded environment! +-------------------------------------------------*/ + +#ifndef atomic_increment32 +INLINE INT32 atomic_increment32(INT32 volatile *ptr) +{ + return atomic_add32(ptr, 1); +} +#endif + + +/*------------------------------------------------- + atomic_decrement32 - atomically decrement the + 32-bit value in memory at 'ptr', returning the + final result. + + Note that the default implementation does + no synchronization. You MUST override this + in osinline.h for it to be useful in a + multithreaded environment! +-------------------------------------------------*/ + +#ifndef atomic_decrement32 +INLINE INT32 atomic_decrement32(INT32 volatile *ptr) +{ + return atomic_add32(ptr, -1); +} +#endif + + + +/*************************************************************************** + INLINE TIMING FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_profile_ticks - return a tick counter + from the processor that can be used for + profiling. It does not need to run at any + particular rate. +-------------------------------------------------*/ + +#ifndef get_profile_ticks +INLINE INT64 get_profile_ticks(void) +{ + return osd_ticks(); +} +#endif + +#endif /* __EMINLINE__ */ diff --git a/archivers/chd/flac.cpp b/archivers/chd/flac.cpp index 955a2848..2ab3a7a8 100644 --- a/archivers/chd/flac.cpp +++ b/archivers/chd/flac.cpp @@ -1,42 +1,12 @@ #include "chdtypes.h" - +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** flac.c FLAC compression wrappers -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #include "flac.h" @@ -65,7 +35,7 @@ flac_encoder::flac_encoder(void *buffer, UINT32 buflength) } -flac_encoder::flac_encoder(struct zfile *file) +flac_encoder::flac_encoder(core_file &file) { init_common(); reset(file); @@ -130,12 +100,12 @@ bool flac_encoder::reset(void *buffer, UINT32 buflength) // reset - reset state with new file parameters //------------------------------------------------- -bool flac_encoder::reset(struct zfile *file) +bool flac_encoder::reset(core_file &file) { // configure the output m_compressed_start = NULL; m_compressed_length = 0; - m_file = file; + m_file = &file; return reset(); } @@ -215,7 +185,7 @@ UINT32 flac_encoder::finish() { // process the data and return the amount written FLAC__stream_encoder_finish(m_encoder); - return (m_file != NULL) ? zfile_ftell(m_file) : m_compressed_offset; + return (m_file != NULL) ? core_ftell(m_file) : m_compressed_offset; } @@ -282,7 +252,7 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf { int count = bytes - offset; if (m_file != NULL) - zfile_fwrite(buffer, count, 1, m_file); + core_fwrite(m_file, buffer, count); else { if (m_compressed_offset + count <= m_compressed_length) @@ -296,7 +266,6 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf } - //************************************************************************** // FLAC DECODER //************************************************************************** @@ -307,12 +276,12 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf flac_decoder::flac_decoder() : m_decoder(FLAC__stream_decoder_new()), - m_file(NULL), - m_compressed_offset(0), - m_compressed_start(NULL), - m_compressed_length(0), - m_compressed2_start(NULL), - m_compressed2_length(0) + m_file(NULL), + m_compressed_offset(0), + m_compressed_start(NULL), + m_compressed_length(0), + m_compressed2_start(NULL), + m_compressed2_length(0) { } @@ -323,12 +292,12 @@ flac_decoder::flac_decoder() flac_decoder::flac_decoder(const void *buffer, UINT32 length, const void *buffer2, UINT32 length2) : m_decoder(FLAC__stream_decoder_new()), - m_file(NULL), - m_compressed_offset(0), - m_compressed_start(reinterpret_cast(buffer)), - m_compressed_length(length), - m_compressed2_start(reinterpret_cast(buffer2)), - m_compressed2_length(length2) + m_file(NULL), + m_compressed_offset(0), + m_compressed_start(reinterpret_cast(buffer)), + m_compressed_length(length), + m_compressed2_start(reinterpret_cast(buffer2)), + m_compressed2_length(length2) { reset(); } @@ -338,14 +307,14 @@ flac_decoder::flac_decoder(const void *buffer, UINT32 length, const void *buffer // flac_decoder - constructor //------------------------------------------------- -flac_decoder::flac_decoder(struct zfile *file) +flac_decoder::flac_decoder(core_file &file) : m_decoder(FLAC__stream_decoder_new()), - m_file(file), - m_compressed_offset(0), - m_compressed_start(NULL), - m_compressed_length(0), - m_compressed2_start(NULL), - m_compressed2_length(0) + m_file(&file), + m_compressed_offset(0), + m_compressed_start(NULL), + m_compressed_length(0), + m_compressed2_start(NULL), + m_compressed2_length(0) { reset(); } @@ -379,7 +348,7 @@ bool flac_decoder::reset() &flac_decoder::metadata_callback_static, &flac_decoder::error_callback_static, this) != FLAC__STREAM_DECODER_INIT_STATUS_OK) return false; - return FLAC__stream_decoder_process_until_end_of_metadata(m_decoder) != 0; + return FLAC__stream_decoder_process_until_end_of_metadata(m_decoder); } @@ -408,19 +377,19 @@ bool flac_decoder::reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_si // modify the template header with our parameters static const UINT8 s_header_template[0x2a] = { - 0x66, 0x4C, 0x61, 0x43, // +00: 'fLaC' stream header - 0x80, // +04: metadata block type 0 (STREAMINFO), + 0x66, 0x4C, 0x61, 0x43, // +00: 'fLaC' stream header + 0x80, // +04: metadata block type 0 (STREAMINFO), // flagged as last block - 0x00, 0x00, 0x22, // +05: metadata block length = 0x22 - 0x00, 0x00, // +08: minimum block size - 0x00, 0x00, // +0A: maximum block size - 0x00, 0x00, 0x00, // +0C: minimum frame size (0 == unknown) - 0x00, 0x00, 0x00, // +0F: maximum frame size (0 == unknown) + 0x00, 0x00, 0x22, // +05: metadata block length = 0x22 + 0x00, 0x00, // +08: minimum block size + 0x00, 0x00, // +0A: maximum block size + 0x00, 0x00, 0x00, // +0C: minimum frame size (0 == unknown) + 0x00, 0x00, 0x00, // +0F: maximum frame size (0 == unknown) 0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, // +12: sample rate (0x0ac44 == 44100), // numchannels (2), sample bits (16), // samples in stream (0 == unknown) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // +1A: MD5 signature (0 == none) - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // // +2A: start of stream data }; memcpy(m_custom_header, s_header_template, sizeof(s_header_template)); @@ -444,9 +413,9 @@ bool flac_decoder::reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_si // reset - reset state with new file parameter //------------------------------------------------- -bool flac_decoder::reset(struct zfile *file) +bool flac_decoder::reset(core_file &file) { - m_file = file; + m_file = &file; m_compressed_start = NULL; m_compressed_length = 0; m_compressed2_start = NULL; @@ -541,7 +510,7 @@ FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], s // if a file, just read if (m_file != NULL) - *bytes = zfile_fread(buffer, 1, expected, m_file); + *bytes = core_fread(m_file, buffer, expected); // otherwise, copy from memory else diff --git a/archivers/chd/flac.h b/archivers/chd/flac.h index 3f2cfe3a..6a3f4e6b 100644 --- a/archivers/chd/flac.h +++ b/archivers/chd/flac.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** flac.h FLAC compression wrappers -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -46,7 +17,7 @@ #include "corefile.h" #ifdef FLAC__NO_DLL -#include "flac/all.h" +#include "../../lib/libflac/include/FLAC/all.h" #else #include #endif @@ -64,7 +35,7 @@ public: // construction/destruction flac_encoder(); flac_encoder(void *buffer, UINT32 buflength); - flac_encoder(struct zfile *file); + flac_encoder(core_file &file); ~flac_encoder(); // configuration @@ -80,7 +51,7 @@ public: // reset bool reset(); bool reset(void *buffer, UINT32 buflength); - bool reset(struct zfile *file); + bool reset(core_file &file); // encode a buffer bool encode_interleaved(const INT16 *samples, UINT32 samples_per_channel, bool swap_endian = false); @@ -96,21 +67,21 @@ private: FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame); // internal state - FLAC__StreamEncoder * m_encoder; // actual encoder - struct zfile * m_file; // output file - UINT32 m_compressed_offset; // current offset with the compressed stream - FLAC__byte * m_compressed_start; // start of compressed data - UINT32 m_compressed_length; // length of the compressed stream + FLAC__StreamEncoder * m_encoder; // actual encoder + core_file * m_file; // output file + UINT32 m_compressed_offset; // current offset with the compressed stream + FLAC__byte * m_compressed_start; // start of compressed data + UINT32 m_compressed_length; // length of the compressed stream // parameters - UINT32 m_sample_rate; // sample rate - UINT8 m_channels; // number of channels - UINT32 m_block_size; // block size + UINT32 m_sample_rate; // sample rate + UINT8 m_channels; // number of channels + UINT32 m_block_size; // block size // header stripping - bool m_strip_metadata; // strip the the metadata? - UINT32 m_ignore_bytes; // how many bytes to ignore when writing - bool m_found_audio; // have we hit the audio yet? + bool m_strip_metadata; // strip the the metadata? + UINT32 m_ignore_bytes; // how many bytes to ignore when writing + bool m_found_audio; // have we hit the audio yet? }; @@ -122,7 +93,7 @@ public: // construction/destruction flac_decoder(); flac_decoder(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0); - flac_decoder(struct zfile *file); + flac_decoder(core_file &file); ~flac_decoder(); // getters (valid after reset) @@ -137,7 +108,7 @@ public: bool reset(); bool reset(const void *buffer, UINT32 length, const void *buffer2 = NULL, UINT32 length2 = 0); bool reset(UINT32 sample_rate, UINT8 num_channels, UINT32 block_size, const void *buffer, UINT32 length); - bool reset(struct zfile *file); + bool reset(core_file &file); // decode to a buffer; num_samples must be a multiple of the block size bool decode_interleaved(INT16 *samples, UINT32 num_samples, bool swap_endian = false); @@ -157,21 +128,21 @@ private: static void error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); // output state - FLAC__StreamDecoder * m_decoder; // actual encoder - struct zfile * m_file; // output file - UINT32 m_sample_rate; // decoded sample rate - UINT8 m_channels; // decoded number of channels - UINT8 m_bits_per_sample; // decoded bits per sample - UINT32 m_compressed_offset; // current offset in compressed data - const FLAC__byte * m_compressed_start; // start of compressed data - UINT32 m_compressed_length; // length of compressed data - const FLAC__byte * m_compressed2_start; // start of compressed data - UINT32 m_compressed2_length; // length of compressed data - INT16 * m_uncompressed_start[8];// pointer to start of uncompressed data (up to 8 streams) - UINT32 m_uncompressed_offset; // current position in uncompressed data - UINT32 m_uncompressed_length; // length of uncompressed data - bool m_uncompressed_swap; // swap uncompressed sample data - UINT8 m_custom_header[0x2a]; // custom header + FLAC__StreamDecoder * m_decoder; // actual encoder + core_file * m_file; // output file + UINT32 m_sample_rate; // decoded sample rate + UINT8 m_channels; // decoded number of channels + UINT8 m_bits_per_sample; // decoded bits per sample + UINT32 m_compressed_offset; // current offset in compressed data + const FLAC__byte * m_compressed_start; // start of compressed data + UINT32 m_compressed_length; // length of compressed data + const FLAC__byte * m_compressed2_start; // start of compressed data + UINT32 m_compressed2_length; // length of compressed data + INT16 * m_uncompressed_start[8];// pointer to start of uncompressed data (up to 8 streams) + UINT32 m_uncompressed_offset; // current position in uncompressed data + UINT32 m_uncompressed_length; // length of uncompressed data + bool m_uncompressed_swap; // swap uncompressed sample data + UINT8 m_custom_header[0x2a]; // custom header }; diff --git a/archivers/chd/harddisk.cpp b/archivers/chd/harddisk.cpp new file mode 100644 index 00000000..a71bb498 --- /dev/null +++ b/archivers/chd/harddisk.cpp @@ -0,0 +1,126 @@ +#include "chdtypes.h" +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + hardisk.c + + Generic MAME hard disk implementation, with differencing files + +***************************************************************************/ + +#include "harddisk.h" + +#include + + +/*************************************************************************** + 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); +} diff --git a/archivers/chd/harddisk.h b/archivers/chd/harddisk.h new file mode 100644 index 00000000..5b32c905 --- /dev/null +++ b/archivers/chd/harddisk.h @@ -0,0 +1,49 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + harddisk.h + + Generic MAME hard disk implementation, with differencing files + +***************************************************************************/ + +#pragma once + +#ifndef __HARDDISK_H__ +#define __HARDDISK_H__ + +#include "osdcore.h" +#include "chd.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct hard_disk_file; + +struct hard_disk_info +{ + UINT32 cylinders; + UINT32 heads; + UINT32 sectors; + UINT32 sectorbytes; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +hard_disk_file *hard_disk_open(chd_file *chd); +void hard_disk_close(hard_disk_file *file); + +chd_file *hard_disk_get_chd(hard_disk_file *file); +hard_disk_info *hard_disk_get_info(hard_disk_file *file); + +UINT32 hard_disk_read(hard_disk_file *file, UINT32 lbasector, void *buffer); +UINT32 hard_disk_write(hard_disk_file *file, UINT32 lbasector, const void *buffer); + +#endif /* __HARDDISK_H__ */ diff --git a/archivers/chd/hashing.cpp b/archivers/chd/hashing.cpp index 0dff0d68..17b6c9f9 100644 --- a/archivers/chd/hashing.cpp +++ b/archivers/chd/hashing.cpp @@ -1,44 +1,14 @@ +#include "chdtypes.h" +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** hashing.c Hashing helper classes. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ -#include "chdtypes.h" - #include "hashing.h" #include "zlib.h" @@ -264,39 +234,39 @@ void crc16_creator::append(const void *data, UINT32 length) { static const UINT16 s_table[256] = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 - }; + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 + }; const UINT8 *src = reinterpret_cast(data); @@ -305,4 +275,4 @@ void crc16_creator::append(const void *data, UINT32 length) while (length-- != 0) crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++]; m_accum.m_raw = crc; -} \ No newline at end of file +} diff --git a/archivers/chd/hashing.h b/archivers/chd/hashing.h index d98b617b..78e02c6e 100644 --- a/archivers/chd/hashing.h +++ b/archivers/chd/hashing.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** hashing.h Hashing helper classes. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -42,6 +13,7 @@ #ifndef __HASHING_H__ #define __HASHING_H__ +#include "osdcore.h" #include "astring.h" #include "md5.h" #include "sha1.h" @@ -98,7 +70,7 @@ public: protected: // internal state - struct sha1_ctx m_context; // internal context + struct sha1_ctx m_context; // internal context }; @@ -148,7 +120,7 @@ public: protected: // internal state - struct MD5Context m_context; // internal context + struct MD5Context m_context; // internal context }; @@ -194,7 +166,7 @@ public: protected: // internal state - crc32_t m_accum; // internal accumulator + crc32_t m_accum; // internal accumulator }; @@ -240,7 +212,7 @@ public: protected: // internal state - crc16_t m_accum; // internal accumulator + crc16_t m_accum; // internal accumulator }; diff --git a/archivers/chd/huffman.cpp b/archivers/chd/huffman.cpp index ff232d11..aa38c93e 100644 --- a/archivers/chd/huffman.cpp +++ b/archivers/chd/huffman.cpp @@ -1,43 +1,12 @@ - #include "chdtypes.h" - +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** huffman.c Static Huffman compression and decompression helpers. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - **************************************************************************** Maximum codelength is officially (alphabetsize - 1). This would be 255 bits @@ -139,7 +108,7 @@ // MACROS //************************************************************************** -#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f)) +#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f)) @@ -154,12 +123,12 @@ huffman_context_base::huffman_context_base(int numcodes, int maxbits, lookup_value *lookup, UINT32 *histo, node_t *nodes) : m_numcodes(numcodes), - m_maxbits(maxbits), - m_prevdata(0), - m_rleremaining(0), - m_lookup(lookup), - m_datahisto(histo), - m_huffnode(nodes) + m_maxbits(maxbits), + m_prevdata(0), + m_rleremaining(0), + m_lookup(lookup), + m_datahisto(histo), + m_huffnode(nodes) { // limit to 24 bits if (maxbits > 24) @@ -347,7 +316,7 @@ huffman_error huffman_context_base::import_tree_huffman(bitstream_in &bitbuf) huffman_error huffman_context_base::export_tree_huffman(bitstream_out &bitbuf) { // first RLE compress the lengths of all the nodes - dynamic_array rle_data(m_numcodes); + dynamic_buffer rle_data(m_numcodes); UINT8 *dest = rle_data; dynamic_array rle_lengths(m_numcodes/3); UINT16 *lengths = rle_lengths; @@ -539,9 +508,9 @@ int CLIB_DECL huffman_context_base::tree_node_compare(const void *item1, const v const node_t *node1 = *(const node_t **)item1; const node_t *node2 = *(const node_t **)item2; if (node2->m_weight != node1->m_weight) - return node2->m_weight - node1->m_weight; + return node2->m_weight - node1->m_weight; if (node2->m_bits - node1->m_bits == 0) - fprintf(stderr, "identical node sort keys, should not happen!\n"); + fprintf(stderr, "identical node sort keys, should not happen!\n"); return (int)node1->m_bits - (int)node2->m_bits; } diff --git a/archivers/chd/huffman.h b/archivers/chd/huffman.h index 47525d82..76ee1b9c 100644 --- a/archivers/chd/huffman.h +++ b/archivers/chd/huffman.h @@ -1,40 +1,11 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles /*************************************************************************** huffman.h Static Huffman compression and decompression helpers. -**************************************************************************** - - Copyright Aaron Giles - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - * Neither the name 'MAME' nor the names of its contributors may be - used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - ***************************************************************************/ #pragma once @@ -78,11 +49,11 @@ protected: // a node in the huffman tree struct node_t { - node_t * m_parent; // pointer to parent node - UINT32 m_count; // number of hits on this node - UINT32 m_weight; // assigned weight of this node - UINT32 m_bits; // bits used to encode the node - UINT8 m_numbits; // number of bits needed for this node + node_t * m_parent; // pointer to parent node + UINT32 m_count; // number of hits on this node + UINT32 m_weight; // assigned weight of this node + UINT32 m_bits; // bits used to encode the node + UINT8 m_numbits; // number of bits needed for this node }; // construction/destruction @@ -108,13 +79,13 @@ protected: protected: // internal state - UINT32 m_numcodes; // number of total codes being processed - UINT8 m_maxbits; // maximum bits per code - UINT8 m_prevdata; // value of the previous data (for delta-RLE encoding) - int m_rleremaining; // number of RLE bytes remaining (for delta-RLE encoding) - lookup_value * m_lookup; // pointer to the lookup table - UINT32 * m_datahisto; // histogram of data values - node_t * m_huffnode; // array of nodes + UINT32 m_numcodes; // number of total codes being processed + UINT8 m_maxbits; // maximum bits per code + UINT8 m_prevdata; // value of the previous data (for delta-RLE encoding) + int m_rleremaining; // number of RLE bytes remaining (for delta-RLE encoding) + lookup_value * m_lookup; // pointer to the lookup table + UINT32 * m_datahisto; // histogram of data values + node_t * m_huffnode; // array of nodes }; @@ -141,8 +112,8 @@ public: private: // array versions of the info we need - UINT32 m_datahisto_array[_NumCodes]; - node_t m_huffnode_array[_NumCodes * 2]; + UINT32 m_datahisto_array[_NumCodes]; + node_t m_huffnode_array[_NumCodes * 2]; }; @@ -166,8 +137,8 @@ public: private: // array versions of the info we need - node_t m_huffnode_array[_NumCodes]; - lookup_value m_lookup_array[1 << _MaxBits]; + node_t m_huffnode_array[_NumCodes]; + lookup_value m_lookup_array[1 << _MaxBits]; }; diff --git a/archivers/chd/md5.cpp b/archivers/chd/md5.cpp index 0c9c91c7..dbf2b01f 100644 --- a/archivers/chd/md5.cpp +++ b/archivers/chd/md5.cpp @@ -1,3 +1,4 @@ +#include "chdtypes.h" /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was @@ -20,7 +21,7 @@ * Still in the public domain. */ -#include /* for memcpy() */ +#include /* for memcpy() */ #include "md5.h" @@ -69,9 +70,9 @@ MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) t = ctx->bytes[0]; if ((ctx->bytes[0] = t + len) < t) - ctx->bytes[1]++; /* Carry from low to high */ + ctx->bytes[1]++; /* Carry from low to high */ - t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ if (t > len) { memcpy((md5byte *)ctx->in + 64 - t, buf, len); return; @@ -103,7 +104,7 @@ MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) void MD5Final(md5byte digest[16], struct MD5Context *ctx) { - int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ md5byte *p = (md5byte *)ctx->in + count; /* Set the first char of padding to 0x80. There is always room. */ @@ -112,7 +113,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx) /* Bytes of padding needed to make 56 bytes (-8..55) */ count = 56 - 1 - count; - if (count < 0) { /* Padding forces an extra block */ + if (count < 0) { /* Padding forces an extra block */ memset(p, 0, count + 8); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); @@ -129,7 +130,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx) byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(MD5Context)); /* In case it's sensitive */ + memset(ctx, 0, sizeof(MD5Context)); /* In case it's sensitive */ } #ifndef ASM_MD5 @@ -144,7 +145,7 @@ MD5Final(md5byte digest[16], struct MD5Context *ctx) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f,w,x,y,z,in,s) \ - (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to diff --git a/archivers/chd/osdcomm.h b/archivers/chd/osdcomm.h new file mode 100644 index 00000000..4c783557 --- /dev/null +++ b/archivers/chd/osdcomm.h @@ -0,0 +1,226 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + osdcomm.h + + Common definitions shared by the OSD layer. This includes the most + fundamental integral types as well as compiler-specific tweaks. + +***************************************************************************/ + +#pragma once + +#ifndef __OSDCOMM_H__ +#define __OSDCOMM_H__ + +#include +#include + + +/*************************************************************************** + 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__ */ diff --git a/archivers/chd/osdcore.h b/archivers/chd/osdcore.h index e69de29b..d46938f2 100644 --- a/archivers/chd/osdcore.h +++ b/archivers/chd/osdcore.h @@ -0,0 +1,939 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + osdcore.h + + Core OS-dependent code interface. + +**************************************************************************** + + The prototypes in this file describe the interfaces that the MAME core + and various tools rely upon to interact with the outside world. They are + broken out into several categories. + +***************************************************************************/ + +#pragma once + +#ifndef __OSDCORE_H__ +#define __OSDCORE_H__ + +#include "osdcomm.h" +#include "delegate.h" + +/*************************************************************************** + FILE I/O INTERFACES +***************************************************************************/ + +/* Make sure we have a path separator (default to /) */ +#ifndef PATH_SEPARATOR +#define PATH_SEPARATOR "/" +#endif + +/* flags controlling file access */ +#define OPEN_FLAG_READ 0x0001 /* open for read */ +#define OPEN_FLAG_WRITE 0x0002 /* open for write */ +#define OPEN_FLAG_CREATE 0x0004 /* create & truncate file */ +#define OPEN_FLAG_CREATE_PATHS 0x0008 /* create paths as necessary */ +#define OPEN_FLAG_NO_PRELOAD 0x0010 /* do not decompress on open */ + +/* error codes returned by routines below */ +enum file_error +{ + FILERR_NONE, + FILERR_FAILURE, + FILERR_OUT_OF_MEMORY, + FILERR_NOT_FOUND, + FILERR_ACCESS_DENIED, + FILERR_ALREADY_OPEN, + FILERR_TOO_MANY_FILES, + FILERR_INVALID_DATA, + FILERR_INVALID_ACCESS +}; + +/* osd_file is an opaque type which represents an open file */ +struct osd_file; + +/*----------------------------------------------------------------------------- + osd_open: open a new file. + + Parameters: + + path - path to the file to open + + openflags - some combination of: + + OPEN_FLAG_READ - open the file for read access + OPEN_FLAG_WRITE - open the file for write access + OPEN_FLAG_CREATE - create/truncate the file when opening + OPEN_FLAG_CREATE_PATHS - specifies that non-existant paths + should be created if necessary + + file - pointer to an osd_file * to receive the newly-opened file + handle; this is only valid if the function returns FILERR_NONE + + filesize - pointer to a UINT64 to receive the size of the opened + file; this is only valid if the function returns FILERR_NONE + + Return value: + + a file_error describing any error that occurred while opening + the file, or FILERR_NONE if no error occurred + + Notes: + + This function is called by core_fopen and several other places in + the core to access files. These functions will construct paths by + concatenating various search paths held in the options.c options + database with partial paths specified by the core. The core assumes + that the path separator is the first character of the string + PATH_SEPARATOR, but does not interpret any path separators in the + search paths, so if you use a different path separator in a search + path, you may get a mixture of PATH_SEPARATORs (from the core) and + alternate path separators (specified by users and placed into the + options database). +-----------------------------------------------------------------------------*/ +file_error osd_open(const TCHAR *path, UINT32 openflags, osd_file **file, UINT64 *filesize); + + +/*----------------------------------------------------------------------------- + osd_close: close an open file + + Parameters: + + file - handle to a file previously opened via osd_open + + Return value: + + a file_error describing any error that occurred while closing + the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_close(osd_file *file); + + +/*----------------------------------------------------------------------------- + osd_read: read from an open file + + Parameters: + + file - handle to a file previously opened via osd_open + + buffer - pointer to memory that will receive the data read + + offset - offset within the file to read from + + length - number of bytes to read from the file + + actual - pointer to a UINT32 to receive the number of bytes actually + read during the operation; valid only if the function returns + FILERR_NONE + + Return value: + + a file_error describing any error that occurred while reading + from the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_read(osd_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual); + + +/*----------------------------------------------------------------------------- + osd_write: write to an open file + + Parameters: + + file - handle to a file previously opened via osd_open + + buffer - pointer to memory that contains the data to write + + offset - offset within the file to write to + + length - number of bytes to write to the file + + actual - pointer to a UINT32 to receive the number of bytes actually + written during the operation; valid only if the function returns + FILERR_NONE + + Return value: + + a file_error describing any error that occurred while writing to + the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_write(osd_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual); + + +/*----------------------------------------------------------------------------- + osd_truncate: change the size of an open file + + Parameters: + + file - handle to a file previously opened via osd_open + + offset - future size of the file + + Return value: + + a file_error describing any error that occurred while writing to + the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_truncate(osd_file *file, UINT64 offset); + + +/*----------------------------------------------------------------------------- + osd_rmfile: deletes a file + + Parameters: + + filename - path to file to delete + + Return value: + + a file_error describing any error that occurred while deleting + the file, or FILERR_NONE if no error occurred +-----------------------------------------------------------------------------*/ +file_error osd_rmfile(const TCHAR *filename); + + +/*----------------------------------------------------------------------------- + osd_get_physical_drive_geometry: if the given path points to a physical + drive, return the geometry of that drive + + Parameters: + + filename - pointer to a path which might describe a physical drive + + cylinders - pointer to a UINT32 to receive the number of cylinders + of the physical drive + + heads - pointer to a UINT32 to receive the number of heads per + cylinder of the physical drive + + sectors - pointer to a UINT32 to receive the number of sectors per + cylinder of the physical drive + + bps - pointer to a UINT32 to receive the number of bytes per sector + of the physical drive + + Return value: + + TRUE if the filename points to a physical drive and if the values + pointed to by cylinders, heads, sectors, and bps are valid; FALSE in + any other case +-----------------------------------------------------------------------------*/ +int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UINT32 *heads, UINT32 *sectors, UINT32 *bps); + + +/*----------------------------------------------------------------------------- + osd_uchar_from_osdchar: convert the given character or sequence of + characters from the OS-default encoding to a Unicode character + + Parameters: + + uchar - pointer to a UINT32 to receive the resulting unicode + character + + osdchar - pointer to one or more chars that are in the OS-default + encoding + + count - number of characters provided in the OS-default encoding + + Return value: + + The number of characters required to form a Unicode character. +-----------------------------------------------------------------------------*/ +int osd_uchar_from_osdchar(UINT32 /* unicode_char */ *uchar, const char *osdchar, size_t count); + + + +/*************************************************************************** + DIRECTORY INTERFACES +***************************************************************************/ + +/* types of directory entries that can be returned */ +enum osd_dir_entry_type +{ + ENTTYPE_NONE, + ENTTYPE_FILE, + ENTTYPE_DIR, + ENTTYPE_OTHER +}; + +/* osd_directory is an opaque type which represents an open directory */ +struct osd_directory; + +/* osd_directory_entry contains basic information about a file when iterating through */ +/* a directory */ +struct osd_directory_entry +{ + const char * name; /* name of the entry */ + osd_dir_entry_type type; /* type of the entry */ + UINT64 size; /* size of the entry */ +}; + + +/*----------------------------------------------------------------------------- + osd_opendir: open a directory for iteration + + Parameters: + + dirname - path to the directory in question + + Return value: + + upon success, this function should return an osd_directory pointer + which contains opaque data necessary to traverse the directory; on + failure, this function should return NULL +-----------------------------------------------------------------------------*/ +osd_directory *osd_opendir(const char *dirname); + + +/*----------------------------------------------------------------------------- + osd_readdir: return information about the next entry in the directory + + Parameters: + + dir - pointer to an osd_directory that was returned from a prior + call to osd_opendir + + Return value: + + a constant pointer to an osd_directory_entry representing the current item + in the directory, or NULL, indicating that no more entries are + present +-----------------------------------------------------------------------------*/ +const osd_directory_entry *osd_readdir(osd_directory *dir); + + +/*----------------------------------------------------------------------------- + osd_closedir: close an open directory for iteration + + Parameters: + + dir - pointer to an osd_directory that was returned from a prior + call to osd_opendir + + Return value: + + frees any allocated memory and resources associated with the open + directory +-----------------------------------------------------------------------------*/ +void osd_closedir(osd_directory *dir); + + +/*----------------------------------------------------------------------------- + osd_is_absolute_path: returns whether the specified path is absolute + + Parameters: + + path - the path in question + + Return value: + + non-zero if the path is absolute, zero otherwise +-----------------------------------------------------------------------------*/ +int osd_is_absolute_path(const char *path); + + + +/*************************************************************************** + TIMING INTERFACES +***************************************************************************/ + +/* a osd_ticks_t is a 64-bit unsigned integer that is used as a core type in timing interfaces */ +typedef UINT64 osd_ticks_t; + + +/*----------------------------------------------------------------------------- + osd_ticks: return the current running tick counter + + Parameters: + + None + + Return value: + + an osd_ticks_t value which represents the current tick counter + + Notes: + + The resolution of this counter should be 1ms or better for accurate + performance. It is also important that the source of this timer be + accurate. It is ok if this call is not ultra-fast, since it is + primarily used for once/frame synchronization. +-----------------------------------------------------------------------------*/ +osd_ticks_t osd_ticks(void); + + +/*----------------------------------------------------------------------------- + osd_ticks_per_second: return the number of ticks per second + + Parameters: + + None + + Return value: + + an osd_ticks_t value which represents the number of ticks per + second +-----------------------------------------------------------------------------*/ +osd_ticks_t osd_ticks_per_second(void); + + +/*----------------------------------------------------------------------------- + osd_sleep: sleep for the specified time interval + + Parameters: + + duration - an osd_ticks_t value that specifies how long we should + sleep + + Return value: + + None + + Notes: + + The OSD layer should try to sleep for as close to the specified + duration as possible, or less. This is used as a mechanism to + "give back" unneeded time to other programs running in the system. + On a simple, non multitasking system, this routine can be a no-op. + If there is significant volatility in the amount of time that the + sleep occurs for, the OSD layer should strive to sleep for less time + than specified rather than sleeping too long. +-----------------------------------------------------------------------------*/ +void osd_sleep(osd_ticks_t duration); + + + +/*************************************************************************** + SYNCHRONIZATION INTERFACES +***************************************************************************/ + +/* osd_lock is an opaque type which represents a recursive lock/mutex */ +struct osd_lock; + + +/*----------------------------------------------------------------------------- + osd_lock_alloc: allocate a new lock + + Parameters: + + None. + + Return value: + + A pointer to the allocated lock. +-----------------------------------------------------------------------------*/ +osd_lock *osd_lock_alloc(void); + + +/*----------------------------------------------------------------------------- + osd_lock_acquire: acquire a lock, blocking until it can be acquired + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + None. + + Notes: + + osd_locks are defined to be recursive. If the current thread already + owns the lock, this function should return immediately. +-----------------------------------------------------------------------------*/ +void osd_lock_acquire(osd_lock *lock); + + +/*----------------------------------------------------------------------------- + osd_lock_try: attempt to acquire a lock + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + TRUE if the lock was available and was acquired successfully. + FALSE if the lock was already in used by another thread. +-----------------------------------------------------------------------------*/ +int osd_lock_try(osd_lock *lock); + + +/*----------------------------------------------------------------------------- + osd_lock_release: release control of a lock that has been acquired + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + None. +-----------------------------------------------------------------------------*/ +void osd_lock_release(osd_lock *lock); + + +/*----------------------------------------------------------------------------- + osd_lock_free: free the memory and resources associated with an osd_lock + + Parameters: + + lock - a pointer to a previously allocated osd_lock. + + Return value: + + None. +-----------------------------------------------------------------------------*/ +void osd_lock_free(osd_lock *lock); + + + +/*************************************************************************** + WORK ITEM INTERFACES +***************************************************************************/ + +/* this is the maximum number of supported threads for a single work queue */ +/* threadid values are expected to range from 0..WORK_MAX_THREADS-1 */ +#define WORK_MAX_THREADS 16 + +/* these flags can be set when creating a queue to give hints to the code about + how to configure the queue */ +#define WORK_QUEUE_FLAG_IO 0x0001 +#define WORK_QUEUE_FLAG_MULTI 0x0002 +#define WORK_QUEUE_FLAG_HIGH_FREQ 0x0004 + +/* these flags can be set when queueing a work item to indicate how to handle + its deconstruction */ +#define WORK_ITEM_FLAG_AUTO_RELEASE 0x0001 + +/* osd_work_queue is an opaque type which represents a queue of work items */ +struct osd_work_queue; + +/* osd_work_item is an opaque type which represents a single work item */ +struct osd_work_item; + +/* osd_work_callback is a callback function that does work */ +typedef void *(*osd_work_callback)(void *param, int threadid); + + +/*----------------------------------------------------------------------------- + osd_work_queue_alloc: create a new work queue + + Parameters: + + flags - one or more of the WORK_QUEUE_FLAG_* values ORed together: + + WORK_QUEUE_FLAG_IO - indicates that the work queue will do some + I/O; this may be a useful hint so that threads are created + even on single-processor systems since I/O can often be + overlapped with other work + + WORK_QUEUE_FLAG_MULTI - indicates that the work queue should + take advantage of as many processors as it can; items queued + here are assumed to be fully independent or shared + + WORK_QUEUE_FLAG_HIGH_FREQ - indicates that items are expected + to be queued at high frequency and acted upon quickly; in + general, this implies doing some spin-waiting internally + before falling back to OS-specific synchronization + + Return value: + + A pointer to an allocated osd_work_queue object. + + Notes: + + A work queue abstracts the notion of how potentially threaded work + can be performed. If no threading support is available, it is a + simple matter to execute the work items as they are queued. +-----------------------------------------------------------------------------*/ +osd_work_queue *osd_work_queue_alloc(int flags); + + +/*----------------------------------------------------------------------------- + osd_work_queue_items: return the number of pending items in the queue + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + Return value: + + The number of incomplete items remaining in the queue. +-----------------------------------------------------------------------------*/ +int osd_work_queue_items(osd_work_queue *queue); + + +/*----------------------------------------------------------------------------- + osd_work_queue_wait: wait for the queue to be empty + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + timeout - a timeout value in osd_ticks_per_second() + + Return value: + + TRUE if the queue is empty; FALSE if the wait timed out before the + queue was emptied. +-----------------------------------------------------------------------------*/ +int osd_work_queue_wait(osd_work_queue *queue, osd_ticks_t timeout); + + +/*----------------------------------------------------------------------------- + osd_work_queue_free: free a work queue, waiting for all items to complete + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + Return value: + + None. +-----------------------------------------------------------------------------*/ +void osd_work_queue_free(osd_work_queue *queue); + + +/*----------------------------------------------------------------------------- + osd_work_item_queue_multiple: queue a set of work items + + Parameters: + + queue - pointer to an osd_work_queue that was previously created via + osd_work_queue_alloc + + callback - pointer to a function that will do the work + + numitems - number of work items to queue + + param - a void * parameter that can be used to pass data to the + function + + paramstep - the number of bytes to increment param by for each item + queued; for example, if you have an array of work_unit objects, + you can point param to the base of the array and set paramstep to + sizeof(work_unit) + + flags - one or more of the WORK_ITEM_FLAG_* values ORed together: + + WORK_ITEM_FLAG_AUTO_RELEASE - indicates that the work item + should be automatically freed when it is complete + + Return value: + + A pointer to the final allocated osd_work_item in the list. + + Notes: + + On single-threaded systems, this function may actually execute the + work item immediately before returning. +-----------------------------------------------------------------------------*/ +osd_work_item *osd_work_item_queue_multiple(osd_work_queue *queue, osd_work_callback callback, INT32 numitems, void *parambase, INT32 paramstep, UINT32 flags); + + +/* inline helper to queue a single work item using the same interface */ +INLINE osd_work_item *osd_work_item_queue(osd_work_queue *queue, osd_work_callback callback, void *param, UINT32 flags) +{ + return osd_work_item_queue_multiple(queue, callback, 1, param, 0, flags); +} + + +/*----------------------------------------------------------------------------- + osd_work_item_wait: wait for a work item to complete + + Parameters: + + item - pointer to an osd_work_item that was previously returned from + osd_work_item_queue + + timeout - a timeout value in osd_ticks_per_second() + + Return value: + + TRUE if the item completed; FALSE if the wait timed out before the + item completed. +-----------------------------------------------------------------------------*/ +int osd_work_item_wait(osd_work_item *item, osd_ticks_t timeout); + + +/*----------------------------------------------------------------------------- + osd_work_item_result: get the result of a work item + + Parameters: + + item - pointer to an osd_work_item that was previously returned from + osd_work_item_queue + + Return value: + + A void * that represents the work item's result. +-----------------------------------------------------------------------------*/ +void *osd_work_item_result(osd_work_item *item); + + +/*----------------------------------------------------------------------------- + osd_work_item_release: release the memory allocated to a work item + + Parameters: + + item - pointer to an osd_work_item that was previously returned from + osd_work_item_queue + + Return value: + + None. + + Notes: + + The osd_work_item exists until explicitly released, even if it has + long since completed. It is the queuer's responsibility to release + any work items it has queued. +-----------------------------------------------------------------------------*/ +void osd_work_item_release(osd_work_item *item); + + + +/*************************************************************************** + MISCELLANEOUS INTERFACES +***************************************************************************/ + +/*----------------------------------------------------------------------------- + osd_malloc: allocate memory + + Parameters: + + size - the number of bytes to allocate + + Return value: + + a pointer to the allocated memory + + Notes: + + This is just a hook to do OS-specific allocation trickery. + It can be safely written as a wrapper to malloc(). +-----------------------------------------------------------------------------*/ +void *osd_malloc(size_t size); + + +/*----------------------------------------------------------------------------- + osd_malloc_array: allocate memory, hinting tha this memory contains an + array + + Parameters: + + size - the number of bytes to allocate + + Return value: + + a pointer to the allocated memory + + Notes: + + This is just a hook to do OS-specific allocation trickery. + It can be safely written as a wrapper to malloc(). +-----------------------------------------------------------------------------*/ +void *osd_malloc_array(size_t size); + + +/*----------------------------------------------------------------------------- + osd_free: free memory allocated by osd_malloc + + Parameters: + + ptr - the pointer returned from osd_mallo + + Return value: + + None +-----------------------------------------------------------------------------*/ +void osd_free(void *ptr); + + +/*----------------------------------------------------------------------------- + osd_alloc_executable: allocate memory that can contain executable code + + Parameters: + + size - the number of bytes to allocate + + Return value: + + a pointer to the allocated memory + + Notes: + + On many systems, this call may acceptably map to malloc(). On systems + where pages are tagged with "no execute" privileges, it may be + necessary to perform some kind of special allocation to ensure that + code placed into this buffer can be executed. +-----------------------------------------------------------------------------*/ +void *osd_alloc_executable(size_t size); + + +/*----------------------------------------------------------------------------- + osd_free_executable: free memory allocated by osd_alloc_executable + + Parameters: + + ptr - the pointer returned from osd_alloc_executable + + size - the number of bytes originally requested + + Return value: + + None +-----------------------------------------------------------------------------*/ +void osd_free_executable(void *ptr, size_t size); + + +/*----------------------------------------------------------------------------- + osd_break_into_debugger: break into the hosting system's debugger if one + is attached + + Parameters: + + message - pointer to string to output to the debugger + + Return value: + + None. + + Notes: + + This function is called when an assertion or other important error + occurs. If a debugger is attached to the current process, it should + break into the debugger and display the given message. +-----------------------------------------------------------------------------*/ +void osd_break_into_debugger(const char *message); + + +/*----------------------------------------------------------------------------- + MESS specific code below +-----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------- + osd_get_clipboard_text: retrieves text from the clipboard + + Return value: + + the returned string needs to be osd_free()-ed! + +-----------------------------------------------------------------------------*/ +char *osd_get_clipboard_text(void); + + +/*************************************************************************** + DIRECTORY INTERFACES +***************************************************************************/ + +/*----------------------------------------------------------------------------- + osd_stat: return a directory entry for a path + + Parameters: + + path - path in question + + Return value: + + an allocated pointer to an osd_directory_entry representing + info on the path; even if the file does not exist. + free with osd_free() + +-----------------------------------------------------------------------------*/ +osd_directory_entry *osd_stat(const char *path); + +/*************************************************************************** + PATH INTERFACES +***************************************************************************/ + +/*----------------------------------------------------------------------------- + osd_get_full_path: retrieves the full path + + Parameters: + + path - the path in question + dst - pointer to receive new path; the returned string needs to be osd_free()-ed! + + Return value: + + file error + +-----------------------------------------------------------------------------*/ +file_error osd_get_full_path(char **dst, const char *path); + + +/*************************************************************************** + MIDI I/O INTERFACES +***************************************************************************/ +struct osd_midi_device; + +void osd_list_midi_devices(void); +// free result with osd_close_midi_channel() +osd_midi_device *osd_open_midi_input(const char *devname); +// free result with osd_close_midi_channel() +osd_midi_device *osd_open_midi_output(const char *devname); +void osd_close_midi_channel(osd_midi_device *dev); +bool osd_poll_midi_channel(osd_midi_device *dev); +int osd_read_midi_channel(osd_midi_device *dev, UINT8 *pOut); +void osd_write_midi_channel(osd_midi_device *dev, UINT8 data); + +/*************************************************************************** + UNCATEGORIZED INTERFACES +***************************************************************************/ + +/*----------------------------------------------------------------------------- + osd_get_volume_name: retrieves the volume name + + Parameters: + + idx - order number of volume + + Return value: + + pointer to volume name + +-----------------------------------------------------------------------------*/ +const char *osd_get_volume_name(int idx); + +/* ----- output management ----- */ + +// output channels +enum output_channel +{ + OSD_OUTPUT_CHANNEL_ERROR, + OSD_OUTPUT_CHANNEL_WARNING, + OSD_OUTPUT_CHANNEL_INFO, + OSD_OUTPUT_CHANNEL_DEBUG, + OSD_OUTPUT_CHANNEL_VERBOSE, + OSD_OUTPUT_CHANNEL_LOG, + OSD_OUTPUT_CHANNEL_COUNT +}; + +// output channel callback +typedef delegate output_delegate; + +/* set the output handler for a channel, returns the current one */ +output_delegate osd_set_output_channel(output_channel channel, output_delegate callback); + +/* calls to be used by the code */ +void CLIB_DECL osd_printf_error(const char *format, ...) ATTR_PRINTF(1,2); +void CLIB_DECL osd_printf_warning(const char *format, ...) ATTR_PRINTF(1,2); +void CLIB_DECL osd_printf_info(const char *format, ...) ATTR_PRINTF(1,2); +void CLIB_DECL osd_printf_verbose(const char *format, ...) ATTR_PRINTF(1,2); +void CLIB_DECL osd_printf_debug(const char *format, ...) ATTR_PRINTF(1,2); + +/* discourage the use of printf directly */ +/* sadly, can't do this because of the ATTR_PRINTF under GCC */ +/* +#undef printf +#define printf !MUST_USE_osd_printf_*_CALLS_WITHIN_THE_CORE! +*/ + +#endif /* __OSDEPEND_H__ */ diff --git a/archivers/chd/osinline.h b/archivers/chd/osinline.h new file mode 100644 index 00000000..3e28c982 --- /dev/null +++ b/archivers/chd/osinline.h @@ -0,0 +1,51 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// osinline.h +// +// Inline implementations for non-GCC Win32 compilers +// +//============================================================ + +#ifndef __OSINLINE__ +#define __OSINLINE__ + +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) +#include "eivcx86.h" +#endif + +#if defined(_MSC_VER) +#include "eivc.h" +#endif + +INT32 win_compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange); +INT32 win_atomic_exchange32(INT32 volatile *ptr, INT32 exchange); +INT32 win_atomic_add32(INT32 volatile *ptr, INT32 delta); + +#ifdef PTR64 +INT64 win_compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange); +#endif + + +#ifndef compare_exchange32 +#define compare_exchange32 win_compare_exchange32 +#endif /* compare_exchange32 */ + +#ifdef PTR64 +#ifndef compare_exchange64 +#define compare_exchange64 win_compare_exchange64 +#endif /* compare_exchange64 */ +#endif + +#ifndef atomic_exchange32 +#define atomic_exchange32 win_atomic_exchange32 +#endif /* atomic_exchange32 */ + + +#ifndef atomic_add32 +#define atomic_add32 win_atomic_add32 +#endif /* atomic_add32 */ + + +#endif /* __OSINLINE__ */ diff --git a/archivers/chd/palette.h b/archivers/chd/palette.h new file mode 100644 index 00000000..939138e6 --- /dev/null +++ b/archivers/chd/palette.h @@ -0,0 +1,274 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/****************************************************************************** + + palette.h + + Core palette routines. + +***************************************************************************/ + +#pragma once + +#ifndef __PALETTE_H__ +#define __PALETTE_H__ + +#include "osdcore.h" +#include "coretmpl.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// forward definitions +class palette_t; + +// an rgb15_t is a single combined 15-bit R,G,B value +typedef UINT16 rgb15_t; + + +// ======================> rgb_t + +// an rgb_t is a single combined R,G,B (and optionally alpha) value +class rgb_t +{ +public: + // construction/destruction + rgb_t() { } + rgb_t(UINT32 data) { m_data = data; } + rgb_t(UINT8 r, UINT8 g, UINT8 b) { m_data = (255 << 24) | (r << 16) | (g << 8) | b; } + rgb_t(UINT8 a, UINT8 r, UINT8 g, UINT8 b) { m_data = (a << 24) | (r << 16) | (g << 8) | b; } + + // getters + UINT8 a() const { return m_data >> 24; } + UINT8 r() const { return m_data >> 16; } + UINT8 g() const { return m_data >> 8; } + UINT8 b() const { return m_data >> 0; } + rgb15_t as_rgb15() const { return ((r() >> 3) << 10) | ((g() >> 3) << 5) | ((b() >> 3) << 0); } + UINT8 brightness() const { return (r() * 222 + g() * 707 + b() * 71) / 1000; } + UINT32 const *ptr() const { return &m_data; } + + // setters + rgb_t &set_a(UINT8 a) { m_data &= ~0xff000000; m_data |= a << 24; return *this; } + rgb_t &set_r(UINT8 r) { m_data &= ~0x00ff0000; m_data |= r << 16; return *this; } + rgb_t &set_g(UINT8 g) { m_data &= ~0x0000ff00; m_data |= g << 8; return *this; } + rgb_t &set_b(UINT8 b) { m_data &= ~0x000000ff; m_data |= b << 0; return *this; } + + // implicit conversion operators + operator UINT32() const { return m_data; } + + // operations + rgb_t &scale8(UINT8 scale) { m_data = rgb_t(clamphi((a() * scale) >> 8), clamphi((r() * scale) >> 8), clamphi((g() * scale) >> 8), clamphi((b() * scale) >> 8)); return *this; } + + // assignment operators + rgb_t &operator=(UINT32 rhs) { m_data = rhs; return *this; } + rgb_t &operator+=(const rgb_t &rhs) { m_data = rgb_t(clamphi(a() + rhs.a()), clamphi(r() + rhs.r()), clamphi(g() + rhs.g()), clamphi(b() + rhs.b())); return *this; } + rgb_t &operator-=(const rgb_t &rhs) { m_data = rgb_t(clamplo(a() - rhs.a()), clamplo(r() - rhs.r()), clamplo(g() - rhs.g()), clamplo(b() - rhs.b())); return *this; } + + // arithmetic operators + const rgb_t operator+(const rgb_t &rhs) const { rgb_t result = *this; result += rhs; return result; } + const rgb_t operator-(const rgb_t &rhs) const { rgb_t result = *this; result -= rhs; return result; } + + // static helpers + static UINT8 clamp(INT32 value) { return (value < 0) ? 0 : (value > 255) ? 255 : value; } + static UINT8 clamphi(INT32 value) { return (value > 255) ? 255 : value; } + static UINT8 clamplo(INT32 value) { return (value < 0) ? 0 : value; } + + // constants + static const rgb_t black; + static const rgb_t white; + +private: + UINT32 m_data; +}; + + +// ======================> palette_client + +// a single palette client +class palette_client +{ +public: + // construction/destruction + palette_client(palette_t &palette); + ~palette_client(); + + // getters + palette_client *next() const { return m_next; } + palette_t &palette() const { return m_palette; } + const UINT32 *dirty_list(UINT32 &mindirty, UINT32 &maxdirty); + + // dirty marking + void mark_dirty(UINT32 index) { m_live->mark_dirty(index); } + +private: + // internal object to track dirty states + class dirty_state + { + public: + // construction + dirty_state(); + + // operations + const UINT32 *dirty_list(UINT32 &mindirty, UINT32 &maxdirty); + void resize(UINT32 colors); + void mark_dirty(UINT32 index); + void reset(); + + private: + // internal state + dynamic_array 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 m_entry_color; // array of raw colors + dynamic_array m_entry_contrast; // contrast value for each entry + dynamic_array m_adjusted_color; // array of adjusted colors + dynamic_array m_adjusted_rgb15; // array of adjusted colors as RGB15 + + dynamic_array m_group_bright; // brightness value for each group + dynamic_array 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 +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 +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__ diff --git a/archivers/chd/sha1.cpp b/archivers/chd/sha1.cpp index 5e2de844..266f3ed5 100644 --- a/archivers/chd/sha1.cpp +++ b/archivers/chd/sha1.cpp @@ -1,3 +1,4 @@ +#include "chdtypes.h" /* sha1.h * * The sha1 hash function. @@ -23,8 +24,6 @@ * MA 02111-1307, USA. */ -#include "chdtypes.h" - #include "sha1.h" #include @@ -99,8 +98,8 @@ static void WRITE_UINT32(unsigned char* data, UINT32 val) for this information */ #define expand(W,i) ( W[ i & 15 ] = \ - ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ - W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) + ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ + W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) /* The prototype SHA sub-round. The fundamental sub-round is: @@ -117,25 +116,25 @@ static void WRITE_UINT32(unsigned char* data, UINT32 val) the next 20 values from the W[] array each time */ #define subRound(a, b, c, d, e, f, k, data) \ - ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) + ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) /* Initialize the SHA values */ void sha1_init(struct sha1_ctx *ctx) { - /* Set the h-vars to their initial values */ - ctx->digest[ 0 ] = h0init; - ctx->digest[ 1 ] = h1init; - ctx->digest[ 2 ] = h2init; - ctx->digest[ 3 ] = h3init; - ctx->digest[ 4 ] = h4init; - - /* Initialize bit count */ - ctx->count_low = ctx->count_high = 0; - - /* Initialize buffer */ - ctx->index = 0; + /* Set the h-vars to their initial values */ + ctx->digest[ 0 ] = h0init; + ctx->digest[ 1 ] = h1init; + ctx->digest[ 2 ] = h2init; + ctx->digest[ 3 ] = h3init; + ctx->digest[ 4 ] = h4init; + + /* Initialize bit count */ + ctx->count_low = ctx->count_high = 0; + + /* Initialize buffer */ + ctx->index = 0; } /* Perform the SHA transformation. Note that this code, like MD5, seems to @@ -148,156 +147,156 @@ sha1_init(struct sha1_ctx *ctx) static void sha1_transform(UINT32 *state, UINT32 *data) { - UINT32 A, B, C, D, E; /* Local vars */ - - /* Set up first buffer and local data buffer */ - A = state[0]; - B = state[1]; - C = state[2]; - D = state[3]; - E = state[4]; - - /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ - subRound( A, B, C, D, E, f1, K1, data[ 0] ); - subRound( E, A, B, C, D, f1, K1, data[ 1] ); - subRound( D, E, A, B, C, f1, K1, data[ 2] ); - subRound( C, D, E, A, B, f1, K1, data[ 3] ); - subRound( B, C, D, E, A, f1, K1, data[ 4] ); - subRound( A, B, C, D, E, f1, K1, data[ 5] ); - subRound( E, A, B, C, D, f1, K1, data[ 6] ); - subRound( D, E, A, B, C, f1, K1, data[ 7] ); - subRound( C, D, E, A, B, f1, K1, data[ 8] ); - subRound( B, C, D, E, A, f1, K1, data[ 9] ); - subRound( A, B, C, D, E, f1, K1, data[10] ); - subRound( E, A, B, C, D, f1, K1, data[11] ); - subRound( D, E, A, B, C, f1, K1, data[12] ); - subRound( C, D, E, A, B, f1, K1, data[13] ); - subRound( B, C, D, E, A, f1, K1, data[14] ); - subRound( A, B, C, D, E, f1, K1, data[15] ); - subRound( E, A, B, C, D, f1, K1, expand( data, 16 ) ); - subRound( D, E, A, B, C, f1, K1, expand( data, 17 ) ); - subRound( C, D, E, A, B, f1, K1, expand( data, 18 ) ); - subRound( B, C, D, E, A, f1, K1, expand( data, 19 ) ); - - subRound( A, B, C, D, E, f2, K2, expand( data, 20 ) ); - subRound( E, A, B, C, D, f2, K2, expand( data, 21 ) ); - subRound( D, E, A, B, C, f2, K2, expand( data, 22 ) ); - subRound( C, D, E, A, B, f2, K2, expand( data, 23 ) ); - subRound( B, C, D, E, A, f2, K2, expand( data, 24 ) ); - subRound( A, B, C, D, E, f2, K2, expand( data, 25 ) ); - subRound( E, A, B, C, D, f2, K2, expand( data, 26 ) ); - subRound( D, E, A, B, C, f2, K2, expand( data, 27 ) ); - subRound( C, D, E, A, B, f2, K2, expand( data, 28 ) ); - subRound( B, C, D, E, A, f2, K2, expand( data, 29 ) ); - subRound( A, B, C, D, E, f2, K2, expand( data, 30 ) ); - subRound( E, A, B, C, D, f2, K2, expand( data, 31 ) ); - subRound( D, E, A, B, C, f2, K2, expand( data, 32 ) ); - subRound( C, D, E, A, B, f2, K2, expand( data, 33 ) ); - subRound( B, C, D, E, A, f2, K2, expand( data, 34 ) ); - subRound( A, B, C, D, E, f2, K2, expand( data, 35 ) ); - subRound( E, A, B, C, D, f2, K2, expand( data, 36 ) ); - subRound( D, E, A, B, C, f2, K2, expand( data, 37 ) ); - subRound( C, D, E, A, B, f2, K2, expand( data, 38 ) ); - subRound( B, C, D, E, A, f2, K2, expand( data, 39 ) ); - - subRound( A, B, C, D, E, f3, K3, expand( data, 40 ) ); - subRound( E, A, B, C, D, f3, K3, expand( data, 41 ) ); - subRound( D, E, A, B, C, f3, K3, expand( data, 42 ) ); - subRound( C, D, E, A, B, f3, K3, expand( data, 43 ) ); - subRound( B, C, D, E, A, f3, K3, expand( data, 44 ) ); - subRound( A, B, C, D, E, f3, K3, expand( data, 45 ) ); - subRound( E, A, B, C, D, f3, K3, expand( data, 46 ) ); - subRound( D, E, A, B, C, f3, K3, expand( data, 47 ) ); - subRound( C, D, E, A, B, f3, K3, expand( data, 48 ) ); - subRound( B, C, D, E, A, f3, K3, expand( data, 49 ) ); - subRound( A, B, C, D, E, f3, K3, expand( data, 50 ) ); - subRound( E, A, B, C, D, f3, K3, expand( data, 51 ) ); - subRound( D, E, A, B, C, f3, K3, expand( data, 52 ) ); - subRound( C, D, E, A, B, f3, K3, expand( data, 53 ) ); - subRound( B, C, D, E, A, f3, K3, expand( data, 54 ) ); - subRound( A, B, C, D, E, f3, K3, expand( data, 55 ) ); - subRound( E, A, B, C, D, f3, K3, expand( data, 56 ) ); - subRound( D, E, A, B, C, f3, K3, expand( data, 57 ) ); - subRound( C, D, E, A, B, f3, K3, expand( data, 58 ) ); - subRound( B, C, D, E, A, f3, K3, expand( data, 59 ) ); - - subRound( A, B, C, D, E, f4, K4, expand( data, 60 ) ); - subRound( E, A, B, C, D, f4, K4, expand( data, 61 ) ); - subRound( D, E, A, B, C, f4, K4, expand( data, 62 ) ); - subRound( C, D, E, A, B, f4, K4, expand( data, 63 ) ); - subRound( B, C, D, E, A, f4, K4, expand( data, 64 ) ); - subRound( A, B, C, D, E, f4, K4, expand( data, 65 ) ); - subRound( E, A, B, C, D, f4, K4, expand( data, 66 ) ); - subRound( D, E, A, B, C, f4, K4, expand( data, 67 ) ); - subRound( C, D, E, A, B, f4, K4, expand( data, 68 ) ); - subRound( B, C, D, E, A, f4, K4, expand( data, 69 ) ); - subRound( A, B, C, D, E, f4, K4, expand( data, 70 ) ); - subRound( E, A, B, C, D, f4, K4, expand( data, 71 ) ); - subRound( D, E, A, B, C, f4, K4, expand( data, 72 ) ); - subRound( C, D, E, A, B, f4, K4, expand( data, 73 ) ); - subRound( B, C, D, E, A, f4, K4, expand( data, 74 ) ); - subRound( A, B, C, D, E, f4, K4, expand( data, 75 ) ); - subRound( E, A, B, C, D, f4, K4, expand( data, 76 ) ); - subRound( D, E, A, B, C, f4, K4, expand( data, 77 ) ); - subRound( C, D, E, A, B, f4, K4, expand( data, 78 ) ); - subRound( B, C, D, E, A, f4, K4, expand( data, 79 ) ); - - /* Build message digest */ - state[0] += A; - state[1] += B; - state[2] += C; - state[3] += D; - state[4] += E; + UINT32 A, B, C, D, E; /* Local vars */ + + /* Set up first buffer and local data buffer */ + A = state[0]; + B = state[1]; + C = state[2]; + D = state[3]; + E = state[4]; + + /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ + subRound( A, B, C, D, E, f1, K1, data[ 0] ); + subRound( E, A, B, C, D, f1, K1, data[ 1] ); + subRound( D, E, A, B, C, f1, K1, data[ 2] ); + subRound( C, D, E, A, B, f1, K1, data[ 3] ); + subRound( B, C, D, E, A, f1, K1, data[ 4] ); + subRound( A, B, C, D, E, f1, K1, data[ 5] ); + subRound( E, A, B, C, D, f1, K1, data[ 6] ); + subRound( D, E, A, B, C, f1, K1, data[ 7] ); + subRound( C, D, E, A, B, f1, K1, data[ 8] ); + subRound( B, C, D, E, A, f1, K1, data[ 9] ); + subRound( A, B, C, D, E, f1, K1, data[10] ); + subRound( E, A, B, C, D, f1, K1, data[11] ); + subRound( D, E, A, B, C, f1, K1, data[12] ); + subRound( C, D, E, A, B, f1, K1, data[13] ); + subRound( B, C, D, E, A, f1, K1, data[14] ); + subRound( A, B, C, D, E, f1, K1, data[15] ); + subRound( E, A, B, C, D, f1, K1, expand( data, 16 ) ); + subRound( D, E, A, B, C, f1, K1, expand( data, 17 ) ); + subRound( C, D, E, A, B, f1, K1, expand( data, 18 ) ); + subRound( B, C, D, E, A, f1, K1, expand( data, 19 ) ); + + subRound( A, B, C, D, E, f2, K2, expand( data, 20 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 21 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 22 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 23 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 24 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 25 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 26 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 27 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 28 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 29 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 30 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 31 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 32 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 33 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 34 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 35 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 36 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 37 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 38 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 39 ) ); + + subRound( A, B, C, D, E, f3, K3, expand( data, 40 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 41 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 42 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 43 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 44 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 45 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 46 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 47 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 48 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 49 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 50 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 51 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 52 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 53 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 54 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 55 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 56 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 57 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 58 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 59 ) ); + + subRound( A, B, C, D, E, f4, K4, expand( data, 60 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 61 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 62 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 63 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 64 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 65 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 66 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 67 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 68 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 69 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 70 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 71 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 72 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 73 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 74 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 75 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 76 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 77 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 78 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 79 ) ); + + /* Build message digest */ + state[0] += A; + state[1] += B; + state[2] += C; + state[3] += D; + state[4] += E; } static void sha1_block(struct sha1_ctx *ctx, const UINT8 *block) { - UINT32 data[SHA1_DATA_LENGTH]; - int i; + UINT32 data[SHA1_DATA_LENGTH]; + int i; - /* Update block count */ - if (!++ctx->count_low) - ++ctx->count_high; + /* Update block count */ + if (!++ctx->count_low) + ++ctx->count_high; - /* Endian independent conversion */ - for (i = 0; idigest, data); + sha1_transform(ctx->digest, data); } void sha1_update(struct sha1_ctx *ctx, - unsigned length, const UINT8 *buffer) + unsigned length, const UINT8 *buffer) { - if (ctx->index) - { /* Try to fill partial block */ - unsigned left = SHA1_DATA_SIZE - ctx->index; - if (length < left) + if (ctx->index) + { /* Try to fill partial block */ + unsigned left = SHA1_DATA_SIZE - ctx->index; + if (length < left) { - memcpy(ctx->block + ctx->index, buffer, length); - ctx->index += length; - return; /* Finished */ + memcpy(ctx->block + ctx->index, buffer, length); + ctx->index += length; + return; /* Finished */ } - else + else { - memcpy(ctx->block + ctx->index, buffer, left); - sha1_block(ctx, ctx->block); - buffer += left; - length -= left; + memcpy(ctx->block + ctx->index, buffer, left); + sha1_block(ctx, ctx->block); + buffer += left; + length -= left; } - } - while (length >= SHA1_DATA_SIZE) - { - sha1_block(ctx, buffer); - buffer += SHA1_DATA_SIZE; - length -= SHA1_DATA_SIZE; - } - ctx->index = length; - if (length) - /* Buffer leftovers */ - memcpy(ctx->block, buffer, length); + } + while (length >= SHA1_DATA_SIZE) + { + sha1_block(ctx, buffer); + buffer += SHA1_DATA_SIZE; + length -= SHA1_DATA_SIZE; + } + ctx->index = length; + if (length) + /* Buffer leftovers */ + memcpy(ctx->block, buffer, length); } /* Final wrapup - pad to SHA1_DATA_SIZE-byte boundary with the bit pattern @@ -306,84 +305,84 @@ sha1_update(struct sha1_ctx *ctx, void sha1_final(struct sha1_ctx *ctx) { - UINT32 data[SHA1_DATA_LENGTH]; - int i; - int words; + UINT32 data[SHA1_DATA_LENGTH]; + int i; + int words; - i = ctx->index; + i = ctx->index; - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ - assert(i < SHA1_DATA_SIZE); - ctx->block[i++] = 0x80; + assert(i < SHA1_DATA_SIZE); + ctx->block[i++] = 0x80; - /* Fill rest of word */ - for( ; i & 3; i++) - ctx->block[i] = 0; + /* Fill rest of word */ + for( ; i & 3; i++) + ctx->block[i] = 0; - /* i is now a multiple of the word size 4 */ - words = i >> 2; - for (i = 0; i < words; i++) - data[i] = READ_UINT32(ctx->block + 4*i); + /* i is now a multiple of the word size 4 */ + words = i >> 2; + for (i = 0; i < words; i++) + data[i] = READ_UINT32(ctx->block + 4*i); - if (words > (SHA1_DATA_LENGTH-2)) - { /* No room for length in this block. Process it and + if (words > (SHA1_DATA_LENGTH-2)) + { /* No room for length in this block. Process it and * pad with another one */ - for (i = words ; i < SHA1_DATA_LENGTH; i++) + for (i = words ; i < SHA1_DATA_LENGTH; i++) data[i] = 0; - sha1_transform(ctx->digest, data); - for (i = 0; i < (SHA1_DATA_LENGTH-2); i++) + sha1_transform(ctx->digest, data); + for (i = 0; i < (SHA1_DATA_LENGTH-2); i++) data[i] = 0; - } - else - for (i = words ; i < SHA1_DATA_LENGTH - 2; i++) - data[i] = 0; - - /* There are 512 = 2^9 bits in one block */ - data[SHA1_DATA_LENGTH-2] = (ctx->count_high << 9) | (ctx->count_low >> 23); - data[SHA1_DATA_LENGTH-1] = (ctx->count_low << 9) | (ctx->index << 3); - sha1_transform(ctx->digest, data); + } + else + for (i = words ; i < SHA1_DATA_LENGTH - 2; i++) + data[i] = 0; + + /* There are 512 = 2^9 bits in one block */ + data[SHA1_DATA_LENGTH-2] = (ctx->count_high << 9) | (ctx->count_low >> 23); + data[SHA1_DATA_LENGTH-1] = (ctx->count_low << 9) | (ctx->index << 3); + sha1_transform(ctx->digest, data); } void sha1_digest(const struct sha1_ctx *ctx, - unsigned length, - UINT8 *digest) + unsigned length, + UINT8 *digest) { - unsigned i; - unsigned words; - unsigned leftover; + unsigned i; + unsigned words; + unsigned leftover; - assert(length <= SHA1_DIGEST_SIZE); + assert(length <= SHA1_DIGEST_SIZE); - words = length / 4; - leftover = length % 4; + words = length / 4; + leftover = length % 4; - for (i = 0; i < words; i++, digest += 4) - WRITE_UINT32(digest, ctx->digest[i]); + for (i = 0; i < words; i++, digest += 4) + WRITE_UINT32(digest, ctx->digest[i]); - if (leftover) - { - UINT32 word; - unsigned j = leftover; + if (leftover) + { + UINT32 word; + unsigned j = leftover; - assert(i < _SHA1_DIGEST_LENGTH); + assert(i < _SHA1_DIGEST_LENGTH); - word = ctx->digest[i]; + word = ctx->digest[i]; - switch (leftover) + switch (leftover) { default: /* this is just here to keep the compiler happy; it can never happen */ case 3: - digest[--j] = (word >> 8) & 0xff; - /* Fall through */ + digest[--j] = (word >> 8) & 0xff; + /* Fall through */ case 2: - digest[--j] = (word >> 16) & 0xff; - /* Fall through */ + digest[--j] = (word >> 16) & 0xff; + /* Fall through */ case 1: - digest[--j] = (word >> 24) & 0xff; + digest[--j] = (word >> 24) & 0xff; + } } - } } diff --git a/archivers/chd/sha1.h b/archivers/chd/sha1.h index 028f3303..dcec92e4 100644 --- a/archivers/chd/sha1.h +++ b/archivers/chd/sha1.h @@ -36,10 +36,10 @@ struct sha1_ctx { - UINT32 digest[_SHA1_DIGEST_LENGTH]; /* Message digest */ - UINT32 count_low, count_high; /* 64-bit block count */ - UINT8 block[SHA1_DATA_SIZE]; /* SHA1 data buffer */ - unsigned int index; /* index into buffer */ + UINT32 digest[_SHA1_DIGEST_LENGTH]; /* Message digest */ + UINT32 count_low, count_high; /* 64-bit block count */ + UINT8 block[SHA1_DATA_SIZE]; /* SHA1 data buffer */ + unsigned int index; /* index into buffer */ }; void @@ -47,15 +47,15 @@ sha1_init(struct sha1_ctx *ctx); void sha1_update(struct sha1_ctx *ctx, - unsigned length, - const UINT8 *data); + unsigned length, + const UINT8 *data); void sha1_final(struct sha1_ctx *ctx); void sha1_digest(const struct sha1_ctx *ctx, - unsigned length, - UINT8 *digest); + unsigned length, + UINT8 *digest); #endif /* NETTLE_SHA1_H_INCLUDED */ diff --git a/archivers/chd/windows/eivc.h b/archivers/chd/windows/eivc.h new file mode 100644 index 00000000..93927b17 --- /dev/null +++ b/archivers/chd/windows/eivc.h @@ -0,0 +1,174 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// eivc.h +// +// Inline implementations for MSVC compiler. +// +//============================================================ + +#ifndef __EIVC__ +#define __EIVC__ + +#if (_MSC_VER >= 1400) + +// need to ignore 'nonstandard extension used' warning in setjmp.h +#pragma warning(push) +#pragma warning(disable: 4987) +#include +#pragma warning(pop) + +#else +extern "C" long __cdecl _InterlockedIncrement(long volatile *); +extern "C" long __cdecl _InterlockedDecrement(long volatile *); +extern "C" long _InterlockedExchange(long volatile *, long); +extern "C" long _InterlockedCompareExchange (long volatile *, long, long); +extern "C" long _InterlockedExchangeAdd(long volatile *, long); +extern "C" unsigned char _BitScanReverse(unsigned long *Index, unsigned long Mask); +#endif + +#pragma intrinsic(_InterlockedIncrement) +#pragma intrinsic(_InterlockedDecrement) +#pragma intrinsic(_InterlockedCompareExchange) +#pragma intrinsic(_InterlockedExchange) +#pragma intrinsic(_InterlockedExchangeAdd) +#if (_MSC_VER >= 1310) +#pragma intrinsic(_BitScanReverse) +#endif + + +/*************************************************************************** + INLINE BIT MANIPULATION FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + count_leading_zeros - return the number of + leading zero bits in a 32-bit value +-------------------------------------------------*/ + +#ifndef count_leading_zeros +#define count_leading_zeros _count_leading_zeros +INLINE UINT8 _count_leading_zeros(UINT32 value) +{ + UINT32 index; + return _BitScanReverse((unsigned long *)&index, value) ? (index ^ 31) : 32; +} +#endif + + +/*------------------------------------------------- + count_leading_ones - return the number of + leading one bits in a 32-bit value +-------------------------------------------------*/ + +#ifndef count_leading_ones +#define count_leading_ones _count_leading_ones +INLINE UINT8 _count_leading_ones(UINT32 value) +{ + UINT32 index; + return _BitScanReverse((unsigned long *)&index, ~value) ? (index ^ 31) : 32; +} +#endif + + + +/*************************************************************************** + INLINE SYNCHRONIZATION FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + compare_exchange32 - compare the 'compare' + value against the memory at 'ptr'; if equal, + swap in the 'exchange' value. Regardless, + return the previous value at 'ptr'. +-------------------------------------------------*/ + +#ifndef compare_exchange32 +#define compare_exchange32 _compare_exchange32 +INLINE INT32 _compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange) +{ + return _InterlockedCompareExchange((volatile long *)ptr, exchange, compare); +} +#endif + + +/*------------------------------------------------- + compare_exchange64 - compare the 'compare' + value against the memory at 'ptr'; if equal, + swap in the 'exchange' value. Regardless, + return the previous value at 'ptr'. +-------------------------------------------------*/ + +#ifdef PTR64 +#ifndef compare_exchange64 +#define compare_exchange64 _compare_exchange64 +INLINE INT64 _compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange) +{ + return _InterlockedCompareExchange64(ptr, exchange, compare); +} +#endif +#endif + + +/*------------------------------------------------- + atomic_exchange32 - atomically exchange the + exchange value with the memory at 'ptr', + returning the original value. +-------------------------------------------------*/ + +#ifndef atomic_exchange32 +#define atomic_exchange32 _atomic_exchange32 +INLINE INT32 _atomic_exchange32(INT32 volatile *ptr, INT32 exchange) +{ + return _InterlockedExchange((volatile long *)ptr, exchange); +} +#endif + + +/*------------------------------------------------- + atomic_add32 - atomically add the delta value + to the memory at 'ptr', returning the final + result. +-------------------------------------------------*/ + +#ifndef atomic_add32 +#define atomic_add32 _atomic_add32 +INLINE INT32 _atomic_add32(INT32 volatile *ptr, INT32 delta) +{ + return _InterlockedExchangeAdd((volatile long *)ptr, delta) + delta; +} +#endif + + +/*------------------------------------------------- + atomic_increment32 - atomically increment the + 32-bit value in memory at 'ptr', returning the + final result. +-------------------------------------------------*/ + +#ifndef atomic_increment32 +#define atomic_increment32 _atomic_increment32 +INLINE INT32 _atomic_increment32(INT32 volatile *ptr) +{ + return _InterlockedIncrement((volatile long *)ptr); +} +#endif + + +/*------------------------------------------------- + atomic_decrement32 - atomically decrement the + 32-bit value in memory at 'ptr', returning the + final result. +-------------------------------------------------*/ + +#ifndef atomic_decrement32 +#define atomic_decrement32 _atomic_decrement32 +INLINE INT32 _atomic_decrement32(INT32 volatile *ptr) +{ + return _InterlockedDecrement((volatile long *)ptr); +} +#endif + + +#endif /* __EIVC__ */ diff --git a/archivers/chd/windows/eivcx86.h b/archivers/chd/windows/eivcx86.h new file mode 100644 index 00000000..b834bc2a --- /dev/null +++ b/archivers/chd/windows/eivcx86.h @@ -0,0 +1,504 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// eivcx86.h +// +// x86 inline implementations for MSVC compiler. +// +//============================================================ + +#ifndef __EIVCX86__ +#define __EIVCX86__ + +#ifdef PTR64 +#include +#include +#endif + + +/*************************************************************************** + INLINE MATH FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + mul_32x32 - perform a signed 32 bit x 32 bit + multiply and return the full 64 bit result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mul_32x32 _mul_32x32 +INLINE INT64 _mul_32x32(INT32 a, INT32 b) +{ + // in theory this should work, but it is untested + __asm + { + mov eax,a + imul b + // leave results in edx:eax + } +} +#endif + + +/*------------------------------------------------- + mulu_32x32 - perform an unsigned 32 bit x + 32 bit multiply and return the full 64 bit + result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mulu_32x32 _mulu_32x32 +INLINE UINT64 _mulu_32x32(UINT32 a, UINT32 b) +{ + // in theory this should work, but it is untested + __asm + { + mov eax,a + mul b + // leave results in edx:eax + } +} +#endif + + +/*------------------------------------------------- + mul_32x32_hi - perform a signed 32 bit x 32 bit + multiply and return the upper 32 bits of the + result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mul_32x32_hi _mul_32x32_hi +INLINE INT32 _mul_32x32_hi(INT32 a, INT32 b) +{ + INT32 result; + + __asm + { + mov eax,a + imul b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + mulu_32x32_hi - perform an unsigned 32 bit x + 32 bit multiply and return the upper 32 bits + of the result +-------------------------------------------------*/ + +#ifndef PTR64 +#define mulu_32x32_hi _mulu_32x32_hi +INLINE UINT32 _mulu_32x32_hi(UINT32 a, UINT32 b) +{ + INT32 result; + + __asm + { + mov eax,a + mul b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + mul_32x32_shift - perform a signed 32 bit x + 32 bit multiply and shift the result by the + given number of bits before truncating the + result to 32 bits +-------------------------------------------------*/ + +#ifndef PTR64 +#define mul_32x32_shift _mul_32x32_shift +INLINE INT32 _mul_32x32_shift(INT32 a, INT32 b, UINT8 shift) +{ + INT32 result; + + __asm + { + mov eax,a + imul b + mov cl,shift + shrd eax,edx,cl + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + mulu_32x32_shift - perform an unsigned 32 bit x + 32 bit multiply and shift the result by the + given number of bits before truncating the + result to 32 bits +-------------------------------------------------*/ + +#ifndef PTR64 +#define mulu_32x32_shift _mulu_32x32_shift +INLINE UINT32 _mulu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift) +{ + INT32 result; + + __asm + { + mov eax,a + mul b + mov cl,shift + shrd eax,edx,cl + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + div_64x32 - perform a signed 64 bit x 32 bit + divide and return the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define div_64x32 _div_64x32 +INLINE INT32 _div_64x32(INT64 a, INT32 b) +{ + INT32 result; + INT32 alow = a; + INT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + idiv b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + divu_64x32 - perform an unsigned 64 bit x 32 bit + divide and return the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define divu_64x32 _divu_64x32 +INLINE UINT32 _divu_64x32(UINT64 a, UINT32 b) +{ + UINT32 result; + UINT32 alow = a; + UINT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + div b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + div_64x32_rem - perform a signed 64 bit x 32 + bit divide and return the 32 bit quotient and + 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define div_64x32_rem _div_64x32_rem +INLINE INT32 _div_64x32_rem(INT64 a, INT32 b, INT32 *remainder) +{ + INT32 result; + INT32 alow = a; + INT32 ahigh = a >> 32; + INT32 rem; + + __asm + { + mov eax,alow + mov edx,ahigh + idiv b + mov result,eax + mov rem,edx + } + + *remainder = rem; + return result; +} +#endif + + +/*------------------------------------------------- + divu_64x32_rem - perform an unsigned 64 bit x + 32 bit divide and return the 32 bit quotient + and 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define divu_64x32_rem _divu_64x32_rem +INLINE UINT32 _divu_64x32_rem(UINT64 a, UINT32 b, UINT32 *remainder) +{ + UINT32 result; + UINT32 alow = a; + UINT32 ahigh = a >> 32; + UINT32 rem; + + __asm + { + mov eax,alow + mov edx,ahigh + div b + mov result,eax + mov rem,edx + } + + *remainder = rem; + return result; +} +#endif + + +/*------------------------------------------------- + div_32x32_shift - perform a signed divide of + two 32 bit values, shifting the first before + division, and returning the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define div_32x32_shift _div_32x32_shift +INLINE INT32 _div_32x32_shift(INT32 a, INT32 b, UINT8 shift) +{ + INT32 result; + + __asm + { + mov eax,a + cdq + mov cl,shift + shld edx,eax,cl + shl eax,cl + idiv b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + divu_32x32_shift - perform an unsigned divide of + two 32 bit values, shifting the first before + division, and returning the 32 bit quotient +-------------------------------------------------*/ + +#ifndef PTR64 +#define divu_32x32_shift _divu_32x32_shift +INLINE UINT32 _divu_32x32_shift(UINT32 a, UINT32 b, UINT8 shift) +{ + UINT32 result; + + __asm + { + mov eax,a + xor edx,edx + mov cl,shift + shld edx,eax,cl + shl eax,cl + div b + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + mod_64x32 - perform a signed 64 bit x 32 bit + divide and return the 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define mod_64x32 _mod_64x32 +INLINE INT32 _mod_64x32(INT64 a, INT32 b) +{ + INT32 result; + INT32 alow = a; + INT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + idiv b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + modu_64x32 - perform an unsigned 64 bit x 32 bit + divide and return the 32 bit remainder +-------------------------------------------------*/ + +#ifndef PTR64 +#define modu_64x32 _modu_64x32 +INLINE UINT32 _modu_64x32(UINT64 a, UINT32 b) +{ + UINT32 result; + UINT32 alow = a; + UINT32 ahigh = a >> 32; + + __asm + { + mov eax,alow + mov edx,ahigh + div b + mov result,edx + } + + return result; +} +#endif + + +/*------------------------------------------------- + recip_approx - compute an approximate floating + point reciprocal +-------------------------------------------------*/ + +#ifdef PTR64 +#define recip_approx _recip_approx +INLINE float _recip_approx(float z) +{ + __m128 mz = _mm_set_ss(z); + __m128 mooz = _mm_rcp_ss(mz); + float ooz; + _mm_store_ss(&ooz, mooz); + return ooz; +} +#endif + + + +/*************************************************************************** + INLINE BIT MANIPULATION FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + count_leading_zeros - return the number of + leading zero bits in a 32-bit value +-------------------------------------------------*/ + +#ifndef PTR64 +#define count_leading_zeros _count_leading_zeros +INLINE UINT8 _count_leading_zeros(UINT32 value) +{ + INT32 result; + + __asm + { + bsr eax,value + jnz skip + mov eax,63 + skip: + xor eax,31 + mov result,eax + } + + return result; +} +#endif + + +/*------------------------------------------------- + count_leading_ones - return the number of + leading one bits in a 32-bit value +-------------------------------------------------*/ + +#ifndef PTR64 +#define count_leading_ones _count_leading_ones +INLINE UINT8 _count_leading_ones(UINT32 value) +{ + INT32 result; + + __asm + { + mov eax,value + not eax + bsr eax,eax + jnz skip + mov eax,63 + skip: + xor eax,31 + mov result,eax + } + + return result; +} +#endif + + + +/*************************************************************************** + INLINE TIMING FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_profile_ticks - return a tick counter + from the processor that can be used for + profiling. It does not need to run at any + particular rate. +-------------------------------------------------*/ + +#define get_profile_ticks _get_profile_ticks + +#ifdef PTR64 + +INLINE osd_ticks_t _get_profile_ticks(void) +{ + return __rdtsc(); +} + +#else + +INLINE osd_ticks_t _get_profile_ticks(void) +{ + UINT64 result; + UINT64 *presult = &result; + + __asm { + __asm _emit 0Fh __asm _emit 031h // rdtsc + mov ebx, presult + mov [ebx],eax + mov [ebx+4],edx + } + + return result; +} + +#endif + +#endif /* __EIVCX86__ */ diff --git a/archivers/chd/windows/osinline.h b/archivers/chd/windows/osinline.h new file mode 100644 index 00000000..a84da488 --- /dev/null +++ b/archivers/chd/windows/osinline.h @@ -0,0 +1,53 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// osinline.h +// +// Inline implementations for non-GCC Win32 compilers +// +//============================================================ + +#ifndef __OSINLINE__ +#define __OSINLINE__ + +#if 0 +#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) +#include "eivcx86.h" +#endif +#endif + +#if defined(_MSC_VER) +#include "eivc.h" +#endif + +INT32 win_compare_exchange32(INT32 volatile *ptr, INT32 compare, INT32 exchange); +INT32 win_atomic_exchange32(INT32 volatile *ptr, INT32 exchange); +INT32 win_atomic_add32(INT32 volatile *ptr, INT32 delta); + +#ifdef PTR64 +INT64 win_compare_exchange64(INT64 volatile *ptr, INT64 compare, INT64 exchange); +#endif + + +#ifndef compare_exchange32 +#define compare_exchange32 win_compare_exchange32 +#endif /* compare_exchange32 */ + +#ifdef PTR64 +#ifndef compare_exchange64 +#define compare_exchange64 win_compare_exchange64 +#endif /* compare_exchange64 */ +#endif + +#ifndef atomic_exchange32 +#define atomic_exchange32 win_atomic_exchange32 +#endif /* atomic_exchange32 */ + + +#ifndef atomic_add32 +#define atomic_add32 win_atomic_add32 +#endif /* atomic_add32 */ + + +#endif /* __OSINLINE__ */ diff --git a/archivers/chd/windows/wintime.cpp b/archivers/chd/windows/wintime.cpp new file mode 100644 index 00000000..b1b88e0f --- /dev/null +++ b/archivers/chd/windows/wintime.cpp @@ -0,0 +1,111 @@ +#include "../chdtypes.h" +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// wintime.c - Win32 OSD core timing functions +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include + +// MAME headers +#include "../osdcore.h" + + + +//============================================================ +// GLOBAL VARIABLES +//============================================================ + +static osd_ticks_t ticks_per_second = 0; +static osd_ticks_t suspend_ticks = 0; +static BOOL using_qpc = TRUE; + + + +//============================================================ +// osd_ticks +//============================================================ + +osd_ticks_t osd_ticks(void) +{ + LARGE_INTEGER performance_count; + + // if we're suspended, just return that + if (suspend_ticks != 0) + return suspend_ticks; + + // if we have a per second count, just go for it + if (ticks_per_second != 0) + { + // QueryPerformanceCounter if we can + if (using_qpc) + { + QueryPerformanceCounter(&performance_count); + return (osd_ticks_t)performance_count.QuadPart - suspend_ticks; + } + + // otherwise, fall back to timeGetTime + else + return (osd_ticks_t)timeGetTime() - suspend_ticks; + } + + // if not, we have to determine it + using_qpc = QueryPerformanceFrequency(&performance_count) && (performance_count.QuadPart != 0); + if (using_qpc) + ticks_per_second = (osd_ticks_t)performance_count.QuadPart; + else + ticks_per_second = 1000; + + // call ourselves to get the first value + return osd_ticks(); +} + + +//============================================================ +// osd_ticks_per_second +//============================================================ + +osd_ticks_t osd_ticks_per_second(void) +{ + if (ticks_per_second == 0) + osd_ticks(); + return ticks_per_second; +} + + +//============================================================ +// osd_sleep +//============================================================ + +void osd_sleep(osd_ticks_t duration) +{ + DWORD msec; + + // make sure we've computed ticks_per_second + if (ticks_per_second == 0) + (void)osd_ticks(); + + // convert to milliseconds, rounding down + msec = (DWORD)(duration * 1000 / ticks_per_second); + + // only sleep if at least 2 full milliseconds + if (msec >= 2) + { + HANDLE current_thread = GetCurrentThread(); + int old_priority = GetThreadPriority(current_thread); + + // take a couple of msecs off the top for good measure + msec -= 2; + + // bump our thread priority super high so that we get + // priority when we need it + SetThreadPriority(current_thread, THREAD_PRIORITY_TIME_CRITICAL); + Sleep(msec); + SetThreadPriority(current_thread, old_priority); + } +} diff --git a/archivers/chd/windows/winwork.cpp b/archivers/chd/windows/winwork.cpp new file mode 100644 index 00000000..adf46fb4 --- /dev/null +++ b/archivers/chd/windows/winwork.cpp @@ -0,0 +1,785 @@ +#include "../chdtypes.h" +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +//============================================================ +// +// winwork.c - Win32 OSD core work item functions +// +//============================================================ + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#ifdef __GNUC__ +#include +#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); +}