diff options
Diffstat (limited to 'src/lib/util')
-rw-r--r-- | src/lib/util/aviio.cpp | 3369 | ||||
-rw-r--r-- | src/lib/util/aviio.h | 188 | ||||
-rw-r--r-- | src/lib/util/cdrom.cpp | 4 | ||||
-rw-r--r-- | src/lib/util/chd.cpp | 16 | ||||
-rw-r--r-- | src/lib/util/chdcd.cpp | 55 | ||||
-rw-r--r-- | src/lib/util/corefile.cpp | 168 | ||||
-rw-r--r-- | src/lib/util/corefile.h | 25 | ||||
-rw-r--r-- | src/lib/util/un7z.cpp | 693 | ||||
-rw-r--r-- | src/lib/util/un7z.h | 152 | ||||
-rw-r--r-- | src/lib/util/unzip.cpp | 794 | ||||
-rw-r--r-- | src/lib/util/unzip.h | 187 | ||||
-rw-r--r-- | src/lib/util/zippath.cpp | 175 | ||||
-rw-r--r-- | src/lib/util/zippath.h | 4 |
13 files changed, 2813 insertions, 3017 deletions
diff --git a/src/lib/util/aviio.cpp b/src/lib/util/aviio.cpp index a448128070b..8483adb24f6 100644 --- a/src/lib/util/aviio.cpp +++ b/src/lib/util/aviio.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** aviio.c @@ -8,8 +8,10 @@ ***************************************************************************/ -#include <stdlib.h> -#include <assert.h> +#include <array> +#include <cassert> +#include <cstdlib> +#include <cstring> #include "aviio.h" @@ -45,7 +47,7 @@ * @brief A macro that defines four gigabytes. */ -#define FOUR_GB ((UINT64)1 << 32) +#define FOUR_GB (std::uint64_t(1) << 32) /** * @def MAX_SOUND_CHANNELS @@ -222,7 +224,7 @@ #define HUFFYUV_PREDICT_DECORR 0x40 - +namespace { /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ @@ -235,14 +237,12 @@ struct avi_chunk { - /** @brief The offset. */ - UINT64 offset; /* file offset of chunk header */ - /** @brief The size. */ - UINT64 size; /* size of this chunk */ - /** @brief The type. */ - UINT32 type; /* type of this chunk */ - /** @brief The listtype. */ - UINT32 listtype; /* type of this list (if we are a list) */ + avi_chunk() : offset(0), size(0), type(0), listtype(0) { } + + std::uint64_t offset; /* file offset of chunk header */ + std::uint64_t size; /* size of this chunk */ + std::uint32_t type; /* type of this chunk */ + std::uint32_t listtype; /* type of this list (if we are a list) */ }; /** @@ -253,211 +253,297 @@ struct avi_chunk struct avi_chunk_list { - /** @brief The offset. */ - UINT64 offset; /* offset in the file of header */ - /** @brief The length. */ - UINT32 length; /* length of the chunk including header */ + std::uint64_t offset; /* offset in the file of header */ + std::uint32_t length; /* length of the chunk including header */ }; /** - * @struct huffyuv_table + * @struct avi_stream * - * @brief A huffyuv table. + * @brief An avi stream. */ -struct huffyuv_table -{ - /** @brief The shift[ 256]. */ - UINT8 shift[256]; /* bit shift amounts */ - /** @brief The bits[ 256]. */ - UINT32 bits[256]; /* bit match values */ - /** @brief The mask[ 256]. */ - UINT32 mask[256]; /* bit mask values */ - /** @brief The baselookup[ 65536]. */ - UINT16 baselookup[65536]; /* base lookup table */ - /** @brief The extralookup. */ - UINT16 * extralookup; /* extra lookup tables */ -}; +class avi_stream +{ +public: + avi_stream() + : m_type(0) + , m_format(0) + , m_rate(0) + , m_scale(0) + , m_samples(0) + , m_chunks() + , m_width(0) + , m_height(0) + , m_depth(0) + , m_interlace(0) + , m_huffyuv() + , m_channels(0) + , m_samplebits(0) + , m_samplerate(0) + , m_saved_strh_offset(0) + , m_saved_indx_offset(0) + { + } -/** - * @struct huffyuv_data - * - * @brief A huffyuv data. - */ + void initialize_video(avi_file::movie_info const &info) + { + m_type = STREAMTYPE_VIDS; + m_format = info.video_format; + m_rate = info.video_timescale; + m_scale = info.video_sampletime; + m_width = info.video_width; + m_height = info.video_height; + m_depth = info.video_depth; + } -struct huffyuv_data -{ - /** @brief The predictor. */ - UINT8 predictor; /* predictor */ - /** @brief The table[ 3]. */ - huffyuv_table table[3]; /* array of tables */ -}; + void initialize_audio(avi_file::movie_info const &info) + { + m_type = STREAMTYPE_AUDS; + m_format = info.audio_format; + m_rate = info.audio_timescale; + m_scale = info.audio_sampletime; + m_channels = info.audio_channels; + m_samplebits = info.audio_samplebits; + m_samplerate = info.audio_samplerate; + } -/** - * @struct avi_stream - * - * @brief An avi stream. - */ + std::uint32_t type() const { return m_type; } + std::uint32_t format() const { return m_format; } + + std::uint32_t rate() const { return m_rate; } + std::uint32_t scale() const { return m_scale; } + std::uint32_t samples() const { return m_samples; } + void set_samples(std::uint32_t value) { m_samples = value; } + std::uint32_t add_samples(std::uint32_t value) { return m_samples += value; } + + std::uint32_t chunks() const { return m_chunks.size(); } + avi_chunk_list const &chunk(std::uint32_t index) const { assert(index < m_chunks.size()); return m_chunks[index]; } + avi_chunk_list &chunk(std::uint32_t index) { assert(index < m_chunks.size()); return m_chunks[index]; } + avi_file::error set_chunk_info(std::uint32_t index, std::uint64_t offset, std::uint32_t length); + void set_chunks(std::uint32_t count) { assert(count <= m_chunks.size()); m_chunks.resize(count); } + + std::uint32_t width() const { return m_width; } + std::uint32_t height() const { return m_height; } + std::uint32_t depth() const { return m_depth; } + + std::uint16_t channels() const { return m_channels; } + std::uint16_t samplebits() const { return m_samplebits; } + std::uint32_t samplerate() const { return m_samplerate; } + std::uint32_t bytes_per_sample() const { return (m_samplebits / 8) * m_channels; } + + avi_file::error set_strh_data(std::uint8_t const *data, std::uint32_t size); + avi_file::error set_strf_data(std::uint8_t const *data, std::uint32_t size); + + std::uint64_t &saved_strh_offset() { return m_saved_strh_offset; } + std::uint64_t &saved_indx_offset() { return m_saved_indx_offset; } + + // RGB helpers + avi_file::error rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const; + + // YUY helpers + avi_file::error yuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const; + avi_file::error yuy16_compress_to_yuy(const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const; + + // HuffYUV helpers + avi_file::error huffyuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const; + +private: + struct huffyuv_table + { + std::uint8_t shift[256]; /* bit shift amounts */ + std::uint32_t bits[256]; /* bit match values */ + std::uint32_t mask[256]; /* bit mask values */ + std::uint16_t baselookup[65536]; /* base lookup table */ + std::vector<std::uint16_t> extralookup; /* extra lookup tables */ + }; + + struct huffyuv_data + { + std::uint8_t predictor; /* predictor */ + huffyuv_table table[3]; /* array of tables */ + }; -struct avi_stream -{ - /** @brief The type. */ - UINT32 type; /* subtype of stream */ - /** @brief Describes the format to use. */ - UINT32 format; /* format of stream data */ - - /** @brief The rate. */ - UINT32 rate; /* timescale for stream */ - /** @brief The scale. */ - UINT32 scale; /* duration of one sample in the stream */ - /** @brief The samples. */ - UINT32 samples; /* number of samples */ - - /** @brief The chunk. */ - avi_chunk_list * chunk; /* list of chunks */ - /** @brief The chunks. */ - UINT32 chunks; /* chunks currently known */ - /** @brief The chunksalloc. */ - UINT32 chunksalloc; /* number of chunks allocated */ - - /** @brief The width. */ - UINT32 width; /* width of video */ - /** @brief The height. */ - UINT32 height; /* height of video */ - /** @brief The depth. */ - UINT32 depth; /* depth of video */ - /** @brief The interlace. */ - UINT8 interlace; /* interlace parameters */ - /** @brief The huffyuv. */ - huffyuv_data * huffyuv; /* huffyuv decompression data */ - - /** @brief The channels. */ - UINT16 channels; /* audio channels */ - /** @brief The samplebits. */ - UINT16 samplebits; /* audio bits per sample */ - /** @brief The samplerate. */ - UINT32 samplerate; /* audio sample rate */ + avi_file::error huffyuv_extract_tables(const std::uint8_t *chunkdata, std::uint32_t size); + + std::uint32_t m_type; /* subtype of stream */ + std::uint32_t m_format; /* format of stream data */ + + std::uint32_t m_rate; /* timescale for stream */ + std::uint32_t m_scale; /* duration of one sample in the stream */ + std::uint32_t m_samples; /* number of samples */ + + std::vector<avi_chunk_list> m_chunks; /* list of chunks */ + + std::uint32_t m_width; /* width of video */ + std::uint32_t m_height; /* height of video */ + std::uint32_t m_depth; /* depth of video */ + std::uint8_t m_interlace; /* interlace parameters */ + std::unique_ptr<huffyuv_data const> m_huffyuv; /* huffyuv decompression data */ + + std::uint16_t m_channels; /* audio channels */ + std::uint16_t m_samplebits; /* audio bits per sample */ + std::uint32_t m_samplerate; /* audio sample rate */ /* only used when creating */ - /** @brief The saved strh offset. */ - UINT64 saved_strh_offset; /* writeoffset of strh chunk */ - /** @brief The saved indx offset. */ - UINT64 saved_indx_offset; /* writeoffset of indx chunk */ + std::uint64_t m_saved_strh_offset; /* writeoffset of strh chunk */ + std::uint64_t m_saved_indx_offset; /* writeoffset of indx chunk */ }; /** - * @struct avi_file + * @class avi_file_impl * * @brief An avi file. */ -struct avi_file +class avi_file_impl : public avi_file { - /* shared data */ - /** @brief The file. */ - osd_file * file; /* pointer to open file */ - /** @brief The type. */ - int type; /* type of access (read/create) */ - /** @brief The information. */ - avi_movie_info info; /* movie info structure */ - /** @brief The tempbuffer. */ - UINT8 * tempbuffer; /* temporary buffer */ - /** @brief The tempbuffersize. */ - UINT32 tempbuffersize; /* size of the temporary buffer */ - - /* only used when reading */ - /** @brief The streams. */ - int streams; /* number of streams */ - /** @brief The stream. */ - avi_stream * stream; /* allocated array of stream information */ - /** @brief The rootchunk. */ - avi_chunk rootchunk; /* dummy root chunk that wraps the whole file */ - - /* only used when creating */ - /** @brief The writeoffs. */ - UINT64 writeoffs; /* current file write offset */ - /** @brief The riffbase. */ - UINT64 riffbase; /* base of the current RIFF */ - - /** @brief The chunkstack[ 8]. */ - avi_chunk chunkstack[8]; /* stack of chunks we are writing */ - /** @brief The chunksp. */ - int chunksp; /* stack pointer for the current chunk */ - - /** @brief The saved movi offset. */ - UINT64 saved_movi_offset; /* writeoffset of movi list */ - /** @brief The saved avih offset. */ - UINT64 saved_avih_offset; /* writeoffset of avih chunk */ - - /** @brief The soundbuf. */ - INT16 * soundbuf; /* buffer for sound data */ - /** @brief The soundbuf samples. */ - UINT32 soundbuf_samples; /* length of sound buffer in samples */ - /** @brief The soundbuf chansamples[ maximum sound channels]. */ - UINT32 soundbuf_chansamples[MAX_SOUND_CHANNELS]; /* samples in buffer for each channel */ - /** @brief The soundbuf chunks. */ - UINT32 soundbuf_chunks; /* number of chunks completed so far */ - /** @brief The soundbuf frames. */ - UINT32 soundbuf_frames; /* number of frames ahead of the video */ -}; - +public: + avi_file_impl(osd_file::ptr &&file, std::uint64_t length) : avi_file_impl() + { + m_file = std::move(file); + m_type = FILETYPE_READ; + + // make a root atom + m_rootchunk.offset = 0; + m_rootchunk.size = length; + m_rootchunk.type = 0; + m_rootchunk.listtype = 0; + } + avi_file_impl(osd_file::ptr &&file, movie_info const &info) : avi_file_impl() + { + m_file = std::move(file); + m_type = FILETYPE_CREATE; -/*************************************************************************** - PROTOTYPES -***************************************************************************/ + // copy the movie info + m_info = info; + m_info.video_numsamples = 0; + m_info.audio_numsamples = 0; -/* stream helpers */ + // initialize the video track + m_streams.emplace_back(); + m_streams.back().initialize_video(m_info); -/* core chunk read routines */ -static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk); -static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk); -static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result); -static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result); -static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset); -static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer); + // initialize the audio track + if (m_info.audio_channels > 0) + { + m_streams.emplace_back(); + m_streams.back().initialize_audio(m_info); + } + } -/* chunk read helpers */ -static avi_error read_movie_data(avi_file *file); -static avi_error extract_movie_info(avi_file *file); -static avi_error parse_avih_chunk(avi_file *file, avi_chunk *avih); -static avi_error parse_strh_chunk(avi_file *file, avi_stream *stream, avi_chunk *strh); -static avi_error parse_strf_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf); -static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf); -static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1); + virtual ~avi_file_impl() override; + + virtual void printf_chunks() override; + + virtual movie_info const &get_movie_info() const override; + virtual std::uint32_t first_sample_in_frame(std::uint32_t framenum) const override; + + virtual error read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) override; + virtual error read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) override; + + virtual error append_video_frame(bitmap_yuy16 &bitmap) override; + virtual error append_video_frame(bitmap_rgb32 &bitmap) override; + virtual error append_sound_samples(int channel, std::int16_t const *samples, std::uint32_t numsamples, std::uint32_t sampleskip) override; + + error read_movie_data(); + error write_initial_headers(); + error soundbuf_initialize(); + +private: + avi_file_impl() + : m_file() + , m_type(0) + , m_info({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }) + , m_tempbuffer() + , m_streams() + , m_rootchunk() + , m_writeoffs(0) + , m_riffbase(0) + , m_chunkstack() + , m_chunksp(0) + , m_saved_movi_offset(0) + , m_saved_avih_offset(0) + , m_soundbuf() + , m_soundbuf_samples(0) + , m_soundbuf_chansamples{ 0 } + , m_soundbuf_chunks(0) + , m_soundbuf_frames(0) + { + } -/* core chunk write routines */ -static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength); -static avi_error chunk_close(avi_file *file); -static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length); -static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write); + avi_stream *get_video_stream(); + avi_stream *get_audio_stream(int channel, int &offset); + std::uint32_t compute_idx1_size() const; + std::uint32_t get_chunkid_for_stream(const avi_stream *stream) const; + std::uint32_t framenum_to_samplenum(std::uint32_t framenum) const; + error expand_tempbuffer(std::uint32_t length); + + // core chunk read routines + error get_first_chunk(avi_chunk const *parent, avi_chunk &newchunk); + error get_next_chunk(avi_chunk const *parent, avi_chunk &newchunk); + error find_first_chunk(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error find_next_chunk(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error find_first_list(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error find_next_list(std::uint32_t findme, avi_chunk const *container, avi_chunk &result); + error get_next_chunk_internal(avi_chunk const *parent, avi_chunk &newchunk, std::uint64_t offset); + error read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer); + + // chunk read helpers + error extract_movie_info(); + error parse_avih_chunk(avi_chunk const &avih); + error parse_strh_chunk(avi_stream &stream, avi_chunk const &strh); + error parse_strf_chunk(avi_stream &stream, avi_chunk const &strf); + error parse_indx_chunk(avi_stream &stream, avi_chunk const &strf); + error parse_idx1_chunk(std::uint64_t baseoffset, avi_chunk const &idx1); + + // core chunk write routines + error chunk_open(std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength); + error chunk_close(); + error chunk_write(std::uint32_t type, const void *data, std::uint32_t length); + error chunk_overwrite(std::uint32_t type, const void *data, std::uint32_t length, std::uint64_t &offset, bool initial_write); + + // chunk write helpers + error write_avih_chunk(bool initial_write); + error write_strh_chunk(avi_stream &stream, bool initial_write); + error write_strf_chunk(avi_stream const &stream); + error write_indx_chunk(avi_stream &stream, bool initial_write); + error write_idx1_chunk(); + + // sound buffering helpers + error soundbuf_write_chunk(std::uint32_t framenum); + error soundbuf_flush(bool only_flush_full); + + // debugging + void printf_chunk_recursive(avi_chunk const *chunk, int indent); -/* chunk write helpers */ -static avi_error write_initial_headers(avi_file *file); -static avi_error write_avih_chunk(avi_file *file, int initial_write); -static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initial_write); -static avi_error write_strf_chunk(avi_file *file, avi_stream *stream); -static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initial_write); -static avi_error write_idx1_chunk(avi_file *file); + /* shared data */ + osd_file::ptr m_file; /* pointer to open file */ + int m_type; /* type of access (read/create) */ + movie_info m_info; /* movie info structure */ + std::vector<std::uint8_t> m_tempbuffer; /* temporary buffer */ -/* sound buffering helpers */ -static avi_error soundbuf_initialize(avi_file *file); -static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum); -static avi_error soundbuf_flush(avi_file *file, int only_flush_full); + /* only used when reading */ + std::vector<avi_stream> m_streams; /* allocated array of stream information */ + avi_chunk m_rootchunk; /* dummy root chunk that wraps the whole file */ -/* RGB helpers */ -static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, UINT8 *data, UINT32 numbytes); + /* only used when creating */ + std::uint64_t m_writeoffs; /* current file write offset */ + std::uint64_t m_riffbase; /* base of the current RIFF */ -/* YUY helpers */ -static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap); -static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, UINT8 *data, UINT32 numbytes); + std::array<avi_chunk, 8> m_chunkstack; /* stack of chunks we are writing */ + int m_chunksp; /* stack pointer for the current chunk */ -/* HuffYUV helpers */ -static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size); -static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap); + std::uint64_t m_saved_movi_offset; /* writeoffset of movi list */ + std::uint64_t m_saved_avih_offset; /* writeoffset of avih chunk */ -/* debugging */ -static void printf_chunk_recursive(avi_file *file, avi_chunk *chunk, int indent); + std::vector<std::int16_t> m_soundbuf; /* buffer for sound data */ + std::uint32_t m_soundbuf_samples; /* length of sound buffer in samples */ + std::uint32_t m_soundbuf_chansamples[MAX_SOUND_CHANNELS]; /* samples in buffer for each channel */ + std::uint32_t m_soundbuf_chunks; /* number of chunks completed so far */ + std::uint32_t m_soundbuf_frames; /* number of frames ahead of the video */ +}; @@ -471,7 +557,7 @@ static void printf_chunk_recursive(avi_file *file, avi_chunk *chunk, int indent) -------------------------------------------------*/ /** - * @fn static inline UINT16 fetch_16bits(const UINT8 *data) + * @fn static inline std::uint16_t fetch_16bits(const std::uint8_t *data) * * @brief Fetches the 16bits. * @@ -480,7 +566,7 @@ static void printf_chunk_recursive(avi_file *file, avi_chunk *chunk, int indent) * @return The 16bits. */ -static inline UINT16 fetch_16bits(const UINT8 *data) +inline std::uint16_t fetch_16bits(const std::uint8_t *data) { return data[0] | (data[1] << 8); } @@ -492,7 +578,7 @@ static inline UINT16 fetch_16bits(const UINT8 *data) -------------------------------------------------*/ /** - * @fn static inline UINT32 fetch_32bits(const UINT8 *data) + * @fn static inline std::uint32_t fetch_32bits(const std::uint8_t *data) * * @brief Fetches the 32bits. * @@ -501,7 +587,7 @@ static inline UINT16 fetch_16bits(const UINT8 *data) * @return The 32bits. */ -static inline UINT32 fetch_32bits(const UINT8 *data) +inline std::uint32_t fetch_32bits(const std::uint8_t *data) { return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); } @@ -513,7 +599,7 @@ static inline UINT32 fetch_32bits(const UINT8 *data) -------------------------------------------------*/ /** - * @fn static inline UINT64 fetch_64bits(const UINT8 *data) + * @fn static inline std::uint64_t fetch_64bits(const std::uint8_t *data) * * @brief Fetches the 64bits. * @@ -522,12 +608,12 @@ static inline UINT32 fetch_32bits(const UINT8 *data) * @return The 64bits. */ -static inline UINT64 fetch_64bits(const UINT8 *data) +inline std::uint64_t fetch_64bits(const std::uint8_t *data) { - return (UINT64)data[0] | ((UINT64)data[1] << 8) | - ((UINT64)data[2] << 16) | ((UINT64)data[3] << 24) | - ((UINT64)data[4] << 32) | ((UINT64)data[5] << 40) | - ((UINT64)data[6] << 48) | ((UINT64)data[7] << 56); + return std::uint64_t(data[0]) | (std::uint64_t(data[1]) << 8) | + (std::uint64_t(data[2]) << 16) | (std::uint64_t(data[3]) << 24) | + (std::uint64_t(data[4]) << 32) | (std::uint64_t(data[5]) << 40) | + (std::uint64_t(data[6]) << 48) | (std::uint64_t(data[7]) << 56); } @@ -537,7 +623,7 @@ static inline UINT64 fetch_64bits(const UINT8 *data) -------------------------------------------------*/ /** - * @fn static inline void put_16bits(UINT8 *data, UINT16 value) + * @fn static inline void put_16bits(std::uint8_t *data, std::uint16_t value) * * @brief Puts the 16bits. * @@ -545,7 +631,7 @@ static inline UINT64 fetch_64bits(const UINT8 *data) * @param value The value. */ -static inline void put_16bits(UINT8 *data, UINT16 value) +inline void put_16bits(std::uint8_t *data, std::uint16_t value) { data[0] = value >> 0; data[1] = value >> 8; @@ -558,7 +644,7 @@ static inline void put_16bits(UINT8 *data, UINT16 value) -------------------------------------------------*/ /** - * @fn static inline void put_32bits(UINT8 *data, UINT32 value) + * @fn static inline void put_32bits(std::uint8_t *data, std::uint32_t value) * * @brief Puts the 32bits. * @@ -566,7 +652,7 @@ static inline void put_16bits(UINT8 *data, UINT16 value) * @param value The value. */ -static inline void put_32bits(UINT8 *data, UINT32 value) +inline void put_32bits(std::uint8_t *data, std::uint32_t value) { data[0] = value >> 0; data[1] = value >> 8; @@ -581,7 +667,7 @@ static inline void put_32bits(UINT8 *data, UINT32 value) -------------------------------------------------*/ /** - * @fn static inline void put_64bits(UINT8 *data, UINT64 value) + * @fn static inline void put_64bits(std::uint8_t *data, std::uint64_t value) * * @brief Puts the 64bits. * @@ -589,7 +675,7 @@ static inline void put_32bits(UINT8 *data, UINT32 value) * @param value The value. */ -static inline void put_64bits(UINT8 *data, UINT64 value) +inline void put_64bits(std::uint8_t *data, std::uint64_t value) { data[0] = value >> 0; data[1] = value >> 8; @@ -602,29 +688,120 @@ static inline void put_64bits(UINT8 *data, UINT64 value) } +/** + * @fn static void u64toa(std::uint64_t val, char *output) + * + * @brief 64toas. + * + * @param val The value. + * @param [in,out] output If non-null, the output. + */ + +inline void u64toa(std::uint64_t val, char *output) +{ + std::uint32_t lo = std::uint32_t(val & 0xffffffff); + std::uint32_t hi = std::uint32_t(val >> 32); + if (hi != 0) + sprintf(output, "%X%08X", hi, lo); + else + sprintf(output, "%X", lo); +} + + +/*------------------------------------------------- + set_stream_chunk_info - set the chunk info + for a given chunk within a stream +-------------------------------------------------*/ + +/** + * @fn static inline avi_error set_stream_chunk_info(avi_stream *stream, std::uint32_t index, std::uint64_t offset, std::uint32_t length) + * + * @brief Sets stream chunk information. + * + * @param [in,out] stream If non-null, the stream. + * @param index Zero-based index of the. + * @param offset The offset. + * @param length The length. + * + * @return An avi_error. + */ + +inline avi_file::error avi_stream::set_chunk_info(std::uint32_t index, std::uint64_t offset, std::uint32_t length) +{ + /* if we need to allocate more, allocate more */ + if (index >= m_chunks.capacity()) + { + try { m_chunks.reserve((std::max<std::size_t>)(index, m_chunks.capacity() + 1000)); } + catch (...) { return avi_file::error::NO_MEMORY; } + } + + /* update the number of chunks */ + m_chunks.resize((std::max<std::size_t>)(m_chunks.size(), index + 1)); + + /* set the data */ + m_chunks[index].offset = offset; + m_chunks[index].length = length; + + return avi_file::error::NONE; +} + + +inline avi_file::error avi_stream::set_strh_data(std::uint8_t const *data, std::uint32_t size) +{ + m_type = fetch_32bits(&data[0]); + m_scale = fetch_32bits(&data[20]); + m_rate = fetch_32bits(&data[24]); + m_samples = fetch_32bits(&data[32]); + return avi_file::error::NONE; +} + + +inline avi_file::error avi_stream::set_strf_data(std::uint8_t const *data, std::uint32_t size) +{ + /* audio and video streams have differing headers */ + if (m_type == STREAMTYPE_VIDS) + { + m_width = fetch_32bits(&data[4]); + m_height = fetch_32bits(&data[8]); + m_depth = fetch_16bits(&data[14]); + m_format = fetch_32bits(&data[16]); + + /* extra extraction for HuffYUV data */ + if ((m_format == FORMAT_HFYU) && (size >= 56)) + { + avi_file::error const avierr = huffyuv_extract_tables(data, size); + if (avierr != avi_file::error::NONE) + return avierr; + } + } + else if (m_type == STREAMTYPE_AUDS) + { + m_channels = fetch_16bits(&data[2]); + m_samplebits = fetch_16bits(&data[14]); + m_samplerate = fetch_32bits(&data[4]); + } + return avi_file::error::NONE; +} + + /*------------------------------------------------- get_video_stream - return a pointer to the video stream -------------------------------------------------*/ /** - * @fn static inline avi_stream *get_video_stream(avi_file *file) + * @fn static inline avi_stream *get_video_stream() * * @brief Gets video stream. * - * @param [in,out] file If non-null, the file. - * * @return null if it fails, else the video stream. */ -static inline avi_stream *get_video_stream(avi_file *file) +inline avi_stream *avi_file_impl::get_video_stream() { - int streamnum; - - /* find the video stream */ - for (streamnum = 0; streamnum < file->streams; streamnum++) - if (file->stream[streamnum].type == STREAMTYPE_VIDS) - return &file->stream[streamnum]; + for (int streamnum = 0; streamnum < m_streams.size(); streamnum++) + if (m_streams[streamnum].type() == STREAMTYPE_VIDS) + return &m_streams[streamnum]; return nullptr; } @@ -647,21 +824,18 @@ static inline avi_stream *get_video_stream(avi_file *file) * @return null if it fails, else the audio stream. */ -static inline avi_stream *get_audio_stream(avi_file *file, int channel, int *offset) +inline avi_stream *avi_file_impl::get_audio_stream(int channel, int &offset) { - int streamnum; - /* find the audios stream */ - for (streamnum = 0; streamnum < file->streams; streamnum++) - if (file->stream[streamnum].type == STREAMTYPE_AUDS) + for (int streamnum = 0; streamnum < m_streams.size(); streamnum++) + if (m_streams[streamnum].type() == STREAMTYPE_AUDS) { - if (channel < file->stream[streamnum].channels) + if (channel < m_streams[streamnum].channels()) { - if (offset != nullptr) - *offset = channel; - return &file->stream[streamnum]; + offset = channel; + return &m_streams[streamnum]; } - channel -= file->stream[streamnum].channels; + channel -= m_streams[streamnum].channels(); } return nullptr; @@ -669,58 +843,12 @@ static inline avi_stream *get_audio_stream(avi_file *file, int channel, int *off /*------------------------------------------------- - set_stream_chunk_info - set the chunk info - for a given chunk within a stream --------------------------------------------------*/ - -/** - * @fn static inline avi_error set_stream_chunk_info(avi_stream *stream, UINT32 index, UINT64 offset, UINT32 length) - * - * @brief Sets stream chunk information. - * - * @param [in,out] stream If non-null, the stream. - * @param index Zero-based index of the. - * @param offset The offset. - * @param length The length. - * - * @return An avi_error. - */ - -static inline avi_error set_stream_chunk_info(avi_stream *stream, UINT32 index, UINT64 offset, UINT32 length) -{ - /* if we need to allocate more, allocate more */ - if (index >= stream->chunksalloc) - { - UINT32 newcount = MAX(index, stream->chunksalloc + 1000); - avi_chunk_list *newchunks = (avi_chunk_list *)malloc(newcount * sizeof(stream->chunk[0])); - if (newchunks == nullptr) - return AVIERR_NO_MEMORY; - if (stream->chunk != nullptr) - { - memcpy(newchunks, stream->chunk, stream->chunksalloc * sizeof(stream->chunk[0])); - free(stream->chunk); - } - stream->chunk = newchunks; - stream->chunksalloc = newcount; - } - - /* set the data */ - stream->chunk[index].offset = offset; - stream->chunk[index].length = length; - - /* update the number of chunks */ - stream->chunks = MAX(stream->chunks, index + 1); - return AVIERR_NONE; -} - - -/*------------------------------------------------- compute_idx1_size - compute the size of the idx1 chunk -------------------------------------------------*/ /** - * @fn static inline UINT32 compute_idx1_size(avi_file *file) + * @fn static inline std::uint32_t compute_idx1_size(avi_file *file) * * @brief Calculates the index 1 size. * @@ -729,14 +857,13 @@ static inline avi_error set_stream_chunk_info(avi_stream *stream, UINT32 index, * @return The calculated index 1 size. */ -static inline UINT32 compute_idx1_size(avi_file *file) +inline std::uint32_t avi_file_impl::compute_idx1_size() const { int chunks = 0; - int strnum; /* count chunks in streams */ - for (strnum = 0; strnum < file->streams; strnum++) - chunks += file->stream[strnum].chunks; + for (int strnum = 0; strnum < m_streams.size(); strnum++) + chunks += m_streams[strnum].chunks(); return chunks * 16 + 8; } @@ -748,7 +875,7 @@ static inline UINT32 compute_idx1_size(avi_file *file) -------------------------------------------------*/ /** - * @fn static inline UINT32 get_chunkid_for_stream(avi_file *file, avi_stream *stream) + * @fn static inline std::uint32_t get_chunkid_for_stream(avi_file *file, avi_stream *stream) * * @brief Gets chunkid for stream. * @@ -758,14 +885,14 @@ static inline UINT32 compute_idx1_size(avi_file *file) * @return The chunkid for stream. */ -static inline UINT32 get_chunkid_for_stream(avi_file *file, avi_stream *stream) +inline std::uint32_t avi_file_impl::get_chunkid_for_stream(const avi_stream *stream) const { - UINT32 chunkid; + std::uint32_t chunkid; - chunkid = AVI_FOURCC('0' + (stream - file->stream) / 10, '0' + (stream - file->stream) % 10, 0, 0); - if (stream->type == STREAMTYPE_VIDS) - chunkid |= (stream->format == 0) ? CHUNKTYPE_XXDB : CHUNKTYPE_XXDC; - else if (stream->type == STREAMTYPE_AUDS) + chunkid = AVI_FOURCC('0' + (stream - &m_streams[0]) / 10, '0' + (stream - &m_streams[0]) % 10, 0, 0); + if (stream->type() == STREAMTYPE_VIDS) + chunkid |= (stream->format() == 0) ? CHUNKTYPE_XXDB : CHUNKTYPE_XXDC; + else if (stream->type() == STREAMTYPE_AUDS) chunkid |= CHUNKTYPE_XXWB; return chunkid; @@ -778,19 +905,19 @@ static inline UINT32 get_chunkid_for_stream(avi_file *file, avi_stream *stream) -------------------------------------------------*/ /** - * @fn static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) + * @fn static inline std::uint32_t framenum_to_samplenum(avi_file *file, std::uint32_t framenum) * * @brief Framenum to samplenum. * * @param [in,out] file If non-null, the file. * @param framenum The framenum. * - * @return An UINT32. + * @return An std::uint32_t. */ -static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) +inline std::uint32_t avi_file_impl::framenum_to_samplenum(std::uint32_t framenum) const { - return ((UINT64)file->info.audio_samplerate * (UINT64)framenum * (UINT64)file->info.video_sampletime + file->info.video_timescale - 1) / (UINT64)file->info.video_timescale; + return (std::uint64_t(m_info.audio_samplerate) * std::uint64_t(framenum) * std::uint64_t(m_info.video_sampletime) + m_info.video_timescale - 1) / std::uint64_t(m_info.video_timescale); } @@ -801,7 +928,7 @@ static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) -------------------------------------------------*/ /** - * @fn static inline avi_error expand_tempbuffer(avi_file *file, UINT32 length) + * @fn static inline avi_error expand_tempbuffer(avi_file *file, std::uint32_t length) * * @brief Expand tempbuffer. * @@ -811,207 +938,532 @@ static inline UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) * @return An avi_error. */ -static inline avi_error expand_tempbuffer(avi_file *file, UINT32 length) +inline avi_file::error avi_file_impl::expand_tempbuffer(std::uint32_t length) { /* expand the tempbuffer to hold the data if necessary */ - if (length > file->tempbuffersize) + if (length > m_tempbuffer.size()) { - file->tempbuffersize = 2 * length; - UINT8 *newbuffer = (UINT8 *)malloc(file->tempbuffersize); - if (newbuffer == nullptr) - return AVIERR_NO_MEMORY; - if (file->tempbuffer != nullptr) - free(file->tempbuffer); - file->tempbuffer = newbuffer; + try { m_tempbuffer.resize(2 * length); } + catch (...) { return error::NO_MEMORY; } } - return AVIERR_NONE; + return error::NONE; } +/*------------------------------------------------- + rgb32_compress_to_rgb - "compress" an RGB32 + bitmap to an RGB encoded frame +-------------------------------------------------*/ + +/** + * @fn static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) + * + * @brief RGB 32 compress to RGB. + * + * @param [in,out] stream If non-null, the stream. + * @param bitmap The bitmap. + * @param [in,out] data If non-null, the data. + * @param numbytes The numbytes. + * + * @return An avi_error. + */ + +avi_file::error avi_stream::rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const +{ + int const height = (std::min<int>)(m_height, bitmap.height()); + int const width = (std::min<int>)(m_width, bitmap.width()); + std::uint8_t *const dataend = data + numbytes; + int x, y; + + /* compressed video */ + for (y = 0; y < height; y++) + { + const std::uint32_t *source = &bitmap.pix32(y); + std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3; + + for (x = 0; x < width && dest < dataend; x++) + { + rgb_t pix = *source++; + *dest++ = pix.b(); + *dest++ = pix.g(); + *dest++ = pix.r(); + } + + /* fill in any blank space on the right */ + for ( ; x < m_width && dest < dataend; x++) + { + *dest++ = 0; + *dest++ = 0; + *dest++ = 0; + } + } + + /* fill in any blank space on the bottom */ + for ( ; y < m_height; y++) + { + std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3; + for (x = 0; x < m_width && dest < dataend; x++) + { + *dest++ = 0; + *dest++ = 0; + *dest++ = 0; + } + } + + return avi_file::error::NONE; +} -/*************************************************************************** - IMPLEMENTATION -***************************************************************************/ /*------------------------------------------------- - avi_open - open an AVI movie file for read + yuv_decompress_to_yuy16 - decompress a YUV + encoded frame to a YUY16 bitmap -------------------------------------------------*/ /** - * @fn avi_error avi_open(const char *filename, avi_file **file) + * @fn static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) * - * @brief Queries if a given avi open. + * @brief Yuv decompress to yuy 16. * - * @param filename Filename of the file. - * @param [in,out] file If non-null, the file. + * @param [in,out] stream If non-null, the stream. + * @param data The data. + * @param numbytes The numbytes. + * @param [in,out] bitmap The bitmap. * * @return An avi_error. */ -avi_error avi_open(const char *filename, avi_file **file) +avi_file::error avi_stream::yuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const { - avi_file *newfile = nullptr; - file_error filerr; - avi_error avierr; - UINT64 length; - - /* allocate the file */ - newfile = (avi_file *)malloc(sizeof(*newfile)); - if (newfile == nullptr) - return AVIERR_NO_MEMORY; - memset(newfile, 0, sizeof(*newfile)); - newfile->type = FILETYPE_READ; + std::uint16_t const *const dataend = reinterpret_cast<const std::uint16_t *>(data + numbytes); + int x, y; - /* open the file */ - filerr = osd_open(filename, OPEN_FLAG_READ, &newfile->file, &length); - if (filerr != FILERR_NONE) + /* compressed video */ + for (y = 0; y < m_height; y++) { - avierr = AVIERR_CANT_OPEN_FILE; - goto error; + const std::uint16_t *source = reinterpret_cast<const std::uint16_t *>(data) + y * m_width; + std::uint16_t *dest = &bitmap.pix16(y); + + /* switch off the compression */ + switch (m_format) + { + case FORMAT_UYVY: + for (x = 0; x < m_width && source < dataend; x++) + *dest++ = *source++; + break; + + case FORMAT_VYUY: + case FORMAT_YUY2: + for (x = 0; x < m_width && source < dataend; x++) + { + std::uint16_t pix = *source++; + *dest++ = (pix >> 8) | (pix << 8); + } + break; + } } - /* make a root atom */ - newfile->rootchunk.offset = 0; - newfile->rootchunk.size = length; - newfile->rootchunk.type = 0; - newfile->rootchunk.listtype = 0; + return avi_file::error::NONE; +} - /* parse the data */ - avierr = read_movie_data(newfile); - if (avierr != AVIERR_NONE) - goto error; - *file = newfile; - return AVIERR_NONE; +/*------------------------------------------------- + yuy16_compress_to_yuy - "compress" a YUY16 + bitmap to a YUV encoded frame +-------------------------------------------------*/ -error: - /* clean up after an error */ - if (newfile != nullptr) +/** + * @fn static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) + * + * @brief Yuy 16 compress to yuy. + * + * @param [in,out] stream If non-null, the stream. + * @param bitmap The bitmap. + * @param [in,out] data If non-null, the data. + * @param numbytes The numbytes. + * + * @return An avi_error. + */ + +avi_file::error avi_stream::yuy16_compress_to_yuy(const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const +{ + std::uint16_t *const dataend = reinterpret_cast<std::uint16_t *>(data + numbytes); + int x, y; + + /* compressed video */ + for (y = 0; y < m_height; y++) { - if (newfile->file != nullptr) - osd_close(newfile->file); - free(newfile); + const std::uint16_t *source = &bitmap.pix16(y); + std::uint16_t *dest = reinterpret_cast<std::uint16_t *>(data) + y * m_width; + + /* switch off the compression */ + switch (m_format) + { + case FORMAT_UYVY: + for (x = 0; x < m_width && dest < dataend; x++) + *dest++ = *source++; + break; + + case FORMAT_VYUY: + case FORMAT_YUY2: + for (x = 0; x < m_width && dest < dataend; x++) + { + std::uint16_t pix = *source++; + *dest++ = (pix >> 8) | (pix << 8); + } + break; + } } - return avierr; + + return avi_file::error::NONE; } /*------------------------------------------------- - avi_create - create a new AVI movie file + huffyuv_extract_tables - extract HuffYUV + tables -------------------------------------------------*/ /** - * @fn avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file) + * @fn static avi_error huffyuv_extract_tables(avi_stream *stream, const std::uint8_t *chunkdata, std::uint32_t size) * - * @brief Avi create. + * @brief Huffyuv extract tables. * - * @param filename Filename of the file. - * @param info The information. - * @param [in,out] file If non-null, the file. + * @param [in,out] stream If non-null, the stream. + * @param chunkdata The chunkdata. + * @param size The size. * * @return An avi_error. */ -avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file) +avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata, std::uint32_t size) { - avi_file *newfile = nullptr; - file_error filerr; - avi_stream *stream; - avi_error avierr; - UINT64 length; + const std::uint8_t *const chunkend = chunkdata + size; - /* validate video info */ - if ((info->video_format != 0 && info->video_format != FORMAT_UYVY && info->video_format != FORMAT_VYUY && info->video_format != FORMAT_YUY2) || - info->video_width == 0 || - info->video_height == 0 || - info->video_depth == 0 || info->video_depth % 8 != 0) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + /* allocate memory for the data */ + std::unique_ptr<huffyuv_data> huffyuv; + try { huffyuv = std::make_unique<huffyuv_data>(); } + catch (...) { return avi_file::error::NO_MEMORY; } - /* validate audio info */ - if (info->audio_format != 0 || - info->audio_channels > MAX_SOUND_CHANNELS || - info->audio_samplebits != 16) - return AVIERR_UNSUPPORTED_AUDIO_FORMAT; + /* extract predictor information */ + if (&chunkdata[40] >= chunkend) + return avi_file::error::INVALID_DATA; + huffyuv->predictor = chunkdata[40]; - /* allocate the file */ - newfile = (avi_file *)malloc(sizeof(*newfile)); - if (newfile == nullptr) - return AVIERR_NO_MEMORY; - memset(newfile, 0, sizeof(*newfile)); - newfile->type = FILETYPE_CREATE; + /* make sure it's the left predictor */ + if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) != HUFFYUV_PREDICT_LEFT) + return avi_file::error::UNSUPPORTED_VIDEO_FORMAT; - /* open the file */ - filerr = osd_open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &newfile->file, &length); - if (filerr != FILERR_NONE) + /* make sure it's 16bpp YUV data */ + if (chunkdata[41] != 16) + return avi_file::error::UNSUPPORTED_VIDEO_FORMAT; + chunkdata += 44; + + /* loop over tables */ + for (int tabnum = 0; tabnum < 3; tabnum++) { - avierr = AVIERR_CANT_OPEN_FILE; - goto error; + huffyuv_table &table = huffyuv->table[tabnum]; + std::uint32_t curbits, bitadd; + std::uint16_t bitsat16 = 0; + int offset = 0, bits; + + /* loop until we populate the whole table */ + while (offset < 256) + { + int data, shift, count, i; + + /* extract the next run */ + if (chunkdata >= chunkend) + return avi_file::error::INVALID_DATA; + data = *chunkdata++; + shift = data & 0x1f; + count = data >> 5; + + /* zero count means next whole byte is a count */ + if (count == 0) + { + if (chunkdata >= chunkend) + return avi_file::error::INVALID_DATA; + count = *chunkdata++; + } + for (i = 0; i < count; i++) + table.shift[offset++] = shift; + } + + /* now determine bit patterns and masks */ + curbits = 0; + for (bits = 31; bits >= 0; bits--) + { + bitadd = 1 << (32 - bits); + + /* make sure we've cleared out all the bits below */ + if ((curbits & (bitadd - 1)) != 0) + return avi_file::error::INVALID_DATA; + + /* find all entries with this shift count and assign them */ + for (offset = 0; offset < 256; offset++) + if (table.shift[offset] == bits) + { + table.bits[offset] = curbits; + table.mask[offset] = ~(bitadd - 1); + curbits += bitadd; + } + + /* remember the bit pattern when we complete all the 17-bit codes */ + if (bits == 17) + bitsat16 = curbits >> 16; + } + + /* allocate the number of extra lookup tables we need */ + if (bitsat16 > 0) + { + try { table.extralookup.resize(bitsat16 * 65536); } + catch (...) { return avi_file::error::NO_MEMORY; } + for (offset = 0; offset < bitsat16; offset++) + table.baselookup[offset] = (offset << 8) | 0; + } + + /* then create lookup tables */ + for (offset = 0; offset < 256; offset++) + if (table.shift[offset] > 16) + { + std::uint16_t *tablebase = &table.extralookup[(table.bits[offset] >> 16) * 65536]; + std::uint32_t start = table.bits[offset] & 0xffff; + std::uint32_t end = start + ((1 << (32 - table.shift[offset])) - 1); + while (start <= end) + tablebase[start++] = (offset << 8) | (table.shift[offset] - 16); + } + else if (table.shift[offset] > 0) + { + std::uint32_t start = table.bits[offset] >> 16; + std::uint32_t end = start + ((1 << (16 - table.shift[offset])) - 1); + while (start <= end) + table.baselookup[start++] = (offset << 8) | table.shift[offset]; + } } - /* copy the movie info */ - newfile->info = *info; - newfile->info.video_numsamples = 0; - newfile->info.audio_numsamples = 0; + m_huffyuv = std::move(huffyuv); + return avi_file::error::NONE; +} + - /* allocate two streams */ - newfile->stream = (avi_stream *)malloc(2 * sizeof(newfile->stream[0])); - if (newfile->stream == nullptr) +/*------------------------------------------------- + huffyuv_decompress_to_yuy16 - decompress a + HuffYUV-encoded frame to a YUY16 bitmap +-------------------------------------------------*/ + +/** + * @fn static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) + * + * @brief Huffyuv decompress to yuy 16. + * + * @param [in,out] stream If non-null, the stream. + * @param data The data. + * @param numbytes The numbytes. + * @param [in,out] bitmap The bitmap. + * + * @return An avi_error. + */ + +avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const +{ + int prevlines = (m_height > 288) ? 2 : 1; + std::uint8_t lastprevy = 0, lastprevcb = 0, lastprevcr = 0; + std::uint8_t lasty = 0, lastcb = 0, lastcr = 0; + std::uint8_t bitsinbuffer = 0; + std::uint32_t bitbuffer = 0; + std::uint32_t dataoffs = 0; + int x, y; + + /* compressed video */ + for (y = 0; y < m_height; y++) { - avierr = AVIERR_NO_MEMORY; - goto error; + std::uint16_t *dest = &bitmap.pix16(y); + + /* handle the first four bytes independently */ + x = 0; + if (y == 0) + { + /* first DWORD is stored as YUY2 */ + lasty = data[dataoffs++]; + lastcb = data[dataoffs++]; + *dest++ = (lasty << 8) | lastcb; + lasty = data[dataoffs++]; + lastcr = data[dataoffs++]; + *dest++ = (lasty << 8) | lastcr; + x = 2; + } + + /* loop over pixels */ + for ( ; x < m_width; x++) + { + huffyuv_table const &ytable = m_huffyuv->table[0]; + huffyuv_table const &ctable = m_huffyuv->table[1 + (x & 1)]; + std::uint16_t huffdata; + int shift; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + /* look up the Y component */ + huffdata = ytable.baselookup[bitbuffer >> 16]; + shift = huffdata & 0xff; + if (shift == 0) + { + bitsinbuffer -= 16; + bitbuffer <<= 16; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + huffdata = ytable.extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; + shift = huffdata & 0xff; + } + bitsinbuffer -= shift; + bitbuffer <<= shift; + std::uint16_t const pixel = huffdata & 0xff00; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + /* look up the Cb/Cr component */ + huffdata = ctable.baselookup[bitbuffer >> 16]; + shift = huffdata & 0xff; + if (shift == 0) + { + bitsinbuffer -= 16; + bitbuffer <<= 16; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + huffdata = ctable.extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; + shift = huffdata & 0xff; + } + bitsinbuffer -= shift; + bitbuffer <<= shift; + *dest++ = pixel | (huffdata >> 8); + } } - memset(newfile->stream, 0, 2 * sizeof(newfile->stream[0])); - - /* initialize the video track */ - stream = &newfile->stream[newfile->streams++]; - stream->type = STREAMTYPE_VIDS; - stream->format = newfile->info.video_format; - stream->rate = newfile->info.video_timescale; - stream->scale = newfile->info.video_sampletime; - stream->width = newfile->info.video_width; - stream->height = newfile->info.video_height; - stream->depth = newfile->info.video_depth; - - /* initialize the audio track */ - if (newfile->info.audio_channels > 0) + + /* apply deltas */ + lastprevy = lastprevcb = lastprevcr = 0; + for (y = 0; y < m_height; y++) { - stream = &newfile->stream[newfile->streams++]; - stream->type = STREAMTYPE_AUDS; - stream->format = newfile->info.audio_format; - stream->rate = newfile->info.audio_timescale; - stream->scale = newfile->info.audio_sampletime; - stream->channels = newfile->info.audio_channels; - stream->samplebits = newfile->info.audio_samplebits; - stream->samplerate = newfile->info.audio_samplerate; - - /* initialize the sound buffering */ - avierr = soundbuf_initialize(newfile); - if (avierr != AVIERR_NONE) - goto error; - } + std::uint16_t *prevrow = &bitmap.pix16(y - prevlines); + std::uint16_t *dest = &bitmap.pix16(y); - /* write the initial headers */ - avierr = write_initial_headers(newfile); - if (avierr != AVIERR_NONE) - goto error; + /* handle the first four bytes independently */ + x = 0; + if (y == 0) + { + /* lasty, lastcr, lastcb are set up previously */ + x = 2; + } - *file = newfile; - return AVIERR_NONE; + /* left predict or gradient predict */ + if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_LEFT || + ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT)) + { + /* first do left deltas */ + for ( ; x < m_width; x += 2) + { + std::uint16_t pixel0 = dest[x + 0]; + std::uint16_t pixel1 = dest[x + 1]; -error: - /* clean up after an error */ - if (newfile != nullptr) - { - if (newfile->stream != nullptr) - free(newfile->stream); - if (newfile->file != nullptr) + lasty += pixel0 >> 8; + lastcb += pixel0; + dest[x + 0] = (lasty << 8) | lastcb; + + lasty += pixel1 >> 8; + lastcr += pixel1; + dest[x + 1] = (lasty << 8) | lastcr; + } + + /* for gradient, we then add in the previous row */ + if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT && y >= prevlines) + for (x = 0; x < m_width; x++) + { + std::uint16_t curpix = dest[x]; + std::uint16_t prevpix = prevrow[x]; + std::uint8_t ysum = (curpix >> 8) + (prevpix >> 8); + std::uint8_t csum = curpix + prevpix; + dest[x] = (ysum << 8) | csum; + } + } + + /* median predict on rows > 0 */ + else if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_MEDIAN && y >= prevlines) { - osd_close(newfile->file); - osd_rmfile(filename); + for ( ; x < m_width; x += 2) + { + std::uint16_t prevpixel0 = prevrow[x + 0]; + std::uint16_t prevpixel1 = prevrow[x + 1]; + std::uint16_t pixel0 = dest[x + 0]; + std::uint16_t pixel1 = dest[x + 1]; + std::uint8_t a, b, c; + + /* compute previous, above, and (prev + above - above-left) */ + a = lasty; + b = prevpixel0 >> 8; + c = lastprevy; + lastprevy = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lasty = (pixel0 >> 8) + b; + + /* compute previous, above, and (prev + above - above-left) */ + a = lastcb; + b = prevpixel0 & 0xff; + c = lastprevcb; + lastprevcb = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lastcb = (pixel0 & 0xff) + b; + dest[x + 0] = (lasty << 8) | lastcb; + + /* compute previous, above, and (prev + above - above-left) */ + a = lasty; + b = prevpixel1 >> 8; + c = lastprevy; + lastprevy = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lasty = (pixel1 >> 8) + b; + + /* compute previous, above, and (prev + above - above-left) */ + a = lastcr; + b = prevpixel1 & 0xff; + c = lastprevcr; + lastprevcr = b; + if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; } + if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; } + if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; } + lastcr = (pixel1 & 0xff) + b; + dest[x + 1] = (lasty << 8) | lastcr; + } } - free(newfile); } - return avierr; + + return avi_file::error::NONE; } @@ -1029,73 +1481,46 @@ error: * @return An avi_error. */ -avi_error avi_close(avi_file *file) +avi_file_impl::~avi_file_impl() { - avi_error avierr = AVIERR_NONE; - int strnum; + error avierr = error::NONE; /* if we're creating a new file, finalize it by writing out the non-media chunks */ - if (file->type == FILETYPE_CREATE) + if (m_type == FILETYPE_CREATE) { /* flush any pending sound data */ - avierr = soundbuf_flush(file, FALSE); + avierr = soundbuf_flush(false); /* close the movi chunk */ - if (avierr == AVIERR_NONE) - avierr = chunk_close(file); + if (avierr == error::NONE) + avierr = chunk_close(); /* if this is the first RIFF chunk, write an idx1 */ - if (avierr == AVIERR_NONE && file->riffbase == 0) - avierr = write_idx1_chunk(file); + if (avierr == error::NONE && m_riffbase == 0) + avierr = write_idx1_chunk(); /* update the strh and indx chunks for each stream */ - for (strnum = 0; strnum < file->streams; strnum++) + for (int strnum = 0; strnum < m_streams.size(); strnum++) { - if (avierr == AVIERR_NONE) - avierr = write_strh_chunk(file, &file->stream[strnum], FALSE); - if (avierr == AVIERR_NONE) - avierr = write_indx_chunk(file, &file->stream[strnum], FALSE); + if (avierr == error::NONE) + avierr = write_strh_chunk(m_streams[strnum], false); + if (avierr == error::NONE) + avierr = write_indx_chunk(m_streams[strnum], false); } /* update the avih chunk */ - if (avierr == AVIERR_NONE) - avierr = write_avih_chunk(file, FALSE); + if (avierr == error::NONE) + avierr = write_avih_chunk(false); /* close the RIFF chunk */ - if (avierr == AVIERR_NONE) - avierr = chunk_close(file); + if (avierr == error::NONE) + avierr = chunk_close(); } /* close the file */ - osd_close(file->file); - - /* free the stream-specific data */ - for (strnum = 0; strnum < file->streams; strnum++) - { - avi_stream *stream = &file->stream[strnum]; - if (stream->huffyuv != nullptr) - { - huffyuv_data *huffyuv = stream->huffyuv; - int table; + m_file.reset(); - for (table = 0; table < ARRAY_LENGTH(huffyuv->table); table++) - if (huffyuv->table[table].extralookup != nullptr) - free(huffyuv->table[table].extralookup); - free(huffyuv); - } - if (stream->chunk != nullptr) - free(stream->chunk); - } - - /* free the file itself */ - if (file->soundbuf != nullptr) - free(file->soundbuf); - if (file->stream != nullptr) - free(file->stream); - if (file->tempbuffer != nullptr) - free(file->tempbuffer); - free(file); - return avierr; + //return avierr; } @@ -1111,55 +1536,14 @@ avi_error avi_close(avi_file *file) * @param [in,out] file If non-null, the file. */ -void avi_printf_chunks(avi_file *file) -{ - printf_chunk_recursive(file, &file->rootchunk, 0); -} - - -/*------------------------------------------------- - avi_error_string - get the error string for - an avi_error --------------------------------------------------*/ - -/** - * @fn const char *avi_error_string(avi_error err) - * - * @brief Avi error string. - * - * @param err The error. - * - * @return null if it fails, else a char*. - */ - -const char *avi_error_string(avi_error err) +void avi_file_impl::printf_chunks() { - switch (err) - { - case AVIERR_NONE: return "success"; - case AVIERR_END: return "hit end of file"; - case AVIERR_INVALID_DATA: return "invalid data"; - case AVIERR_NO_MEMORY: return "out of memory"; - case AVIERR_READ_ERROR: return "read error"; - case AVIERR_WRITE_ERROR: return "write error"; - case AVIERR_STACK_TOO_DEEP: return "stack overflow"; - case AVIERR_UNSUPPORTED_FEATURE: return "unsupported feature"; - case AVIERR_CANT_OPEN_FILE: return "unable to open file"; - case AVIERR_INCOMPATIBLE_AUDIO_STREAMS: return "found incompatible audio streams"; - case AVIERR_INVALID_SAMPLERATE: return "found invalid sample rate"; - case AVIERR_INVALID_STREAM: return "invalid stream"; - case AVIERR_INVALID_FRAME: return "invalid frame index"; - case AVIERR_INVALID_BITMAP: return "invalid bitmap"; - case AVIERR_UNSUPPORTED_VIDEO_FORMAT: return "unsupported video format"; - case AVIERR_UNSUPPORTED_AUDIO_FORMAT: return "unsupported audio format"; - case AVIERR_EXCEEDED_SOUND_BUFFER: return "sound buffer overflow"; - default: return "undocumented error"; - } + printf_chunk_recursive(&m_rootchunk, 0); } /*------------------------------------------------- - avi_get_movie_info - return a pointer to the + get_movie_info - return a pointer to the movie info -------------------------------------------------*/ @@ -1173,9 +1557,9 @@ const char *avi_error_string(avi_error err) * @return null if it fails, else an avi_movie_info*. */ -const avi_movie_info *avi_get_movie_info(avi_file *file) +avi_file::movie_info const &avi_file_impl::get_movie_info() const { - return &file->info; + return m_info; } @@ -1185,19 +1569,19 @@ const avi_movie_info *avi_get_movie_info(avi_file *file) -------------------------------------------------*/ /** - * @fn UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) + * @fn std::uint32_t avi_first_sample_in_frame(avi_file *file, std::uint32_t framenum) * * @brief Avi first sample in frame. * * @param [in,out] file If non-null, the file. * @param framenum The framenum. * - * @return An UINT32. + * @return An std::uint32_t. */ -UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) +std::uint32_t avi_file_impl::first_sample_in_frame(std::uint32_t framenum) const { - return framenum_to_samplenum(file, framenum); + return framenum_to_samplenum(framenum); } @@ -1208,7 +1592,7 @@ UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) -------------------------------------------------*/ /** - * @fn avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bitmap) + * @fn avi_error avi_read_video_frame(avi_file *file, std::uint32_t framenum, bitmap_yuy16 &bitmap) * * @brief Avi read video frame. * @@ -1219,54 +1603,51 @@ UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) * @return An avi_error. */ -avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bitmap) +avi_file::error avi_file_impl::read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) { - avi_error avierr = AVIERR_NONE; - UINT32 bytes_read, chunkid; - file_error filerr; - avi_stream *stream; - /* get the video stream */ - stream = get_video_stream(file); - if (stream == nullptr) - return AVIERR_INVALID_STREAM; + avi_stream *const stream = get_video_stream(); + if (!stream) + return error::INVALID_STREAM; /* validate our ability to handle the data */ - if (stream->format != FORMAT_UYVY && stream->format != FORMAT_VYUY && stream->format != FORMAT_YUY2 && stream->format != FORMAT_HFYU) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU) + return error::UNSUPPORTED_VIDEO_FORMAT; /* assume one chunk == one frame */ - if (framenum >= stream->chunks) - return AVIERR_INVALID_FRAME; + if (framenum >= stream->chunks()) + return error::INVALID_FRAME; /* we only support YUY-style bitmaps (16bpp) */ - if (bitmap.width() < stream->width || bitmap.height() < stream->height) - return AVIERR_INVALID_BITMAP; + if (bitmap.width() < stream->width() || bitmap.height() < stream->height()) + return error::INVALID_BITMAP; /* expand the tempbuffer to hold the data if necessary */ - avierr = expand_tempbuffer(file, stream->chunk[framenum].length); - if (avierr != AVIERR_NONE) + error avierr = error::NONE; + avierr = expand_tempbuffer(stream->chunk(framenum).length); + if (avierr != error::NONE) return avierr; /* read in the data */ - filerr = osd_read(file->file, file->tempbuffer, stream->chunk[framenum].offset, stream->chunk[framenum].length, &bytes_read); - if (filerr != FILERR_NONE || bytes_read != stream->chunk[framenum].length) - return AVIERR_READ_ERROR; + std::uint32_t bytes_read; + osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length) + return error::READ_ERROR; /* validate this is good data */ - chunkid = fetch_32bits(&file->tempbuffer[0]); - if (chunkid == get_chunkid_for_stream(file, stream)) + std::uint32_t const chunkid = fetch_32bits(&m_tempbuffer[0]); + if (chunkid == get_chunkid_for_stream(stream)) { /* HuffYUV-compressed */ - if (stream->format == FORMAT_HFYU) - avierr = huffyuv_decompress_to_yuy16(stream, file->tempbuffer + 8, stream->chunk[framenum].length - 8, bitmap); + if (stream->format() == FORMAT_HFYU) + avierr = stream->huffyuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap); /* other YUV-compressed */ else - avierr = yuv_decompress_to_yuy16(stream, file->tempbuffer + 8, stream->chunk[framenum].length - 8, bitmap); + avierr = stream->yuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap); } else - avierr = AVIERR_INVALID_DATA; + avierr = error::INVALID_DATA; return avierr; } @@ -1278,7 +1659,7 @@ avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bi -------------------------------------------------*/ /** - * @fn avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output) + * @fn avi_error avi_read_sound_samples(avi_file *file, int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) * * @brief Avi read sound samples. * @@ -1291,95 +1672,90 @@ avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bi * @return An avi_error. */ -avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output) +avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) { - avi_error avierr = AVIERR_NONE; - UINT32 bytes_per_sample; - file_error filerr; - avi_stream *stream; - int offset = 0; - /* get the audio stream */ - stream = get_audio_stream(file, channel, &offset); - if (stream == nullptr) - return AVIERR_INVALID_STREAM; + int offset = 0; + avi_stream *const stream = get_audio_stream(channel, offset); + if (!stream) + return error::INVALID_STREAM; /* validate our ability to handle the data */ - if (stream->format != 0 || (stream->samplebits != 8 && stream->samplebits != 16)) - return AVIERR_UNSUPPORTED_AUDIO_FORMAT; + if (stream->format() != 0 || (stream->samplebits() != 8 && stream->samplebits() != 16)) + return error::UNSUPPORTED_AUDIO_FORMAT; /* verify we are in range */ - if (firstsample >= stream->samples) - return AVIERR_INVALID_FRAME; - if (firstsample + numsamples > stream->samples) - numsamples = stream->samples - firstsample; + if (firstsample >= stream->samples()) + return error::INVALID_FRAME; + if (firstsample + numsamples > stream->samples()) + numsamples = stream->samples() - firstsample; /* determine bytes per sample */ - bytes_per_sample = (stream->samplebits / 8) * stream->channels; + std::uint32_t const bytes_per_sample = stream->bytes_per_sample(); /* loop until all samples have been extracted */ while (numsamples > 0) { - UINT32 chunkbase = 0, chunkend = 0, chunkid; - UINT32 bytes_read, samples_this_chunk; + std::uint32_t chunkbase = 0, chunkend = 0, chunkid; + std::uint32_t bytes_read, samples_this_chunk; int chunknum, sampnum; /* locate the chunk with the first sample */ - for (chunknum = 0; chunknum < stream->chunks; chunknum++) + for (chunknum = 0; chunknum < stream->chunks(); chunknum++) { - chunkend = chunkbase + (stream->chunk[chunknum].length - 8) / bytes_per_sample; + chunkend = chunkbase + (stream->chunk(chunknum).length - 8) / bytes_per_sample; if (firstsample < chunkend) break; chunkbase = chunkend; } /* if we hit the end, fill the rest with silence */ - if (chunknum == stream->chunks) + if (chunknum == stream->chunks()) { - memset(output, 0, numsamples * 2); + std::memset(output, 0, numsamples * 2); break; } /* expand the tempbuffer to hold the data if necessary */ - avierr = expand_tempbuffer(file, stream->chunk[chunknum].length); - if (avierr != AVIERR_NONE) + error avierr = expand_tempbuffer(stream->chunk(chunknum).length); + if (avierr != error::NONE) return avierr; /* read in the data */ - filerr = osd_read(file->file, file->tempbuffer, stream->chunk[chunknum].offset, stream->chunk[chunknum].length, &bytes_read); - if (filerr != FILERR_NONE || bytes_read != stream->chunk[chunknum].length) - return AVIERR_READ_ERROR; + auto const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(chunknum).offset, stream->chunk(chunknum).length, bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(chunknum).length) + return error::READ_ERROR; /* validate this is good data */ - chunkid = fetch_32bits(&file->tempbuffer[0]); - if (chunkid != get_chunkid_for_stream(file, stream)) - return AVIERR_INVALID_DATA; + chunkid = fetch_32bits(&m_tempbuffer[0]); + if (chunkid != get_chunkid_for_stream(stream)) + return error::INVALID_DATA; /* determine how many samples to copy */ samples_this_chunk = chunkend - firstsample; - samples_this_chunk = MIN(samples_this_chunk, numsamples); + samples_this_chunk = (std::min)(samples_this_chunk, numsamples); /* extract 16-bit samples from the chunk */ - if (stream->samplebits == 16) + if (stream->samplebits() == 16) { - const INT16 *base = (const INT16 *)(file->tempbuffer + 8); - base += stream->channels * (firstsample - chunkbase) + offset; + const std::int16_t *base = reinterpret_cast<const std::int16_t *>(&m_tempbuffer[8]); + base += stream->channels() * (firstsample - chunkbase) + offset; for (sampnum = 0; sampnum < samples_this_chunk; sampnum++) { *output++ = LITTLE_ENDIANIZE_INT16(*base); - base += stream->channels; + base += stream->channels(); } } /* extract 8-bit samples from the chunk */ - else if (stream->samplebits == 8) + else if (stream->samplebits() == 8) { - const UINT8 *base = (const UINT8 *)(file->tempbuffer + 8); - base += stream->channels * (firstsample - chunkbase) + offset; + const std::uint8_t *base = &m_tempbuffer[8]; + base += stream->channels() * (firstsample - chunkbase) + offset; for (sampnum = 0; sampnum < samples_this_chunk; sampnum++) { *output++ = (*base << 8) - 0x8000; - base += stream->channels; + base += stream->channels(); } } @@ -1388,7 +1764,7 @@ avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample numsamples -= samples_this_chunk; } - return avierr; + return error::NONE; } @@ -1408,45 +1784,45 @@ avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample * @return An avi_error. */ -avi_error avi_append_video_frame(avi_file *file, bitmap_yuy16 &bitmap) +avi_file::error avi_file_impl::append_video_frame(bitmap_yuy16 &bitmap) { - avi_stream *stream = get_video_stream(file); - avi_error avierr; - UINT32 maxlength; + avi_stream *const stream = get_video_stream(); + error avierr; + std::uint32_t maxlength; /* validate our ability to handle the data */ - if (stream->format != FORMAT_UYVY && stream->format != FORMAT_VYUY && stream->format != FORMAT_YUY2 && stream->format != FORMAT_HFYU) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU) + return error::UNSUPPORTED_VIDEO_FORMAT; /* write out any sound data first */ - avierr = soundbuf_write_chunk(file, stream->chunks); - if (avierr != AVIERR_NONE) + avierr = soundbuf_write_chunk(stream->chunks()); + if (avierr != error::NONE) return avierr; /* make sure we have enough room */ - maxlength = 2 * stream->width * stream->height; - avierr = expand_tempbuffer(file, maxlength); - if (avierr != AVIERR_NONE) + maxlength = 2 * stream->width() * stream->height(); + avierr = expand_tempbuffer(maxlength); + if (avierr != error::NONE) return avierr; /* now compress the data */ - avierr = yuy16_compress_to_yuy(stream, bitmap, file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = stream->yuy16_compress_to_yuy(bitmap, &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* write the data */ - avierr = chunk_write(file, get_chunkid_for_stream(file, stream), file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* set the info for this new chunk */ - avierr = set_stream_chunk_info(stream, stream->chunks, file->writeoffs - maxlength - 8, maxlength + 8); - if (avierr != AVIERR_NONE) + avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8); + if (avierr != error::NONE) return avierr; - stream->samples = file->info.video_numsamples = stream->chunks; + stream->set_samples(m_info.video_numsamples = stream->chunks()); - return AVIERR_NONE; + return error::NONE; } @@ -1466,49 +1842,49 @@ avi_error avi_append_video_frame(avi_file *file, bitmap_yuy16 &bitmap) * @return An avi_error. */ -avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap) +avi_file::error avi_file_impl::append_video_frame(bitmap_rgb32 &bitmap) { - avi_stream *stream = get_video_stream(file); - avi_error avierr; - UINT32 maxlength; + avi_stream *const stream = get_video_stream(); + error avierr; + std::uint32_t maxlength; /* validate our ability to handle the data */ - if (stream->format != 0) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->format() != 0) + return error::UNSUPPORTED_VIDEO_FORMAT; /* depth must be 24 */ - if (stream->depth != 24) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + if (stream->depth() != 24) + return error::UNSUPPORTED_VIDEO_FORMAT; /* write out any sound data first */ - avierr = soundbuf_write_chunk(file, stream->chunks); - if (avierr != AVIERR_NONE) + avierr = soundbuf_write_chunk(stream->chunks()); + if (avierr != error::NONE) return avierr; /* make sure we have enough room */ - maxlength = 3 * stream->width * stream->height; - avierr = expand_tempbuffer(file, maxlength); - if (avierr != AVIERR_NONE) + maxlength = 3 * stream->width() * stream->height(); + avierr = expand_tempbuffer(maxlength); + if (avierr != error::NONE) return avierr; /* copy the RGB data to the destination */ - avierr = rgb32_compress_to_rgb(stream, bitmap, file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = stream->rgb32_compress_to_rgb(bitmap, &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* write the data */ - avierr = chunk_write(file, get_chunkid_for_stream(file, stream), file->tempbuffer, maxlength); - if (avierr != AVIERR_NONE) + avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength); + if (avierr != error::NONE) return avierr; /* set the info for this new chunk */ - avierr = set_stream_chunk_info(stream, stream->chunks, file->writeoffs - maxlength - 8, maxlength + 8); - if (avierr != AVIERR_NONE) + avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8); + if (avierr != error::NONE) return avierr; - stream->samples = file->info.video_numsamples = stream->chunks; + stream->set_samples(m_info.video_numsamples = stream->chunks()); - return AVIERR_NONE; + return error::NONE; } @@ -1518,7 +1894,7 @@ avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap) -------------------------------------------------*/ /** - * @fn avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples, UINT32 sampleskip) + * @fn avi_error avi_append_sound_samples(avi_file *file, int channel, const std::int16_t *samples, std::uint32_t numsamples, std::uint32_t sampleskip) * * @brief Avi append sound samples. * @@ -1531,27 +1907,27 @@ avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap) * @return An avi_error. */ -avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples, UINT32 sampleskip) +avi_file::error avi_file_impl::append_sound_samples(int channel, const std::int16_t *samples, std::uint32_t numsamples, std::uint32_t sampleskip) { - UINT32 sampoffset = file->soundbuf_chansamples[channel]; - UINT32 sampnum; + std::uint32_t sampoffset = m_soundbuf_chansamples[channel]; + std::uint32_t sampnum; /* see if we have enough room in the buffer */ - if (sampoffset + numsamples > file->soundbuf_samples) - return AVIERR_EXCEEDED_SOUND_BUFFER; + if (sampoffset + numsamples > m_soundbuf_samples) + return error::EXCEEDED_SOUND_BUFFER; /* append samples to the buffer in little-endian format */ for (sampnum = 0; sampnum < numsamples; sampnum++) { - INT16 data = *samples++; + std::int16_t data = *samples++; samples += sampleskip; data = LITTLE_ENDIANIZE_INT16(data); - file->soundbuf[sampoffset++ * file->info.audio_channels + channel] = data; + m_soundbuf[sampoffset++ * m_info.audio_channels + channel] = data; } - file->soundbuf_chansamples[channel] = sampoffset; + m_soundbuf_chansamples[channel] = sampoffset; /* flush any full sound chunks to disk */ - return soundbuf_flush(file, TRUE); + return soundbuf_flush(TRUE); } @@ -1561,7 +1937,7 @@ avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *sam -------------------------------------------------*/ /** - * @fn static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer) + * @fn static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, std::uint8_t **buffer) * * @brief Reads chunk data. * @@ -1572,26 +1948,22 @@ avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *sam * @return The chunk data. */ -static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer) +avi_file::error avi_file_impl::read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer) { - file_error filerr; - UINT32 bytes_read; - /* allocate memory for the data */ - *buffer = (UINT8 *)malloc(chunk->size); - if (*buffer == nullptr) - return AVIERR_NO_MEMORY; + try { buffer.reset(new std::uint8_t[chunk.size]); } + catch (...) { return error::NO_MEMORY; } /* read from the file */ - filerr = osd_read(file->file, *buffer, chunk->offset + 8, chunk->size, &bytes_read); - if (filerr != FILERR_NONE || bytes_read != chunk->size) + std::uint32_t bytes_read; + osd_file::error const filerr = m_file->read(&buffer[0], chunk.offset + 8, chunk.size, bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != chunk.size) { - free(*buffer); - *buffer = nullptr; - return AVIERR_READ_ERROR; + buffer.reset(); + return error::READ_ERROR; } - return AVIERR_NONE; + return error::NONE; } @@ -1612,12 +1984,12 @@ static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 * * @return The first chunk. */ -static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk) +avi_file::error avi_file_impl::get_first_chunk(avi_chunk const *parent, avi_chunk &newchunk) { - UINT64 startoffset = (parent != nullptr && parent->type != 0) ? parent->offset + 12 : 0; + std::uint64_t startoffset = (parent != nullptr && parent->type != 0) ? parent->offset + 12 : 0; if (parent != nullptr && parent->type != CHUNKTYPE_LIST && parent->type != CHUNKTYPE_RIFF && parent->type != 0) - return AVIERR_INVALID_DATA; - return get_next_chunk_internal(file, parent, newchunk, startoffset); + return error::INVALID_DATA; + return get_next_chunk_internal(parent, newchunk, startoffset); } @@ -1638,10 +2010,10 @@ static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_ch * @return The next chunk. */ -static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk) +avi_file::error avi_file_impl::get_next_chunk(avi_chunk const *parent, avi_chunk &newchunk) { - UINT64 nextoffset = newchunk->offset + 8 + newchunk->size + (newchunk->size & 1); - return get_next_chunk_internal(file, parent, newchunk, nextoffset); + std::uint64_t nextoffset = newchunk.offset + 8 + newchunk.size + (newchunk.size & 1); + return get_next_chunk_internal(parent, newchunk, nextoffset); } @@ -1651,7 +2023,7 @@ static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chu -------------------------------------------------*/ /** - * @fn static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_first_chunk(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the first chunk. * @@ -1663,13 +2035,13 @@ static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chu * @return The found chunk. */ -static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_first_chunk(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = get_first_chunk(file, container, result); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, result)) - if (result->type == findme) - return AVIERR_NONE; + for (avierr = get_first_chunk(container, result); avierr == error::NONE; avierr = get_next_chunk(container, result)) + if (result.type == findme) + return error::NONE; return avierr; } @@ -1681,7 +2053,7 @@ static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk -------------------------------------------------*/ /** - * @fn static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_next_chunk(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the next chunk. * @@ -1693,13 +2065,13 @@ static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk * @return The found chunk. */ -static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_next_chunk(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = get_next_chunk(file, container, result); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, result)) - if (result->type == findme) - return AVIERR_NONE; + for (avierr = get_next_chunk(container, result); avierr == error::NONE; avierr = get_next_chunk(container, result)) + if (result.type == findme) + return error::NONE; return avierr; } @@ -1711,7 +2083,7 @@ static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk -------------------------------------------------*/ /** - * @fn static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_first_list(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the first list. * @@ -1723,13 +2095,13 @@ static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk * @return The found list. */ -static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_first_list(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = find_first_chunk(file, CHUNKTYPE_LIST, container, result); avierr == AVIERR_NONE; avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result)) - if (result->listtype == findme) - return AVIERR_NONE; + for (avierr = find_first_chunk(CHUNKTYPE_LIST, container, result); avierr == error::NONE; avierr = find_next_chunk(CHUNKTYPE_LIST, container, result)) + if (result.listtype == findme) + return error::NONE; return avierr; } @@ -1741,7 +2113,7 @@ static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk -------------------------------------------------*/ /** - * @fn static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) + * @fn static avi_error find_next_list(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result) * * @brief Searches for the next list. * @@ -1753,13 +2125,13 @@ static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk * @return The found list. */ -static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +avi_file::error avi_file_impl::find_next_list(std::uint32_t findme, const avi_chunk *container, avi_chunk &result) { - avi_error avierr; + error avierr; - for (avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result); avierr == AVIERR_NONE; avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result)) - if (result->listtype == findme) - return AVIERR_NONE; + for (avierr = find_next_chunk(CHUNKTYPE_LIST, container, result); avierr == error::NONE; avierr = find_next_chunk(CHUNKTYPE_LIST, container, result)) + if (result.listtype == findme) + return error::NONE; return avierr; } @@ -1771,7 +2143,7 @@ static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk * -------------------------------------------------*/ /** - * @fn static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset) + * @fn static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, std::uint64_t offset) * * @brief Gets the next chunk internal. * @@ -1783,42 +2155,42 @@ static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk * * @return The next chunk internal. */ -static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset) +avi_file::error avi_file_impl::get_next_chunk_internal(const avi_chunk *parent, avi_chunk &newchunk, std::uint64_t offset) { - file_error filerr; - UINT8 buffer[12]; - UINT32 bytesread; + osd_file::error filerr; + std::uint8_t buffer[12]; + std::uint32_t bytesread; /* NULL parent implies the root */ if (parent == nullptr) - parent = &file->rootchunk; + parent = &m_rootchunk; /* start at the current offset */ - newchunk->offset = offset; + newchunk.offset = offset; /* if we're past the bounds of the parent, bail */ - if (newchunk->offset + 8 >= parent->offset + 8 + parent->size) - return AVIERR_END; + if (newchunk.offset + 8 >= parent->offset + 8 + parent->size) + return error::END; /* read the header */ - filerr = osd_read(file->file, buffer, newchunk->offset, 8, &bytesread); - if (filerr != FILERR_NONE || bytesread != 8) - return AVIERR_INVALID_DATA; + filerr = m_file->read(buffer, newchunk.offset, 8, bytesread); + if (filerr != osd_file::error::NONE || bytesread != 8) + return error::INVALID_DATA; /* fill in the new chunk */ - newchunk->type = fetch_32bits(&buffer[0]); - newchunk->size = fetch_32bits(&buffer[4]); + newchunk.type = fetch_32bits(&buffer[0]); + newchunk.size = fetch_32bits(&buffer[4]); /* if we are a list, fetch the list type */ - if (newchunk->type == CHUNKTYPE_LIST || newchunk->type == CHUNKTYPE_RIFF) + if (newchunk.type == CHUNKTYPE_LIST || newchunk.type == CHUNKTYPE_RIFF) { - filerr = osd_read(file->file, &buffer[8], newchunk->offset + 8, 4, &bytesread); - if (filerr != FILERR_NONE || bytesread != 4) - return AVIERR_INVALID_DATA; - newchunk->listtype = fetch_32bits(&buffer[8]); + filerr = m_file->read(&buffer[8], newchunk.offset + 8, 4, bytesread); + if (filerr != osd_file::error::NONE || bytesread != 4) + return error::INVALID_DATA; + newchunk.listtype = fetch_32bits(&buffer[8]); } - return AVIERR_NONE; + return error::NONE; } @@ -1836,98 +2208,95 @@ static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent * @return The movie data. */ -static avi_error read_movie_data(avi_file *file) +avi_file::error avi_file_impl::read_movie_data() { avi_chunk riff, hdrl, avih, strl, strh, strf, indx, movi, idx1; - avi_error avierr; - int strindex; + error avierr; /* find the RIFF chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_RIFF, nullptr, &riff); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_RIFF, nullptr, riff); + if (avierr != error::NONE) + return avierr; /* verify that the RIFF type is AVI */ if (riff.listtype != LISTTYPE_AVI) { - avierr = AVIERR_INVALID_DATA; - goto error; + avierr = error::INVALID_DATA; + return avierr; } /* find the hdrl LIST chunk within the RIFF */ - avierr = find_first_list(file, LISTTYPE_HDRL, &riff, &hdrl); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_list(LISTTYPE_HDRL, &riff, hdrl); + if (avierr != error::NONE) + return avierr; /* find the avih chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_AVIH, &hdrl, &avih); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_AVIH, &hdrl, avih); + if (avierr != error::NONE) + return avierr; /* parse the avih chunk */ - avierr = parse_avih_chunk(file, &avih); - if (avierr != AVIERR_NONE) - goto error; + avierr = parse_avih_chunk(avih); + if (avierr != error::NONE) + return avierr; /* loop over strl LIST chunks */ - strindex = 0; - for (avierr = find_first_list(file, LISTTYPE_STRL, &hdrl, &strl); avierr == AVIERR_NONE; avierr = find_next_list(file, LISTTYPE_STRL, &hdrl, &strl)) + int strindex = 0; + for (avierr = find_first_list(LISTTYPE_STRL, &hdrl, strl); avierr == error::NONE; avierr = find_next_list(LISTTYPE_STRL, &hdrl, strl)) { /* if we have too many, it's a bad file */ - if (strindex >= file->streams) - goto error; + if (strindex >= m_streams.size()) + return avierr; /* find the strh chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_STRH, &strl, &strh); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_STRH, &strl, strh); + if (avierr != error::NONE) + return avierr; /* parse the data */ - avierr = parse_strh_chunk(file, &file->stream[strindex], &strh); - if (avierr != AVIERR_NONE) - goto error; + avierr = parse_strh_chunk(m_streams[strindex], strh); + if (avierr != error::NONE) + return avierr; /* find the strf chunk */ - avierr = find_first_chunk(file, CHUNKTYPE_STRF, &strl, &strf); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_STRF, &strl, strf); + if (avierr != error::NONE) + return avierr; /* parse the data */ - avierr = parse_strf_chunk(file, &file->stream[strindex], &strf); - if (avierr != AVIERR_NONE) - goto error; + avierr = parse_strf_chunk(m_streams[strindex], strf); + if (avierr != error::NONE) + return avierr; /* find the indx chunk, if present */ - avierr = find_first_chunk(file, CHUNKTYPE_INDX, &strl, &indx); - if (avierr == AVIERR_NONE) - avierr = parse_indx_chunk(file, &file->stream[strindex], &indx); + avierr = find_first_chunk(CHUNKTYPE_INDX, &strl, indx); + if (avierr == error::NONE) + avierr = parse_indx_chunk(m_streams[strindex], indx); /* next stream */ strindex++; } /* normalize the error after parsing the stream headers */ - if (avierr == AVIERR_END) - avierr = AVIERR_NONE; - if (avierr != AVIERR_NONE) - goto error; + if (avierr == error::END) + avierr = error::NONE; + if (avierr != error::NONE) + return avierr; /* find the base of the movi data */ - avierr = find_first_list(file, LISTTYPE_MOVI, &riff, &movi); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_list(LISTTYPE_MOVI, &riff, movi); + if (avierr != error::NONE) + return avierr; /* find and parse the idx1 chunk within the RIFF (if present) */ - avierr = find_first_chunk(file, CHUNKTYPE_IDX1, &riff, &idx1); - if (avierr == AVIERR_NONE) - avierr = parse_idx1_chunk(file, movi.offset + 8, &idx1); - if (avierr != AVIERR_NONE) - goto error; + avierr = find_first_chunk(CHUNKTYPE_IDX1, &riff, idx1); + if (avierr == error::NONE) + avierr = parse_idx1_chunk(movi.offset + 8, idx1); + if (avierr != error::NONE) + return avierr; /* now extract the movie info */ - avierr = extract_movie_info(file); - -error: + avierr = extract_movie_info(); return avierr; } @@ -1947,59 +2316,55 @@ error: * @return The extracted movie information. */ -static avi_error extract_movie_info(avi_file *file) +avi_file::error avi_file_impl::extract_movie_info() { - //avi_stream *audiostream; avi_stream *stream; + int offset; /* get the video stream */ - stream = get_video_stream(file); + stream = get_video_stream(); if (stream != nullptr) { /* fill in the info */ - file->info.video_format = stream->format; - file->info.video_timescale = stream->rate; - file->info.video_sampletime = stream->scale; - file->info.video_numsamples = stream->samples; - file->info.video_width = stream->width; - file->info.video_height = stream->height; + m_info.video_format = stream->format(); + m_info.video_timescale = stream->rate(); + m_info.video_sampletime = stream->scale(); + m_info.video_numsamples = stream->samples(); + m_info.video_width = stream->width(); + m_info.video_height = stream->height(); } /* get the first audio stream */ - stream = get_audio_stream(file, 0, nullptr); + stream = get_audio_stream(0, offset); if (stream != nullptr) { /* fill in the info */ - file->info.audio_format = stream->format; - file->info.audio_timescale = stream->rate; - file->info.audio_sampletime = stream->scale; - file->info.audio_numsamples = stream->samples; - file->info.audio_channels = 1; - file->info.audio_samplebits = stream->samplebits; - file->info.audio_samplerate = stream->samplerate; + m_info.audio_format = stream->format(); + m_info.audio_timescale = stream->rate(); + m_info.audio_sampletime = stream->scale(); + m_info.audio_numsamples = stream->samples(); + m_info.audio_channels = 1; + m_info.audio_samplebits = stream->samplebits(); + m_info.audio_samplerate = stream->samplerate(); } /* now make sure all other audio streams are valid */ - //audiostream = stream; - while (1) + while (nullptr != (stream = get_audio_stream(m_info.audio_channels, offset))) { /* get the stream info */ - stream = get_audio_stream(file, file->info.audio_channels, nullptr); - if (stream == nullptr) - break; - file->info.audio_channels++; + m_info.audio_channels++; /* verify compatibility */ - if (file->info.audio_format != stream->format || - file->info.audio_timescale != stream->rate || - file->info.audio_sampletime != stream->scale || - file->info.audio_numsamples != stream->samples || - file->info.audio_samplebits != stream->samplebits || - file->info.audio_samplerate != stream->samplerate) - return AVIERR_INCOMPATIBLE_AUDIO_STREAMS; + if (m_info.audio_format != stream->format() || + m_info.audio_timescale != stream->rate() || + m_info.audio_sampletime != stream->scale() || + m_info.audio_numsamples != stream->samples() || + m_info.audio_samplebits != stream->samplebits() || + m_info.audio_samplerate != stream->samplerate()) + return error::INCOMPATIBLE_AUDIO_STREAMS; } - return AVIERR_NONE; + return error::NONE; } @@ -2019,28 +2384,21 @@ static avi_error extract_movie_info(avi_file *file) * @return An avi_error. */ -static avi_error parse_avih_chunk(avi_file *file, avi_chunk *avih) +avi_file::error avi_file_impl::parse_avih_chunk(avi_chunk const &avih) { - UINT8 *chunkdata = nullptr; - avi_error avierr; - /* read the data */ - avierr = read_chunk_data(file, avih, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + error avierr = read_chunk_data(avih, chunkdata); + if (avierr != error::NONE) + return avierr; /* extract the data */ - file->streams = fetch_32bits(&chunkdata[24]); + std::uint32_t const streams = fetch_32bits(&chunkdata[24]); /* allocate memory for the streams */ - file->stream = (avi_stream *)malloc(sizeof(*file->stream) * file->streams); - if (file->stream == nullptr) - goto error; - memset(file->stream, 0, sizeof(*file->stream) * file->streams); + m_streams.clear(); + m_streams.resize(streams); -error: - if (chunkdata != nullptr) - free(chunkdata); return avierr; } @@ -2062,26 +2420,16 @@ error: * @return An avi_error. */ -static avi_error parse_strh_chunk(avi_file *file, avi_stream *stream, avi_chunk *strh) +avi_file::error avi_file_impl::parse_strh_chunk(avi_stream &stream, avi_chunk const &strh) { - UINT8 *chunkdata = nullptr; - avi_error avierr; - /* read the data */ - avierr = read_chunk_data(file, strh, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + error const avierr = read_chunk_data(strh, chunkdata); + if (avierr != error::NONE) + return avierr; /* extract the data */ - stream->type = fetch_32bits(&chunkdata[0]); - stream->scale = fetch_32bits(&chunkdata[20]); - stream->rate = fetch_32bits(&chunkdata[24]); - stream->samples = fetch_32bits(&chunkdata[32]); - -error: - if (chunkdata != nullptr) - free(chunkdata); - return avierr; + return stream.set_strh_data(&chunkdata[0], strh.size); } @@ -2102,43 +2450,17 @@ error: * @return An avi_error. */ -static avi_error parse_strf_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf) +avi_file::error avi_file_impl::parse_strf_chunk(avi_stream &stream, avi_chunk const &strf) { - UINT8 *chunkdata = nullptr; - avi_error avierr; + error avierr; /* read the data */ - avierr = read_chunk_data(file, strf, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; - - /* audio and video streams have differing headers */ - if (stream->type == STREAMTYPE_VIDS) - { - stream->width = fetch_32bits(&chunkdata[4]); - stream->height = fetch_32bits(&chunkdata[8]); - stream->depth = fetch_16bits(&chunkdata[14]); - stream->format = fetch_32bits(&chunkdata[16]); - - /* extra extraction for HuffYUV data */ - if (stream->format == FORMAT_HFYU && strf->size >= 56) - { - avierr = huffyuv_extract_tables(stream, chunkdata, strf->size); - if (avierr != AVIERR_NONE) - goto error; - } - } - else if (stream->type == STREAMTYPE_AUDS) - { - stream->channels = fetch_16bits(&chunkdata[2]); - stream->samplebits = fetch_16bits(&chunkdata[14]); - stream->samplerate = fetch_32bits(&chunkdata[4]); - } + std::unique_ptr<std::uint8_t []> chunkdata; + avierr = read_chunk_data(strf, chunkdata); + if (avierr != error::NONE) + return avierr; -error: - if (chunkdata != nullptr) - free(chunkdata); - return avierr; + return stream.set_strf_data(&chunkdata[0], strf.size); } @@ -2158,50 +2480,46 @@ error: * @return An avi_error. */ -static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf) +avi_file::error avi_file_impl::parse_indx_chunk(avi_stream &stream, avi_chunk const &strf) { - UINT32 entries, entry; - UINT8 *chunkdata = nullptr; - UINT16 longs_per_entry; - UINT8 type; - UINT64 baseoffset; - avi_error avierr; + error avierr; /* read the data */ - avierr = read_chunk_data(file, strf, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + avierr = read_chunk_data(strf, chunkdata); + if (avierr != error::NONE) + return avierr; /* extract the data */ - longs_per_entry = fetch_16bits(&chunkdata[0]); + std::uint16_t const longs_per_entry = fetch_16bits(&chunkdata[0]); //subtype = chunkdata[2]; - type = chunkdata[3]; - entries = fetch_32bits(&chunkdata[4]); + std::uint8_t const type = chunkdata[3]; + std::uint32_t const entries = fetch_32bits(&chunkdata[4]); //id = fetch_32bits(&chunkdata[8]); - baseoffset = fetch_64bits(&chunkdata[12]); + std::uint64_t const baseoffset = fetch_64bits(&chunkdata[12]); /* if this is a superindex, loop over entries and call ourselves recursively */ if (type == AVI_INDEX_OF_INDEXES) { /* validate the size of each entry */ if (longs_per_entry != 4) - return AVIERR_INVALID_DATA; + return error::INVALID_DATA; /* loop over entries and create subchunks for each */ - for (entry = 0; entry < entries; entry++) + for (std::uint32_t entry = 0; entry < entries; entry++) { - const UINT8 *base = &chunkdata[24 + entry * 16]; - file_error filerr; + const std::uint8_t *base = &chunkdata[24 + entry * 16]; + osd_file::error filerr; avi_chunk subchunk; - UINT32 bytes_read; - UINT8 buffer[8]; + std::uint32_t bytes_read; + std::uint8_t buffer[8]; /* go read the subchunk */ subchunk.offset = fetch_64bits(&base[0]); - filerr = osd_read(file->file, buffer, subchunk.offset, sizeof(buffer), &bytes_read); - if (filerr != FILERR_NONE || bytes_read != sizeof(buffer)) + filerr = m_file->read(buffer, subchunk.offset, sizeof(buffer), bytes_read); + if (filerr != osd_file::error::NONE || bytes_read != sizeof(buffer)) { - avierr = AVIERR_READ_ERROR; + avierr = error::READ_ERROR; break; } @@ -2210,8 +2528,8 @@ static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk subchunk.size = fetch_32bits(&buffer[4]); /* recursively parse each referenced chunk; stop if we hit an error */ - avierr = parse_indx_chunk(file, stream, &subchunk); - if (avierr != AVIERR_NONE) + avierr = parse_indx_chunk(stream, subchunk); + if (avierr != error::NONE) break; } } @@ -2221,25 +2539,22 @@ static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk { /* validate the size of each entry */ if (longs_per_entry != 2 && longs_per_entry != 3) - return AVIERR_INVALID_DATA; + return error::INVALID_DATA; /* loop over entries and parse out the data */ - for (entry = 0; entry < entries; entry++) + for (std::uint32_t entry = 0; entry < entries; entry++) { - const UINT8 *base = &chunkdata[24 + entry * 4 * longs_per_entry]; - UINT32 offset = fetch_32bits(&base[0]); - UINT32 size = fetch_32bits(&base[4]) & 0x7fffffff; // bit 31 == NOT a keyframe + const std::uint8_t *base = &chunkdata[24 + entry * 4 * longs_per_entry]; + std::uint32_t offset = fetch_32bits(&base[0]); + std::uint32_t size = fetch_32bits(&base[4]) & 0x7fffffff; // bit 31 == NOT a keyframe /* set the info for this chunk and advance */ - avierr = set_stream_chunk_info(stream, stream->chunks++, baseoffset + offset - 8, size + 8); - if (avierr != AVIERR_NONE) + avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset - 8, size + 8); + if (avierr != error::NONE) break; } } -error: - if (chunkdata != nullptr) - free(chunkdata); return avierr; } @@ -2249,7 +2564,7 @@ error: -------------------------------------------------*/ /** - * @fn static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1) + * @fn static avi_error parse_idx1_chunk(avi_file *file, std::uint64_t baseoffset, avi_chunk *idx1) * * @brief Parse index 1 chunk. * @@ -2260,48 +2575,39 @@ error: * @return An avi_error. */ -static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1) +avi_file::error avi_file_impl::parse_idx1_chunk(std::uint64_t baseoffset, avi_chunk const &idx1) { - UINT8 *chunkdata = nullptr; - avi_error avierr; - UINT32 entries; - UINT32 entry; + error avierr; /* read the data */ - avierr = read_chunk_data(file, idx1, &chunkdata); - if (avierr != AVIERR_NONE) - goto error; + std::unique_ptr<std::uint8_t []> chunkdata; + avierr = read_chunk_data(idx1, chunkdata); + if (avierr != error::NONE) + return avierr; /* loop over entries */ - entries = idx1->size / 16; - for (entry = 0; entry < entries; entry++) + std::uint32_t const entries = idx1.size / 16; + for (std::uint32_t entry = 0; entry < entries; entry++) { - const UINT8 *base = &chunkdata[entry * 16]; - UINT32 chunkid = fetch_32bits(&base[0]); - UINT32 offset = fetch_32bits(&base[8]); - UINT32 size = fetch_32bits(&base[12]); - avi_stream *stream; + std::uint8_t const *const base = &chunkdata[entry * 16]; + std::uint32_t const chunkid = fetch_32bits(&base[0]); + std::uint32_t const offset = fetch_32bits(&base[8]); + std::uint32_t const size = fetch_32bits(&base[12]); int streamnum; /* determine the stream index */ streamnum = ((chunkid >> 8) & 0xff) - '0'; streamnum += 10 * ((chunkid & 0xff) - '0'); - if (streamnum >= file->streams) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - stream = &file->stream[streamnum]; + if (streamnum >= m_streams.size()) + return error::INVALID_DATA; + avi_stream &stream = m_streams[streamnum]; /* set the appropriate entry */ - avierr = set_stream_chunk_info(stream, stream->chunks++, baseoffset + offset, size + 8); - if (avierr != AVIERR_NONE) - goto error; + avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset, size + 8); + if (avierr != error::NONE) + return avierr; } -error: - if (chunkdata != nullptr) - free(chunkdata); return avierr; } @@ -2311,7 +2617,7 @@ error: -------------------------------------------------*/ /** - * @fn static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength) + * @fn static avi_error chunk_open(avi_file *file, std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength) * * @brief Queries if a given chunk open. * @@ -2323,57 +2629,55 @@ error: * @return An avi_error. */ -static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength) +avi_file::error avi_file_impl::chunk_open(std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength) { - file_error filerr; - avi_chunk *chunk; - UINT32 written; - /* if we're out of stack entries, bail */ - if (file->chunksp >= ARRAY_LENGTH(file->chunkstack)) - return AVIERR_STACK_TOO_DEEP; - chunk = &file->chunkstack[file->chunksp++]; + if (m_chunksp >= m_chunkstack.size()) + return error::STACK_TOO_DEEP; + avi_chunk &chunk = m_chunkstack[m_chunksp++]; /* set up the chunk information */ - chunk->offset = file->writeoffs; - chunk->size = estlength; - chunk->type = type; - chunk->listtype = listtype; + chunk.offset = m_writeoffs; + chunk.size = estlength; + chunk.type = type; + chunk.listtype = listtype; /* non-list types */ if (type != CHUNKTYPE_RIFF && type != CHUNKTYPE_LIST) { - UINT8 buffer[8]; + std::uint8_t buffer[8]; /* populate the header */ - put_32bits(&buffer[0], chunk->type); - put_32bits(&buffer[4], chunk->size); + put_32bits(&buffer[0], chunk.type); + put_32bits(&buffer[4], chunk.size); /* write the header */ - filerr = osd_write(file->file, buffer, file->writeoffs, sizeof(buffer), &written); - if (filerr != FILERR_NONE || written != sizeof(buffer)) - return AVIERR_WRITE_ERROR; - file->writeoffs += written; + std::uint32_t written; + osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); + if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + return error::WRITE_ERROR; + m_writeoffs += written; } /* list types */ else { - UINT8 buffer[12]; + std::uint8_t buffer[12]; /* populate the header */ - put_32bits(&buffer[0], chunk->type); - put_32bits(&buffer[4], chunk->size); - put_32bits(&buffer[8], chunk->listtype); + put_32bits(&buffer[0], chunk.type); + put_32bits(&buffer[4], chunk.size); + put_32bits(&buffer[8], chunk.listtype); /* write the header */ - filerr = osd_write(file->file, buffer, file->writeoffs, sizeof(buffer), &written); - if (filerr != FILERR_NONE || written != sizeof(buffer)) - return AVIERR_WRITE_ERROR; - file->writeoffs += written; + std::uint32_t written; + osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); + if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + return error::WRITE_ERROR; + m_writeoffs += written; } - return AVIERR_NONE; + return error::NONE; } @@ -2391,32 +2695,31 @@ static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 * @return An avi_error. */ -static avi_error chunk_close(avi_file *file) +avi_file::error avi_file_impl::chunk_close() { - avi_chunk *chunk = &file->chunkstack[--file->chunksp]; - UINT64 chunksize = file->writeoffs - (chunk->offset + 8); - UINT32 written; + avi_chunk const &chunk = m_chunkstack[--m_chunksp]; + std::uint64_t const chunksize = m_writeoffs - (chunk.offset + 8); /* error if we don't fit into 32 bits */ - if (chunksize != (UINT32)chunksize) - return AVIERR_INVALID_DATA; + if (chunksize != std::uint32_t(chunksize)) + return error::INVALID_DATA; /* write the final size if it is different from the guess */ - if (chunk->size != chunksize) + if (chunk.size != chunksize) { - file_error filerr; - UINT8 buffer[4]; + std::uint8_t buffer[4]; - put_32bits(&buffer[0], (UINT32)chunksize); - filerr = osd_write(file->file, buffer, chunk->offset + 4, sizeof(buffer), &written); - if (filerr != FILERR_NONE || written != sizeof(buffer)) - return AVIERR_WRITE_ERROR; + put_32bits(&buffer[0], std::uint32_t(chunksize)); + std::uint32_t written; + osd_file::error const filerr = m_file->write(buffer, chunk.offset + 4, sizeof(buffer), written); + if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + return error::WRITE_ERROR; } /* round up to the next word */ - file->writeoffs += chunksize & 1; + m_writeoffs += chunksize & 1; - return AVIERR_NONE; + return error::NONE; } @@ -2425,7 +2728,7 @@ static avi_error chunk_close(avi_file *file) -------------------------------------------------*/ /** - * @fn static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length) + * @fn static avi_error chunk_write(avi_file *file, std::uint32_t type, const void *data, std::uint32_t length) * * @brief Chunk write. * @@ -2437,67 +2740,63 @@ static avi_error chunk_close(avi_file *file) * @return An avi_error. */ -static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length) +avi_file::error avi_file_impl::chunk_write(std::uint32_t type, const void *data, std::uint32_t length) { - file_error filerr; - avi_error avierr; - UINT32 idxreserve; - UINT32 written; + error avierr; /* if we are the first RIFF, we must reserve enough space for the IDX chunk */ - idxreserve = 0; - if (file->riffbase == 0 && type != CHUNKTYPE_IDX1) - idxreserve = compute_idx1_size(file); + std::uint32_t const idxreserve = (m_riffbase == 0 && type != CHUNKTYPE_IDX1) ? compute_idx1_size() : 0; /* if we are getting too big, split the RIFF */ /* note that we ignore writes before the current RIFF base, as those are assumed to be overwrites of a chunk from the previous RIFF */ - if (file->writeoffs >= file->riffbase && file->writeoffs + length + idxreserve - file->riffbase >= MAX_RIFF_SIZE) + if ((m_writeoffs >= m_riffbase) && ((m_writeoffs + length + idxreserve - m_riffbase) >= MAX_RIFF_SIZE)) { /* close the movi list */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; /* write the idx1 chunk if this is the first */ - if (file->riffbase == 0) + if (m_riffbase == 0) { - avierr = write_idx1_chunk(file); - if (avierr != AVIERR_NONE) + avierr = write_idx1_chunk(); + if (avierr != error::NONE) return avierr; } /* close the RIFF */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; /* open a new RIFF */ - file->riffbase = file->writeoffs; - avierr = chunk_open(file, CHUNKTYPE_RIFF, LISTTYPE_AVIX, 0); - if (avierr != AVIERR_NONE) + m_riffbase = m_writeoffs; + avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVIX, 0); + if (avierr != error::NONE) return avierr; /* open a nested movi list */ - file->saved_movi_offset = file->writeoffs; - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); - if (avierr != AVIERR_NONE) + m_saved_movi_offset = m_writeoffs; + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); + if (avierr != error::NONE) return avierr; } /* open the chunk */ - avierr = chunk_open(file, type, 0, length); - if (avierr != AVIERR_NONE) + avierr = chunk_open(type, 0, length); + if (avierr != error::NONE) return avierr; /* write the data */ - filerr = osd_write(file->file, data, file->writeoffs, length, &written); - if (filerr != FILERR_NONE || written != length) - return AVIERR_WRITE_ERROR; - file->writeoffs += written; + std::uint32_t written; + osd_file::error const filerr = m_file->write(data, m_writeoffs, length, written); + if (filerr != osd_file::error::NONE || written != length) + return error::WRITE_ERROR; + m_writeoffs += written; /* close the chunk */ - return chunk_close(file); + return chunk_close(); } @@ -2509,7 +2808,7 @@ static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT -------------------------------------------------*/ /** - * @fn static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write) + * @fn static avi_error chunk_overwrite(avi_file *file, std::uint32_t type, const void *data, std::uint32_t length, std::uint64_t *offset, int initial_write) * * @brief Chunk overwrite. * @@ -2523,28 +2822,27 @@ static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT * @return An avi_error. */ -static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write) +avi_file::error avi_file_impl::chunk_overwrite(std::uint32_t type, const void *data, std::uint32_t length, std::uint64_t &offset, bool initial_write) { - UINT64 savedoffset = 0; - avi_error avierr; + std::uint64_t savedoffset = 0; /* if this is our initial write, save the offset */ if (initial_write) - *offset = file->writeoffs; + offset = m_writeoffs; /* otherwise, remember the current write offset and replace it with the original */ else { - savedoffset = file->writeoffs; - file->writeoffs = *offset; + savedoffset = m_writeoffs; + m_writeoffs = offset; } /* write the chunk */ - avierr = chunk_write(file, type, data, length); + error const avierr = chunk_write(type, data, length); /* if this isn't the initial write, restore the previous offset */ if (!initial_write) - file->writeoffs = savedoffset; + m_writeoffs = savedoffset; return avierr; } @@ -2565,67 +2863,66 @@ static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, * @return An avi_error. */ -static avi_error write_initial_headers(avi_file *file) +avi_file::error avi_file_impl::write_initial_headers() { - avi_error avierr; - int strnum; + error avierr; /* reset the write pointer */ - file->writeoffs = 0; + m_writeoffs = 0; /* open a RIFF chunk */ - avierr = chunk_open(file, CHUNKTYPE_RIFF, LISTTYPE_AVI, 0); - if (avierr != AVIERR_NONE) + avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVI, 0); + if (avierr != error::NONE) return avierr; /* open a hdlr LIST */ - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_HDRL, 0); - if (avierr != AVIERR_NONE) + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_HDRL, 0); + if (avierr != error::NONE) return avierr; /* write an avih chunk */ - avierr = write_avih_chunk(file, TRUE); - if (avierr != AVIERR_NONE) + avierr = write_avih_chunk(true); + if (avierr != error::NONE) return avierr; /* for each stream, write a strl LIST */ - for (strnum = 0; strnum < file->streams; strnum++) + for (int strnum = 0; strnum < m_streams.size(); strnum++) { /* open a strl LIST */ - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_STRL, 0); - if (avierr != AVIERR_NONE) + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_STRL, 0); + if (avierr != error::NONE) return avierr; /* write the strh chunk */ - avierr = write_strh_chunk(file, &file->stream[strnum], TRUE); - if (avierr != AVIERR_NONE) + avierr = write_strh_chunk(m_streams[strnum], true); + if (avierr != error::NONE) return avierr; /* write the strf chunk */ - avierr = write_strf_chunk(file, &file->stream[strnum]); - if (avierr != AVIERR_NONE) + avierr = write_strf_chunk(m_streams[strnum]); + if (avierr != error::NONE) return avierr; /* write the indx chunk */ - avierr = write_indx_chunk(file, &file->stream[strnum], TRUE); - if (avierr != AVIERR_NONE) + avierr = write_indx_chunk(m_streams[strnum], true); + if (avierr != error::NONE) return avierr; /* close the strl LIST */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; } /* close the hdlr LIST */ - avierr = chunk_close(file); - if (avierr != AVIERR_NONE) + avierr = chunk_close(); + if (avierr != error::NONE) return avierr; /* open a movi LIST */ - file->saved_movi_offset = file->writeoffs; - avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); - if (avierr != AVIERR_NONE) + m_saved_movi_offset = m_writeoffs; + avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); + if (avierr != error::NONE) return avierr; return avierr; @@ -2648,23 +2945,23 @@ static avi_error write_initial_headers(avi_file *file) * @return An avi_error. */ -static avi_error write_avih_chunk(avi_file *file, int initial_write) +avi_file::error avi_file_impl::write_avih_chunk(bool initial_write) { - avi_stream *video = get_video_stream(file); - UINT8 buffer[56]; + avi_stream *video = get_video_stream(); + std::uint8_t buffer[56]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); - put_32bits(&buffer[0], 1000000 * (INT64)video->scale / video->rate); /* dwMicroSecPerFrame */ + put_32bits(&buffer[0], 1000000 * std::int64_t(video->scale()) / video->rate()); /* dwMicroSecPerFrame */ put_32bits(&buffer[12], AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* dwFlags */ - put_32bits(&buffer[16], video->samples); /* dwTotalFrames */ - put_32bits(&buffer[24], file->streams); /* dwStreams */ - put_32bits(&buffer[32], video->width); /* dwWidth */ - put_32bits(&buffer[36], video->height); /* dwHeight */ + put_32bits(&buffer[16], video->samples()); /* dwTotalFrames */ + put_32bits(&buffer[24], m_streams.size()); /* dwStreams */ + put_32bits(&buffer[32], video->width()); /* dwWidth */ + put_32bits(&buffer[36], video->height()); /* dwHeight */ /* (over)write the chunk */ - return chunk_overwrite(file, CHUNKTYPE_AVIH, buffer, sizeof(buffer), &file->saved_avih_offset, initial_write); + return chunk_overwrite(CHUNKTYPE_AVIH, buffer, sizeof(buffer), m_saved_avih_offset, initial_write); } @@ -2685,41 +2982,41 @@ static avi_error write_avih_chunk(avi_file *file, int initial_write) * @return An avi_error. */ -static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initial_write) +avi_file::error avi_file_impl::write_strh_chunk(avi_stream &stream, bool initial_write) { - UINT8 buffer[56]; + std::uint8_t buffer[56]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); - put_32bits(&buffer[0], stream->type); /* fccType */ - put_32bits(&buffer[20], stream->scale); /* dwScale */ - put_32bits(&buffer[24], stream->rate); /* dwRate */ - put_32bits(&buffer[32], stream->samples); /* dwLength */ + put_32bits(&buffer[0], stream.type()); /* fccType */ + put_32bits(&buffer[20], stream.scale()); /* dwScale */ + put_32bits(&buffer[24], stream.rate()); /* dwRate */ + put_32bits(&buffer[32], stream.samples()); /* dwLength */ put_32bits(&buffer[40], 10000); /* dwQuality */ /* video-stream specific data */ - if (stream->type == STREAMTYPE_VIDS) + if (stream.type() == STREAMTYPE_VIDS) { put_32bits(&buffer[4], /* fccHandler */ - (stream->format == FORMAT_HFYU) ? HANDLER_HFYU : HANDLER_DIB); + (stream.format() == FORMAT_HFYU) ? HANDLER_HFYU : HANDLER_DIB); put_32bits(&buffer[36], /* dwSuggestedBufferSize */ - stream->width * stream->height * 4); - put_16bits(&buffer[52], stream->width); /* rcFrame.right */ - put_16bits(&buffer[54], stream->height); /* rcFrame.bottom */ + stream.width() * stream.height() * 4); + put_16bits(&buffer[52], stream.width()); /* rcFrame.right */ + put_16bits(&buffer[54], stream.height()); /* rcFrame.bottom */ } /* audio-stream specific data */ - if (stream->type == STREAMTYPE_AUDS) + if (stream.type() == STREAMTYPE_AUDS) { put_32bits(&buffer[36], /* dwSuggestedBufferSize */ - stream->samplerate * stream->channels * (stream->samplebits / 8)); + stream.samplerate() * stream.bytes_per_sample()); put_32bits(&buffer[44], /* dwSampleSize */ - stream->channels * (stream->samplebits / 8)); + stream.bytes_per_sample()); } /* write the chunk */ - return chunk_overwrite(file, CHUNKTYPE_STRH, buffer, sizeof(buffer), &stream->saved_strh_offset, initial_write); + return chunk_overwrite(CHUNKTYPE_STRH, buffer, sizeof(buffer), stream.saved_strh_offset(), initial_write); } @@ -2739,51 +3036,51 @@ static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initia * @return An avi_error. */ -static avi_error write_strf_chunk(avi_file *file, avi_stream *stream) +avi_file::error avi_file_impl::write_strf_chunk(avi_stream const &stream) { /* video stream */ - if (stream->type == STREAMTYPE_VIDS) + if (stream.type() == STREAMTYPE_VIDS) { - UINT8 buffer[40]; + std::uint8_t buffer[40]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); put_32bits(&buffer[0], sizeof(buffer)); /* biSize */ - put_32bits(&buffer[4], stream->width); /* biWidth */ - put_32bits(&buffer[8], stream->height); /* biHeight */ + put_32bits(&buffer[4], stream.width()); /* biWidth */ + put_32bits(&buffer[8], stream.height()); /* biHeight */ put_16bits(&buffer[12], 1); /* biPlanes */ - put_16bits(&buffer[14], stream->depth); /* biBitCount */ - put_32bits(&buffer[16], stream->format); /* biCompression */ + put_16bits(&buffer[14], stream.depth()); /* biBitCount */ + put_32bits(&buffer[16], stream.format()); /* biCompression */ put_32bits(&buffer[20], /* biSizeImage */ - stream->width * stream->height * (stream->depth + 7) / 8); + stream.width() * stream.height() * (stream.depth() + 7) / 8); /* write the chunk */ - return chunk_write(file, CHUNKTYPE_STRF, buffer, sizeof(buffer)); + return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer)); } /* audio stream */ - if (stream->type == STREAMTYPE_AUDS) + if (stream.type() == STREAMTYPE_AUDS) { - UINT8 buffer[16]; + std::uint8_t buffer[16]; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); put_16bits(&buffer[0], 1); /* wFormatTag */ - put_16bits(&buffer[2], stream->channels); /* nChannels */ - put_32bits(&buffer[4], stream->samplerate); /* nSamplesPerSec */ + put_16bits(&buffer[2], stream.channels()); /* nChannels */ + put_32bits(&buffer[4], stream.samplerate()); /* nSamplesPerSec */ put_32bits(&buffer[8], /* nAvgBytesPerSec */ - stream->samplerate * stream->channels * (stream->samplebits / 8)); + stream.samplerate() * stream.bytes_per_sample()); put_16bits(&buffer[12], /* nBlockAlign */ - stream->channels * (stream->samplebits / 8)); - put_16bits(&buffer[14], stream->samplebits); /* wBitsPerSample */ + stream.bytes_per_sample()); + put_16bits(&buffer[14], stream.samplebits()); /* wBitsPerSample */ /* write the chunk */ - return chunk_write(file, CHUNKTYPE_STRF, buffer, sizeof(buffer)); + return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer)); } - return AVIERR_INVALID_DATA; + return error::INVALID_DATA; } @@ -2804,35 +3101,30 @@ static avi_error write_strf_chunk(avi_file *file, avi_stream *stream) * @return An avi_error. */ -static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initial_write) +avi_file::error avi_file_impl::write_indx_chunk(avi_stream &stream, bool initial_write) { - UINT8 buffer[24 + 16 * MAX_AVI_SIZE_IN_GB / 4]; - UINT32 chunkid, indexchunkid; - UINT32 master_entries = 0; + std::uint8_t buffer[24 + 16 * MAX_AVI_SIZE_IN_GB / 4]; + std::uint32_t master_entries = 0; /* reset the buffer */ - memset(buffer, 0, sizeof(buffer)); + std::memset(buffer, 0, sizeof(buffer)); /* construct the chunk ID and index chunk ID */ - chunkid = get_chunkid_for_stream(file, stream); - indexchunkid = AVI_FOURCC('i', 'x', '0' + (stream - file->stream) / 10, '0' + (stream - file->stream) % 10); + std::uint32_t const chunkid = get_chunkid_for_stream(&stream); + std::uint32_t const indexchunkid = AVI_FOURCC('i', 'x', '0' + (&stream - &m_streams[0]) / 10, '0' + (&stream - &m_streams[0]) % 10); /* loop over chunks of 4GB and write out indexes for them first */ - if (!initial_write && file->riffbase != 0) + if (!initial_write && m_riffbase != 0) { - UINT64 currentbase; - for (currentbase = 0; currentbase < file->writeoffs; currentbase += FOUR_GB) + for (std::uint64_t currentbase = 0; currentbase < m_writeoffs; currentbase += FOUR_GB) { - UINT64 currentend = currentbase + FOUR_GB; - UINT32 chunks_this_index = 0; - UINT32 bytes_this_index = 0; - avi_error avierr; - UINT32 chunknum; - UINT8 *tempbuf; + std::uint64_t const currentend = currentbase + FOUR_GB; + std::uint32_t chunks_this_index = 0; + std::uint32_t bytes_this_index = 0; /* count chunks that are in this region */ - for (chunknum = 0; chunknum < stream->chunks; chunknum++) - if (stream->chunk[chunknum].offset >= currentbase && stream->chunk[chunknum].offset < currentend) + for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++) + if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend) chunks_this_index++; /* if no chunks, skip */ @@ -2840,10 +3132,10 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia continue; /* allocate memory */ - tempbuf = (UINT8 *)malloc(24 + 8 * chunks_this_index); - if (tempbuf == nullptr) - return AVIERR_NO_MEMORY; - memset(tempbuf, 0, 24 + 8 * chunks_this_index); + std::unique_ptr<std::uint8_t []> tempbuf; + try { tempbuf.reset(new std::uint8_t[24 + 8 * chunks_this_index]); } + catch (...) { return error::NO_MEMORY; } + std::memset(&tempbuf[0], 0, 24 + 8 * chunks_this_index); /* make a regular index */ put_16bits(&tempbuf[0], 2); /* wLongsPerEntry */ @@ -2855,28 +3147,27 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia /* now fill in the indexes */ chunks_this_index = 0; - for (chunknum = 0; chunknum < stream->chunks; chunknum++) - if (stream->chunk[chunknum].offset >= currentbase && stream->chunk[chunknum].offset < currentend) + for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++) + if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend) { - put_32bits(&tempbuf[24 + 8 * chunks_this_index + 0], stream->chunk[chunknum].offset + 8 - currentbase); - put_32bits(&tempbuf[24 + 8 * chunks_this_index + 4], stream->chunk[chunknum].length - 8); - bytes_this_index += stream->chunk[chunknum].length; + put_32bits(&tempbuf[24 + 8 * chunks_this_index + 0], stream.chunk(chunknum).offset + 8 - currentbase); + put_32bits(&tempbuf[24 + 8 * chunks_this_index + 4], stream.chunk(chunknum).length - 8); + bytes_this_index += stream.chunk(chunknum).length; chunks_this_index++; } /* write the offset of this index to the master table */ - put_64bits(&buffer[24 + 16 * master_entries + 0], file->writeoffs); + put_64bits(&buffer[24 + 16 * master_entries + 0], m_writeoffs); put_32bits(&buffer[24 + 16 * master_entries + 8], 24 + 8 * chunks_this_index + 8); - if (stream->type == STREAMTYPE_VIDS) + if (stream.type() == STREAMTYPE_VIDS) put_32bits(&buffer[24 + 16 * master_entries + 12], chunks_this_index); - else if (stream->type == STREAMTYPE_AUDS) - put_32bits(&buffer[24 + 16 * master_entries + 12], bytes_this_index / ((stream->samplebits / 8) * stream->channels)); + else if (stream.type() == STREAMTYPE_AUDS) + put_32bits(&buffer[24 + 16 * master_entries + 12], bytes_this_index / stream.bytes_per_sample()); master_entries++; /* write the index */ - avierr = chunk_write(file, indexchunkid, tempbuf, 24 + 8 * chunks_this_index); - free(tempbuf); - if (avierr != AVIERR_NONE) + error const avierr = chunk_write(indexchunkid, &tempbuf[0], 24 + 8 * chunks_this_index); + if (avierr != error::NONE) return avierr; } } @@ -2892,7 +3183,7 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia } /* (over)write the chunk */ - return chunk_overwrite(file, (master_entries == 0) ? CHUNKTYPE_JUNK : CHUNKTYPE_INDX, buffer, sizeof(buffer), &stream->saved_indx_offset, initial_write); + return chunk_overwrite((master_entries == 0) ? CHUNKTYPE_JUNK : CHUNKTYPE_INDX, buffer, sizeof(buffer), stream.saved_indx_offset(), initial_write); } @@ -2910,47 +3201,42 @@ static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initia * @return An avi_error. */ -static avi_error write_idx1_chunk(avi_file *file) +avi_file::error avi_file_impl::write_idx1_chunk() { - UINT32 tempbuflength = compute_idx1_size(file) - 8; - UINT32 curchunk[2] = { 0 }; - UINT32 curoffset; - avi_error avierr; - UINT8 *tempbuf; + std::uint32_t const tempbuflength = compute_idx1_size() - 8; + std::uint32_t curchunk[2] = { 0 }; /* allocate a temporary buffer */ - tempbuf = (UINT8 *)malloc(tempbuflength); - if (tempbuf == nullptr) - return AVIERR_NO_MEMORY; + std::unique_ptr<std::uint8_t []> tempbuf; + try { tempbuf.reset(new std::uint8_t[tempbuflength]); } + catch (...) { return error::NO_MEMORY; } /* fill it in */ - for (curoffset = 0; curoffset < tempbuflength; curoffset += 16) + for (std::uint32_t curoffset = 0; curoffset < tempbuflength; curoffset += 16) { - UINT64 minoffset = ~(UINT64)0; - int strnum, minstr = 0; + std::uint64_t minoffset = ~std::uint64_t(0); + int minstr = 0; /* determine which stream has the next chunk */ - for (strnum = 0; strnum < file->streams; strnum++) - if (curchunk[strnum] < file->stream[strnum].chunks && file->stream[strnum].chunk[curchunk[strnum]].offset < minoffset) + for (int strnum = 0; strnum < m_streams.size(); strnum++) + if (curchunk[strnum] < m_streams[strnum].chunks() && m_streams[strnum].chunk(curchunk[strnum]).offset < minoffset) { - minoffset = file->stream[strnum].chunk[curchunk[strnum]].offset; + minoffset = m_streams[strnum].chunk(curchunk[strnum]).offset; minstr = strnum; } /* make an entry for this index */ - put_32bits(&tempbuf[curoffset + 0], get_chunkid_for_stream(file, &file->stream[minstr])); + put_32bits(&tempbuf[curoffset + 0], get_chunkid_for_stream(&m_streams[minstr])); put_32bits(&tempbuf[curoffset + 4], 0x0010 /* AVIIF_KEYFRAME */); - put_32bits(&tempbuf[curoffset + 8], minoffset - (file->saved_movi_offset + 8)); - put_32bits(&tempbuf[curoffset + 12], file->stream[minstr].chunk[curchunk[minstr]].length - 8); + put_32bits(&tempbuf[curoffset + 8], minoffset - (m_saved_movi_offset + 8)); + put_32bits(&tempbuf[curoffset + 12], m_streams[minstr].chunk(curchunk[minstr]).length - 8); /* advance the chunk counter for this stream */ curchunk[minstr]++; } /* write the chunk */ - avierr = chunk_write(file, CHUNKTYPE_IDX1, tempbuf, tempbuflength); - free(tempbuf); - return avierr; + return chunk_write(CHUNKTYPE_IDX1, &tempbuf[0], tempbuflength); } @@ -2969,31 +3255,31 @@ static avi_error write_idx1_chunk(avi_file *file) * @return An avi_error. */ -static avi_error soundbuf_initialize(avi_file *file) +avi_file::error avi_file_impl::soundbuf_initialize() { - avi_stream *audio = get_audio_stream(file, 0, nullptr); - avi_stream *video = get_video_stream(file); + int offset; + avi_stream *const audio = get_audio_stream(0, offset); + avi_stream *const video = get_video_stream(); /* we require a video stream */ if (video == nullptr) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + return error::UNSUPPORTED_VIDEO_FORMAT; /* skip if no audio stream */ if (audio == nullptr) - return AVIERR_NONE; + return error::NONE; /* determine the number of samples we want in our buffer; 2 seconds should be enough */ - file->soundbuf_samples = file->info.audio_samplerate * SOUND_BUFFER_MSEC / 1000; + m_soundbuf_samples = m_info.audio_samplerate * SOUND_BUFFER_MSEC / 1000; /* allocate a buffer */ - file->soundbuf = (INT16 *)malloc(file->soundbuf_samples * file->info.audio_channels * sizeof(file->soundbuf[0])); - if (file->soundbuf == nullptr) - return AVIERR_NO_MEMORY; - memset(file->soundbuf, 0, file->soundbuf_samples * file->info.audio_channels * sizeof(file->soundbuf[0])); + m_soundbuf.clear(); + try { m_soundbuf.resize(m_soundbuf_samples * m_info.audio_channels, 0); } + catch (...) { return error::NO_MEMORY; } /* determine the number of frames to be ahead (0.75secs) */ - file->soundbuf_frames = ((UINT64)video->rate * 75) / ((UINT64)video->scale * 100) + 1; - return AVIERR_NONE; + m_soundbuf_frames = (std::uint64_t(video->rate()) * 75) / (std::uint64_t(video->scale()) * 100) + 1; + return error::NONE; } @@ -3003,7 +3289,7 @@ static avi_error soundbuf_initialize(avi_file *file) -------------------------------------------------*/ /** - * @fn static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum) + * @fn static avi_error soundbuf_write_chunk(avi_file *file, std::uint32_t framenum) * * @brief Soundbuf write chunk. * @@ -3013,30 +3299,30 @@ static avi_error soundbuf_initialize(avi_file *file) * @return An avi_error. */ -static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum) +avi_file::error avi_file_impl::soundbuf_write_chunk(std::uint32_t framenum) { - avi_stream *stream = get_audio_stream(file, 0, nullptr); - avi_error avierr; - UINT32 length; + int offset; + avi_stream *const stream = get_audio_stream(0, offset); + std::uint32_t length; /* skip if no audio stream */ if (stream == nullptr) - return AVIERR_NONE; + return error::NONE; /* determine the length of this chunk */ if (framenum == 0) - length = framenum_to_samplenum(file, file->soundbuf_frames); + length = framenum_to_samplenum(m_soundbuf_frames); else - length = framenum_to_samplenum(file, framenum + 1 + file->soundbuf_frames) - framenum_to_samplenum(file, framenum + file->soundbuf_frames); - length *= stream->channels * sizeof(INT16); + length = framenum_to_samplenum(framenum + 1 + m_soundbuf_frames) - framenum_to_samplenum(framenum + m_soundbuf_frames); + length *= stream->channels() * sizeof(std::int16_t); /* then do the initial write */ - avierr = chunk_write(file, get_chunkid_for_stream(file, stream), file->soundbuf, length); - if (avierr != AVIERR_NONE) + error const avierr = chunk_write(get_chunkid_for_stream(stream), &m_soundbuf[0], length); + if (avierr != error::NONE) return avierr; /* set the info for this new chunk */ - return set_stream_chunk_info(stream, stream->chunks, file->writeoffs - length - 8, length + 8); + return stream->set_chunk_info(stream->chunks(), m_writeoffs - length - 8, length + 8); } @@ -3056,36 +3342,35 @@ static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum) * @return An avi_error. */ -static avi_error soundbuf_flush(avi_file *file, int only_flush_full) +avi_file::error avi_file_impl::soundbuf_flush(bool only_flush_full) { - avi_stream *stream = get_audio_stream(file, 0, nullptr); - INT32 channelsamples = file->soundbuf_samples; - INT32 processedsamples = 0; - UINT32 bytes_per_sample; - UINT32 finalchunks; - avi_error avierr; - UINT32 chunknum; - UINT32 chunkid; - int channel; + int offset; + avi_stream *const stream = get_audio_stream(0, offset); + std::int32_t channelsamples = m_soundbuf_samples; + std::int32_t processedsamples = 0; + std::uint32_t finalchunks; + error avierr; + std::uint32_t chunknum; + std::uint32_t chunkid; /* skip if no stream */ if (stream == nullptr) - return AVIERR_NONE; + return error::NONE; /* get the chunk ID for this stream */ - chunkid = get_chunkid_for_stream(file, stream); - bytes_per_sample = stream->channels * sizeof(INT16); - finalchunks = stream->chunks; + chunkid = get_chunkid_for_stream(stream); + std::uint32_t const bytes_per_sample = stream->channels() * sizeof(std::int16_t); + finalchunks = stream->chunks(); /* find out how many samples we've accumulated */ - for (channel = 0; channel < stream->channels; channel++) - channelsamples = MIN(channelsamples, file->soundbuf_chansamples[channel]); + for (int channel = 0; channel < stream->channels(); channel++) + channelsamples = (std::min<std::int32_t>)(channelsamples, m_soundbuf_chansamples[channel]); /* loop over pending sound chunks */ - for (chunknum = file->soundbuf_chunks; chunknum < stream->chunks; chunknum++) + for (chunknum = m_soundbuf_chunks; chunknum < stream->chunks(); chunknum++) { - avi_chunk_list *chunk = &stream->chunk[chunknum]; - UINT32 chunksamples = (chunk->length - 8) / bytes_per_sample; + avi_chunk_list &chunk = stream->chunk(chunknum); + std::uint32_t const chunksamples = (chunk.length - 8) / bytes_per_sample; /* stop if we don't have enough to satisfy this chunk */ if (only_flush_full && channelsamples < chunksamples) @@ -3094,696 +3379,300 @@ static avi_error soundbuf_flush(avi_file *file, int only_flush_full) /* if we don't have all the samples we need, pad with 0's */ if (channelsamples > 0 && channelsamples < chunksamples) { - if (processedsamples + chunksamples > file->soundbuf_samples) - return AVIERR_EXCEEDED_SOUND_BUFFER; - memset(&file->soundbuf[(processedsamples + channelsamples) * stream->channels], 0, (chunksamples - channelsamples) * bytes_per_sample); + if (processedsamples + chunksamples > m_soundbuf_samples) + return error::EXCEEDED_SOUND_BUFFER; + std::memset(&m_soundbuf[(processedsamples + channelsamples) * stream->channels()], 0, (chunksamples - channelsamples) * bytes_per_sample); } /* if we're completely out of samples, clear the buffer entirely and use the end */ else if (channelsamples <= 0) { - processedsamples = file->soundbuf_samples - chunksamples; - memset(&file->soundbuf[processedsamples * stream->channels], 0, chunksamples * bytes_per_sample); + processedsamples = m_soundbuf_samples - chunksamples; + std::memset(&m_soundbuf[processedsamples * stream->channels()], 0, chunksamples * bytes_per_sample); chunkid = CHUNKTYPE_JUNK; finalchunks--; } /* copy the sample data in */ - avierr = chunk_overwrite(file, chunkid, &file->soundbuf[processedsamples * stream->channels], chunk->length - 8, &chunk->offset, FALSE); - if (avierr != AVIERR_NONE) + avierr = chunk_overwrite(chunkid, &m_soundbuf[processedsamples * stream->channels()], chunk.length - 8, chunk.offset, false); + if (avierr != error::NONE) return avierr; /* add up the samples */ if (channelsamples > chunksamples) - file->info.audio_numsamples = stream->samples += chunksamples; + m_info.audio_numsamples = stream->add_samples(chunksamples); else if (channelsamples > 0) - file->info.audio_numsamples = stream->samples += channelsamples; + m_info.audio_numsamples = stream->add_samples(channelsamples); /* advance past those */ processedsamples += chunksamples; channelsamples -= chunksamples; - channelsamples = MAX(0, channelsamples); + channelsamples = (std::max)(0, channelsamples); } /* if we have a non-zero offset, shift everything down */ if (processedsamples > 0) { /* first account for the samples we processed */ - memmove(&file->soundbuf[0], &file->soundbuf[processedsamples * stream->channels], (file->soundbuf_samples - processedsamples) * bytes_per_sample); - for (channel = 0; channel < stream->channels; channel++) - file->soundbuf_chansamples[channel] -= processedsamples; + std::memmove(&m_soundbuf[0], &m_soundbuf[processedsamples * stream->channels()], (m_soundbuf_samples - processedsamples) * bytes_per_sample); + for (int channel = 0; channel < stream->channels(); channel++) + m_soundbuf_chansamples[channel] -= processedsamples; } /* update the final chunk count */ if (!only_flush_full) - stream->chunks = finalchunks; + stream->set_chunks(finalchunks); /* account for flushed chunks */ - file->soundbuf_chunks = chunknum; - return AVIERR_NONE; + m_soundbuf_chunks = chunknum; + return error::NONE; } /*------------------------------------------------- - rgb32_compress_to_rgb - "compress" an RGB32 - bitmap to an RGB encoded frame + printf_chunk_recursive - print information + about a chunk recursively -------------------------------------------------*/ /** - * @fn static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, UINT8 *data, UINT32 numbytes) - * - * @brief RGB 32 compress to RGB. + * @fn static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent) * - * @param [in,out] stream If non-null, the stream. - * @param bitmap The bitmap. - * @param [in,out] data If non-null, the data. - * @param numbytes The numbytes. + * @brief Printf chunk recursive. * - * @return An avi_error. + * @param [in,out] file If non-null, the file. + * @param [in,out] container If non-null, the container. + * @param indent The indent. */ -static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, UINT8 *data, UINT32 numbytes) +void avi_file_impl::printf_chunk_recursive(avi_chunk const *container, int indent) { - int height = MIN(stream->height, bitmap.height()); - int width = MIN(stream->width, bitmap.width()); - UINT8 *dataend = data + numbytes; - int x, y; + char size[20], offset[20]; + avi_chunk curchunk; + error avierr; - /* compressed video */ - for (y = 0; y < height; y++) + /* iterate over chunks in this container */ + for (avierr = get_first_chunk(container, curchunk); avierr == error::NONE; avierr = get_next_chunk(container, curchunk)) { - const UINT32 *source = &bitmap.pix32(y); - UINT8 *dest = data + (stream->height - 1 - y) * stream->width * 3; - - for (x = 0; x < width && dest < dataend; x++) - { - rgb_t pix = *source++; - *dest++ = pix.b(); - *dest++ = pix.g(); - *dest++ = pix.r(); - } + std::uint32_t chunksize = curchunk.size; + bool recurse = false; - /* fill in any blank space on the right */ - for ( ; x < stream->width && dest < dataend; x++) - { - *dest++ = 0; - *dest++ = 0; - *dest++ = 0; - } - } + u64toa(curchunk.size, size); + u64toa(curchunk.offset, offset); + printf("%*schunk = %c%c%c%c, size=%s (%s)\n", indent, "", + std::uint8_t(curchunk.type >> 0), + std::uint8_t(curchunk.type >> 8), + std::uint8_t(curchunk.type >> 16), + std::uint8_t(curchunk.type >> 24), + size, offset); - /* fill in any blank space on the bottom */ - for ( ; y < stream->height; y++) - { - UINT8 *dest = data + (stream->height - 1 - y) * stream->width * 3; - for (x = 0; x < stream->width && dest < dataend; x++) + /* certain chunks are just containers; recurse into them */ + switch (curchunk.type) { - *dest++ = 0; - *dest++ = 0; - *dest++ = 0; + /* basic containers */ + case CHUNKTYPE_RIFF: + case CHUNKTYPE_LIST: + printf("%*stype = %c%c%c%c\n", indent, "", + std::uint8_t(curchunk.listtype >> 0), + std::uint8_t(curchunk.listtype >> 8), + std::uint8_t(curchunk.listtype >> 16), + std::uint8_t(curchunk.listtype >> 24)); + recurse = true; + chunksize = 0; + break; } - } - - return AVIERR_NONE; -} - - -/*------------------------------------------------- - yuv_decompress_to_yuy16 - decompress a YUV - encoded frame to a YUY16 bitmap --------------------------------------------------*/ - -/** - * @fn static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) - * - * @brief Yuv decompress to yuy 16. - * - * @param [in,out] stream If non-null, the stream. - * @param data The data. - * @param numbytes The numbytes. - * @param [in,out] bitmap The bitmap. - * - * @return An avi_error. - */ -static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) -{ - const UINT16 *dataend = (const UINT16 *)(data + numbytes); - int x, y; - - /* compressed video */ - for (y = 0; y < stream->height; y++) - { - const UINT16 *source = (const UINT16 *)data + y * stream->width; - UINT16 *dest = &bitmap.pix16(y); - - /* switch off the compression */ - switch (stream->format) + /* print data within the chunk */ + if (chunksize > 0 && curchunk.size < 1024 * 1024) { - case FORMAT_UYVY: - for (x = 0; x < stream->width && source < dataend; x++) - *dest++ = *source++; - break; + std::unique_ptr<std::uint8_t []> data; - case FORMAT_VYUY: - case FORMAT_YUY2: - for (x = 0; x < stream->width && source < dataend; x++) + /* read the data for a chunk */ + avierr = read_chunk_data(curchunk, data); + if (avierr == error::NONE) + { + std::uint32_t const bytes = (std::min)(std::uint32_t(512), chunksize); + for (std::uint32_t i = 0; i < bytes; i++) { - UINT16 pix = *source++; - *dest++ = (pix >> 8) | (pix << 8); + if (i % 16 == 0) printf("%*s ", indent, ""); + printf("%02X ", data[i]); + if (i % 16 == 15) printf("\n"); } - break; + if (chunksize % 16 != 0) printf("\n"); + } } + + /* if we're recursing, dive down */ + if (recurse) + printf_chunk_recursive(&curchunk, indent + 4); } - return AVIERR_NONE; + /* if we didn't get a legitimate error, indicate that */ + if (avierr != error::END) + printf("[chunk error %d]\n", int(avierr)); } +} // anonymous namespace + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ /*------------------------------------------------- - yuy16_compress_to_yuy - "compress" a YUY16 - bitmap to a YUV encoded frame + avi_open - open an AVI movie file for read -------------------------------------------------*/ /** - * @fn static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, UINT8 *data, UINT32 numbytes) + * @fn avi_error avi_open(const char *filename, avi_file **file) * - * @brief Yuy 16 compress to yuy. + * @brief Queries if a given avi open. * - * @param [in,out] stream If non-null, the stream. - * @param bitmap The bitmap. - * @param [in,out] data If non-null, the data. - * @param numbytes The numbytes. + * @param filename Filename of the file. + * @param [in,out] file If non-null, the file. * * @return An avi_error. */ -static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, UINT8 *data, UINT32 numbytes) +avi_file::error avi_file::open(std::string const &filename, ptr &file) { - const UINT16 *dataend = (const UINT16 *)(data + numbytes); - int x, y; - - /* compressed video */ - for (y = 0; y < stream->height; y++) - { - const UINT16 *source = &bitmap.pix16(y); - UINT16 *dest = (UINT16 *)data + y * stream->width; + /* open the file */ + osd_file::ptr f; + std::uint64_t length; + if (osd_file::open(filename, OPEN_FLAG_READ, f, length) != osd_file::error::NONE) + return error::CANT_OPEN_FILE; - /* switch off the compression */ - switch (stream->format) - { - case FORMAT_UYVY: - for (x = 0; x < stream->width && dest < dataend; x++) - *dest++ = *source++; - break; + /* allocate the file */ + std::unique_ptr<avi_file_impl> newfile; + try { newfile = std::make_unique<avi_file_impl>(std::move(f), length); } + catch (...) { return error::NO_MEMORY; } - case FORMAT_VYUY: - case FORMAT_YUY2: - for (x = 0; x < stream->width && dest < dataend; x++) - { - UINT16 pix = *source++; - *dest++ = (pix >> 8) | (pix << 8); - } - break; - } - } + /* parse the data */ + error const avierr = newfile->read_movie_data(); + if (avierr != error::NONE) + return avierr; - return AVIERR_NONE; + file = std::move(newfile); + return error::NONE; } /*------------------------------------------------- - huffyuv_extract_tables - extract HuffYUV - tables + avi_create - create a new AVI movie file -------------------------------------------------*/ /** - * @fn static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size) + * @fn avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file) * - * @brief Huffyuv extract tables. + * @brief Avi create. * - * @param [in,out] stream If non-null, the stream. - * @param chunkdata The chunkdata. - * @param size The size. + * @param filename Filename of the file. + * @param info The information. + * @param [in,out] file If non-null, the file. * * @return An avi_error. */ -static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size) +avi_file::error avi_file::create(std::string const &filename, movie_info const &info, ptr &file) { - const UINT8 *chunkend = chunkdata + size; - avi_error avierr = AVIERR_NONE; - int tabnum; - - /* allocate memory for the data */ - stream->huffyuv = (huffyuv_data *)malloc(sizeof(*stream->huffyuv)); - if (stream->huffyuv == nullptr) - { - avierr = AVIERR_NO_MEMORY; - goto error; - } - - /* extract predictor information */ - if (&chunkdata[40] >= chunkend) - return AVIERR_INVALID_DATA; - stream->huffyuv->predictor = chunkdata[40]; + /* validate video info */ + if ((info.video_format != 0 && info.video_format != FORMAT_UYVY && info.video_format != FORMAT_VYUY && info.video_format != FORMAT_YUY2) || + (info.video_width == 0) || + (info.video_height == 0) || + (info.video_depth == 0) || + (info.video_depth % 8 != 0)) + return error::UNSUPPORTED_VIDEO_FORMAT; - /* make sure it's the left predictor */ - if ((stream->huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) != HUFFYUV_PREDICT_LEFT) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + /* validate audio info */ + if (info.audio_format != 0 || + info.audio_channels > MAX_SOUND_CHANNELS || + info.audio_samplebits != 16) + return error::UNSUPPORTED_AUDIO_FORMAT; - /* make sure it's 16bpp YUV data */ - if (chunkdata[41] != 16) - return AVIERR_UNSUPPORTED_VIDEO_FORMAT; - chunkdata += 44; + /* open the file */ + osd_file::ptr f; + std::uint64_t length; + osd_file::error const filerr = osd_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, f, length); + if (filerr != osd_file::error::NONE) + return error::CANT_OPEN_FILE; - /* loop over tables */ - for (tabnum = 0; tabnum < 3; tabnum++) + /* allocate the file */ + error avierr; + std::unique_ptr<avi_file_impl> newfile; + try { newfile = std::make_unique<avi_file_impl>(std::move(f), info); } + catch (...) { - huffyuv_table *table = &stream->huffyuv->table[tabnum]; - UINT32 curbits, bitadd; - UINT16 bitsat16 = 0; - int offset = 0, bits; - - /* loop until we populate the whole table */ - while (offset < 256) - { - int data, shift, count, i; - - /* extract the next run */ - if (chunkdata >= chunkend) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - data = *chunkdata++; - shift = data & 0x1f; - count = data >> 5; - - /* zero count means next whole byte is a count */ - if (count == 0) - { - if (chunkdata >= chunkend) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - count = *chunkdata++; - } - for (i = 0; i < count; i++) - table->shift[offset++] = shift; - } - - /* now determine bit patterns and masks */ - curbits = 0; - for (bits = 31; bits >= 0; bits--) - { - bitadd = 1 << (32 - bits); - - /* make sure we've cleared out all the bits below */ - if ((curbits & (bitadd - 1)) != 0) - { - avierr = AVIERR_INVALID_DATA; - goto error; - } - - /* find all entries with this shift count and assign them */ - for (offset = 0; offset < 256; offset++) - if (table->shift[offset] == bits) - { - table->bits[offset] = curbits; - table->mask[offset] = ~(bitadd - 1); - curbits += bitadd; - } + avierr = error::NO_MEMORY; + goto error; + } - /* remember the bit pattern when we complete all the 17-bit codes */ - if (bits == 17) - bitsat16 = curbits >> 16; - } + /* initialize the sound buffering */ + avierr = newfile->soundbuf_initialize(); + if (avierr != error::NONE) + goto error; - /* allocate the number of extra lookup tables we need */ - if (bitsat16 > 0) - { - table->extralookup = (UINT16 *)malloc(bitsat16 * 65536 * sizeof(table->extralookup[0])); - if (table->extralookup == nullptr) - { - avierr = AVIERR_NO_MEMORY; - goto error; - } - for (offset = 0; offset < bitsat16; offset++) - table->baselookup[offset] = (offset << 8) | 0; - } + /* write the initial headers */ + avierr = newfile->write_initial_headers(); + if (avierr != error::NONE) + goto error; - /* then create lookup tables */ - for (offset = 0; offset < 256; offset++) - if (table->shift[offset] > 16) - { - UINT16 *tablebase = table->extralookup + (table->bits[offset] >> 16) * 65536; - UINT32 start = table->bits[offset] & 0xffff; - UINT32 end = start + ((1 << (32 - table->shift[offset])) - 1); - while (start <= end) - tablebase[start++] = (offset << 8) | (table->shift[offset] - 16); - } - else if (table->shift[offset] > 0) - { - UINT32 start = table->bits[offset] >> 16; - UINT32 end = start + ((1 << (16 - table->shift[offset])) - 1); - while (start <= end) - table->baselookup[start++] = (offset << 8) | table->shift[offset]; - } - } + file = std::move(newfile); + return error::NONE; error: - if (avierr != AVIERR_NONE && stream->huffyuv != nullptr) - { - free(stream->huffyuv); - stream->huffyuv = nullptr; - } + f.reset(); + newfile.reset(); + osd_file::remove(filename); return avierr; } /*------------------------------------------------- - huffyuv_decompress_to_yuy16 - decompress a - HuffYUV-encoded frame to a YUY16 bitmap + avi_error_string - get the error string for + an avi_error -------------------------------------------------*/ /** - * @fn static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) + * @fn const char *avi_error_string(avi_error err) * - * @brief Huffyuv decompress to yuy 16. + * @brief Avi error string. * - * @param [in,out] stream If non-null, the stream. - * @param data The data. - * @param numbytes The numbytes. - * @param [in,out] bitmap The bitmap. + * @param err The error. * - * @return An avi_error. + * @return null if it fails, else a char*. */ -static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_yuy16 &bitmap) +const char *avi_file::error_string(error err) { - huffyuv_data *huffyuv = stream->huffyuv; - int prevlines = (stream->height > 288) ? 2 : 1; - UINT8 lastprevy = 0, lastprevcb = 0, lastprevcr = 0; - UINT8 lasty = 0, lastcb = 0, lastcr = 0; - UINT8 bitsinbuffer = 0; - UINT32 bitbuffer = 0; - UINT32 dataoffs = 0; - int x, y; - - /* compressed video */ - for (y = 0; y < stream->height; y++) - { - UINT16 *dest = &bitmap.pix16(y); - - /* handle the first four bytes independently */ - x = 0; - if (y == 0) - { - /* first DWORD is stored as YUY2 */ - lasty = data[dataoffs++]; - lastcb = data[dataoffs++]; - *dest++ = (lasty << 8) | lastcb; - lasty = data[dataoffs++]; - lastcr = data[dataoffs++]; - *dest++ = (lasty << 8) | lastcr; - x = 2; - } - - /* loop over pixels */ - for ( ; x < stream->width; x++) - { - huffyuv_table *ytable = &huffyuv->table[0]; - huffyuv_table *ctable = &huffyuv->table[1 + (x & 1)]; - UINT16 pixel, huffdata; - int shift; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - /* look up the Y component */ - huffdata = ytable->baselookup[bitbuffer >> 16]; - shift = huffdata & 0xff; - if (shift == 0) - { - bitsinbuffer -= 16; - bitbuffer <<= 16; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - huffdata = ytable->extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; - shift = huffdata & 0xff; - } - bitsinbuffer -= shift; - bitbuffer <<= shift; - pixel = huffdata & 0xff00; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - /* look up the Cb/Cr component */ - huffdata = ctable->baselookup[bitbuffer >> 16]; - shift = huffdata & 0xff; - if (shift == 0) - { - bitsinbuffer -= 16; - bitbuffer <<= 16; - - /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ - while (bitsinbuffer <= 24 && dataoffs < numbytes) - { - bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); - bitsinbuffer += 8; - } - - huffdata = ctable->extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; - shift = huffdata & 0xff; - } - bitsinbuffer -= shift; - bitbuffer <<= shift; - *dest++ = pixel | (huffdata >> 8); - } - } - - /* apply deltas */ - lastprevy = lastprevcb = lastprevcr = 0; - for (y = 0; y < stream->height; y++) + switch (err) { - UINT16 *prevrow = &bitmap.pix16(y - prevlines); - UINT16 *dest = &bitmap.pix16(y); - - /* handle the first four bytes independently */ - x = 0; - if (y == 0) - { - /* lasty, lastcr, lastcb are set up previously */ - x = 2; - } - - /* left predict or gradient predict */ - if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_LEFT || - ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT)) - { - /* first do left deltas */ - for ( ; x < stream->width; x += 2) - { - UINT16 pixel0 = dest[x + 0]; - UINT16 pixel1 = dest[x + 1]; - - lasty += pixel0 >> 8; - lastcb += pixel0; - dest[x + 0] = (lasty << 8) | lastcb; - - lasty += pixel1 >> 8; - lastcr += pixel1; - dest[x + 1] = (lasty << 8) | lastcr; - } - - /* for gradient, we then add in the previous row */ - if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT && y >= prevlines) - for (x = 0; x < stream->width; x++) - { - UINT16 curpix = dest[x]; - UINT16 prevpix = prevrow[x]; - UINT8 ysum = (curpix >> 8) + (prevpix >> 8); - UINT8 csum = curpix + prevpix; - dest[x] = (ysum << 8) | csum; - } - } - - /* median predict on rows > 0 */ - else if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_MEDIAN && y >= prevlines) - { - for ( ; x < stream->width; x += 2) - { - UINT16 prevpixel0 = prevrow[x + 0]; - UINT16 prevpixel1 = prevrow[x + 1]; - UINT16 pixel0 = dest[x + 0]; - UINT16 pixel1 = dest[x + 1]; - UINT8 a, b, c; - - /* compute previous, above, and (prev + above - above-left) */ - a = lasty; - b = prevpixel0 >> 8; - c = lastprevy; - lastprevy = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lasty = (pixel0 >> 8) + b; - - /* compute previous, above, and (prev + above - above-left) */ - a = lastcb; - b = prevpixel0 & 0xff; - c = lastprevcb; - lastprevcb = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lastcb = (pixel0 & 0xff) + b; - dest[x + 0] = (lasty << 8) | lastcb; - - /* compute previous, above, and (prev + above - above-left) */ - a = lasty; - b = prevpixel1 >> 8; - c = lastprevy; - lastprevy = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lasty = (pixel1 >> 8) + b; - - /* compute previous, above, and (prev + above - above-left) */ - a = lastcr; - b = prevpixel1 & 0xff; - c = lastprevcr; - lastprevcr = b; - if (a > b) { UINT8 tmp = a; a = b; b = tmp; } - if (a > c) { UINT8 tmp = a; a = c; c = tmp; } - if (b > c) { UINT8 tmp = b; b = c; c = tmp; } - lastcr = (pixel1 & 0xff) + b; - dest[x + 1] = (lasty << 8) | lastcr; - } - } + case error::NONE: return "success"; + case error::END: return "hit end of file"; + case error::INVALID_DATA: return "invalid data"; + case error::NO_MEMORY: return "out of memory"; + case error::READ_ERROR: return "read error"; + case error::WRITE_ERROR: return "write error"; + case error::STACK_TOO_DEEP: return "stack overflow"; + case error::UNSUPPORTED_FEATURE: return "unsupported feature"; + case error::CANT_OPEN_FILE: return "unable to open file"; + case error::INCOMPATIBLE_AUDIO_STREAMS: return "found incompatible audio streams"; + case error::INVALID_SAMPLERATE: return "found invalid sample rate"; + case error::INVALID_STREAM: return "invalid stream"; + case error::INVALID_FRAME: return "invalid frame index"; + case error::INVALID_BITMAP: return "invalid bitmap"; + case error::UNSUPPORTED_VIDEO_FORMAT: return "unsupported video format"; + case error::UNSUPPORTED_AUDIO_FORMAT: return "unsupported audio format"; + case error::EXCEEDED_SOUND_BUFFER: return "sound buffer overflow"; + default: return "undocumented error"; } - - return AVIERR_NONE; } -/** - * @fn static void u64toa(UINT64 val, char *output) - * - * @brief 64toas. - * - * @param val The value. - * @param [in,out] output If non-null, the output. - */ -static void u64toa(UINT64 val, char *output) +avi_file::avi_file() { - UINT32 lo = (UINT32)(val & 0xffffffff); - UINT32 hi = (UINT32)(val >> 32); - if (hi != 0) - sprintf(output, "%X%08X", hi, lo); - else - sprintf(output, "%X", lo); } -/*------------------------------------------------- - printf_chunk_recursive - print information - about a chunk recursively --------------------------------------------------*/ - -/** - * @fn static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent) - * - * @brief Printf chunk recursive. - * - * @param [in,out] file If non-null, the file. - * @param [in,out] container If non-null, the container. - * @param indent The indent. - */ - -static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent) +avi_file::~avi_file() { - char size[20], offset[20]; - avi_chunk curchunk; - int avierr; - - /* iterate over chunks in this container */ - for (avierr = get_first_chunk(file, container, &curchunk); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, &curchunk)) - { - UINT32 chunksize = curchunk.size; - int recurse = FALSE; - - u64toa(curchunk.size, size); - u64toa(curchunk.offset, offset); - printf("%*schunk = %c%c%c%c, size=%s (%s)\n", indent, "", - (UINT8)(curchunk.type >> 0), - (UINT8)(curchunk.type >> 8), - (UINT8)(curchunk.type >> 16), - (UINT8)(curchunk.type >> 24), - size, offset); - - /* certain chunks are just containers; recurse into them */ - switch (curchunk.type) - { - /* basic containers */ - case CHUNKTYPE_RIFF: - case CHUNKTYPE_LIST: - printf("%*stype = %c%c%c%c\n", indent, "", - (UINT8)(curchunk.listtype >> 0), - (UINT8)(curchunk.listtype >> 8), - (UINT8)(curchunk.listtype >> 16), - (UINT8)(curchunk.listtype >> 24)); - recurse = TRUE; - chunksize = 0; - break; - } - - /* print data within the chunk */ - if (chunksize > 0 && curchunk.size < 1024 * 1024) - { - UINT8 *data = nullptr; - int i; - - /* read the data for a chunk */ - avierr = read_chunk_data(file, &curchunk, &data); - if (avierr == AVIERR_NONE) - { - int bytes = MIN(512, chunksize); - for (i = 0; i < bytes; i++) - { - if (i % 16 == 0) printf("%*s ", indent, ""); - printf("%02X ", data[i]); - if (i % 16 == 15) printf("\n"); - } - if (chunksize % 16 != 0) printf("\n"); - free(data); - } - } - - /* if we're recursing, dive down */ - if (recurse) - printf_chunk_recursive(file, &curchunk, indent + 4); - } - - /* if we didn't get a legitimate error, indicate that */ - if (avierr != AVIERR_END) - printf("[chunk error %d]\n", avierr); } diff --git a/src/lib/util/aviio.h b/src/lib/util/aviio.h index 86e2fd19cd2..c6d547903fe 100644 --- a/src/lib/util/aviio.h +++ b/src/lib/util/aviio.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** aviio.h @@ -8,52 +8,15 @@ ***************************************************************************/ -#ifndef __AVIIO_H__ -#define __AVIIO_H__ +#ifndef MAME_LIB_UTIL_AVIIO_H +#define MAME_LIB_UTIL_AVIIO_H #include "osdcore.h" #include "bitmap.h" - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -enum avi_error -{ - AVIERR_NONE = 0, - AVIERR_END, - AVIERR_INVALID_DATA, - AVIERR_NO_MEMORY, - AVIERR_READ_ERROR, - AVIERR_WRITE_ERROR, - AVIERR_STACK_TOO_DEEP, - AVIERR_UNSUPPORTED_FEATURE, - AVIERR_CANT_OPEN_FILE, - AVIERR_INCOMPATIBLE_AUDIO_STREAMS, - AVIERR_INVALID_SAMPLERATE, - AVIERR_INVALID_STREAM, - AVIERR_INVALID_FRAME, - AVIERR_INVALID_BITMAP, - AVIERR_UNSUPPORTED_VIDEO_FORMAT, - AVIERR_UNSUPPORTED_AUDIO_FORMAT, - AVIERR_EXCEEDED_SOUND_BUFFER -}; - - -enum avi_datatype -{ - AVIDATA_VIDEO, - AVIDATA_AUDIO_CHAN0, - AVIDATA_AUDIO_CHAN1, - AVIDATA_AUDIO_CHAN2, - AVIDATA_AUDIO_CHAN3, - AVIDATA_AUDIO_CHAN4, - AVIDATA_AUDIO_CHAN5, - AVIDATA_AUDIO_CHAN6, - AVIDATA_AUDIO_CHAN7 -}; - +#include <cstdint> +#include <memory> +#include <string> /*************************************************************************** @@ -69,53 +32,98 @@ enum avi_datatype -/*************************************************************************** - TYPE DEFINITIONS -***************************************************************************/ - -struct avi_file; - - -struct avi_movie_info +class avi_file { - UINT32 video_format; /* format of video data */ - UINT32 video_timescale; /* timescale for video data */ - UINT32 video_sampletime; /* duration of a single video sample (frame) */ - UINT32 video_numsamples; /* total number of video samples */ - UINT32 video_width; /* width of the video */ - UINT32 video_height; /* height of the video */ - UINT32 video_depth; /* depth of the video */ - - UINT32 audio_format; /* format of audio data */ - UINT32 audio_timescale; /* timescale for audio data */ - UINT32 audio_sampletime; /* duration of a single audio sample */ - UINT32 audio_numsamples; /* total number of audio samples */ - UINT32 audio_channels; /* number of audio channels */ - UINT32 audio_samplebits; /* number of bits per channel */ - UINT32 audio_samplerate; /* sample rate of audio */ +public: + + /*********************************************************************** + CONSTANTS + ***********************************************************************/ + + enum class error + { + NONE = 0, + END, + INVALID_DATA, + NO_MEMORY, + READ_ERROR, + WRITE_ERROR, + STACK_TOO_DEEP, + UNSUPPORTED_FEATURE, + CANT_OPEN_FILE, + INCOMPATIBLE_AUDIO_STREAMS, + INVALID_SAMPLERATE, + INVALID_STREAM, + INVALID_FRAME, + INVALID_BITMAP, + UNSUPPORTED_VIDEO_FORMAT, + UNSUPPORTED_AUDIO_FORMAT, + EXCEEDED_SOUND_BUFFER + }; + + enum class datatype + { + VIDEO, + AUDIO_CHAN0, + AUDIO_CHAN1, + AUDIO_CHAN2, + AUDIO_CHAN3, + AUDIO_CHAN4, + AUDIO_CHAN5, + AUDIO_CHAN6, + AUDIO_CHAN7 + }; + + + /*********************************************************************** + TYPE DEFINITIONS + ***********************************************************************/ + + struct movie_info + { + std::uint32_t video_format; // format of video data + std::uint32_t video_timescale; // timescale for video data + std::uint32_t video_sampletime; // duration of a single video sample (frame) + std::uint32_t video_numsamples; // total number of video samples + std::uint32_t video_width; // width of the video + std::uint32_t video_height; // height of the video + std::uint32_t video_depth; // depth of the video + + std::uint32_t audio_format; // format of audio data + std::uint32_t audio_timescale; // timescale for audio data + std::uint32_t audio_sampletime; // duration of a single audio sample + std::uint32_t audio_numsamples; // total number of audio samples + std::uint32_t audio_channels; // number of audio channels + std::uint32_t audio_samplebits; // number of bits per channel + std::uint32_t audio_samplerate; // sample rate of audio + }; + + typedef std::unique_ptr<avi_file> ptr; + + + /*********************************************************************** + PROTOTYPES + ***********************************************************************/ + + static error open(std::string const &filename, ptr &file); + static error create(std::string const &filename, movie_info const &info, ptr &file); + virtual ~avi_file(); + + virtual void printf_chunks() = 0; + static const char *error_string(error err); + + virtual movie_info const &get_movie_info() const = 0; + virtual std::uint32_t first_sample_in_frame(std::uint32_t framenum) const = 0; + + virtual error read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) = 0; + virtual error read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) = 0; + + virtual error append_video_frame(bitmap_yuy16 &bitmap) = 0; + virtual error append_video_frame(bitmap_rgb32 &bitmap) = 0; + virtual error append_sound_samples(int channel, std::int16_t const *samples, std::uint32_t numsamples, std::uint32_t sampleskip) = 0; + +protected: + avi_file(); }; - - -/*************************************************************************** - PROTOTYPES -***************************************************************************/ - -avi_error avi_open(const char *filename, avi_file **file); -avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file); -avi_error avi_close(avi_file *file); - -void avi_printf_chunks(avi_file *file); -const char *avi_error_string(avi_error err); - -const avi_movie_info *avi_get_movie_info(avi_file *file); -UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum); - -avi_error avi_read_video_frame(avi_file *file, UINT32 framenum, bitmap_yuy16 &bitmap); -avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output); - -avi_error avi_append_video_frame(avi_file *file, bitmap_yuy16 &bitmap); -avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap); -avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples, UINT32 sampleskip); - -#endif +#endif // MAME_LIB_UTIL_AVIIO_H diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp index 8bdf4af6c0f..bd48a73d8d8 100644 --- a/src/lib/util/cdrom.cpp +++ b/src/lib/util/cdrom.cpp @@ -244,8 +244,8 @@ cdrom_file *cdrom_open(const char *inputfile) for (i = 0; i < file->cdtoc.numtrks; i++) { - file_error filerr = util::core_file::open(file->track_info.track[i].fname.c_str(), OPEN_FLAG_READ, file->fhandle[i]); - if (filerr != FILERR_NONE) + osd_file::error filerr = util::core_file::open(file->track_info.track[i].fname.c_str(), OPEN_FLAG_READ, file->fhandle[i]); + if (filerr != osd_file::error::NONE) { fprintf(stderr, "Unable to open file: %s\n", file->track_info.track[i].fname.c_str()); cdrom_close(file); diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp index 93a1b34ba29..3a97c6ce0fe 100644 --- a/src/lib/util/chd.cpp +++ b/src/lib/util/chd.cpp @@ -660,8 +660,8 @@ chd_error chd_file::create(const char *filename, UINT64 logicalbytes, UINT32 hun // create the new file util::core_file::ptr file; - const file_error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); - if (filerr != FILERR_NONE) + const osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); + if (filerr != osd_file::error::NONE) return CHDERR_FILE_NOT_FOUND; // create the file normally, then claim the file @@ -672,7 +672,7 @@ chd_error chd_file::create(const char *filename, UINT64 logicalbytes, UINT32 hun if (chderr != CHDERR_NONE) { file.reset(); - osd_rmfile(filename); + osd_file::remove(filename); } else { @@ -705,8 +705,8 @@ chd_error chd_file::create(const char *filename, UINT64 logicalbytes, UINT32 hun // create the new file util::core_file::ptr file; - const file_error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); - if (filerr != FILERR_NONE) + const osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); + if (filerr != osd_file::error::NONE) return CHDERR_FILE_NOT_FOUND; // create the file normally, then claim the file @@ -717,7 +717,7 @@ chd_error chd_file::create(const char *filename, UINT64 logicalbytes, UINT32 hun if (chderr != CHDERR_NONE) { file.reset(); - osd_rmfile(filename); + osd_file::remove(filename); } else { @@ -749,8 +749,8 @@ chd_error chd_file::open(const char *filename, bool writeable, chd_file *parent) // open the file const UINT32 openflags = writeable ? (OPEN_FLAG_READ | OPEN_FLAG_WRITE) : OPEN_FLAG_READ; util::core_file::ptr file; - const file_error filerr = util::core_file::open(filename, openflags, file); - if (filerr != FILERR_NONE) + const osd_file::error filerr = util::core_file::open(filename, openflags, file); + if (filerr != osd_file::error::NONE) return CHDERR_FILE_NOT_FOUND; // now open the CHD diff --git a/src/lib/util/chdcd.cpp b/src/lib/util/chdcd.cpp index eeb196d242f..0e8b32b1ed6 100644 --- a/src/lib/util/chdcd.cpp +++ b/src/lib/util/chdcd.cpp @@ -87,13 +87,10 @@ static std::string get_file_path(std::string &path) static UINT64 get_file_size(const char *filename) { - osd_file *file; - UINT64 filesize = 0; - file_error filerr; + osd_file::ptr file; + std::uint64_t filesize = 0; - filerr = osd_open(filename, OPEN_FLAG_READ, &file, &filesize); - if (filerr == FILERR_NONE) - osd_close(file); + osd_file::open(filename, OPEN_FLAG_READ, file, filesize); return filesize; } @@ -215,57 +212,51 @@ static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs) UINT32 length, rate, filesize; UINT16 bits, temp16; char buf[32]; - osd_file *file; - file_error filerr; + osd_file::ptr file; UINT64 fsize = 0; - UINT32 actual; + std::uint32_t actual; - filerr = osd_open(filename, OPEN_FLAG_READ, &file, &fsize); - if (filerr != FILERR_NONE) + osd_file::error filerr = osd_file::open(filename, OPEN_FLAG_READ, file, fsize); + if (filerr != osd_file::error::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); + file->read(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); + file->read(&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); + file->read(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; } @@ -273,9 +264,9 @@ static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs) /* seek until we find a format tag */ while (1) { - osd_read(file, buf, offset, 4, &actual); + file->read(buf, offset, 4, actual); offset += actual; - osd_read(file, &length, offset, 4, &actual); + file->read(&length, offset, 4, actual); offset += actual; length = LITTLE_ENDIANIZE_INT32(length); if (memcmp(&buf[0], "fmt ", 4) == 0) @@ -285,56 +276,51 @@ static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs) 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); + file->read(&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); + file->read(&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); + file->read(&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); + file->read(buf, offset, 6, actual); offset += actual; /* bits/sample */ - osd_read(file, &bits, offset, 2, &actual); + file->read(&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; } @@ -345,9 +331,9 @@ static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs) /* seek until we find a data tag */ while (1) { - osd_read(file, buf, offset, 4, &actual); + file->read(buf, offset, 4, actual); offset += actual; - osd_read(file, &length, offset, 4, &actual); + file->read(&length, offset, 4, actual); offset += actual; length = LITTLE_ENDIANIZE_INT32(length); if (memcmp(&buf[0], "data", 4) == 0) @@ -357,14 +343,11 @@ static UINT32 parse_wav_sample(const char *filename, UINT32 *dataoffs) 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) { diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index 64169a606fc..ec1e25340e3 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -23,9 +23,7 @@ namespace util { - namespace { - /*************************************************************************** VALIDATION ***************************************************************************/ @@ -143,7 +141,7 @@ class core_proxy_file : public core_file public: core_proxy_file(core_file &file) : m_file(file) { } virtual ~core_proxy_file() override { } - virtual file_error compress(int level) override { return m_file.compress(level); } + virtual osd_file::error compress(int level) override { return m_file.compress(level); } virtual int seek(std::int64_t offset, int whence) override { return m_file.seek(offset, whence); } virtual std::uint64_t tell() const override { return m_file.tell(); } @@ -159,9 +157,9 @@ public: virtual std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); } virtual int puts(const char *s) override { return m_file.puts(s); } virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override { return m_file.vprintf(args); } - virtual file_error truncate(std::uint64_t offset) override { return m_file.truncate(offset); } + virtual osd_file::error truncate(std::uint64_t offset) override { return m_file.truncate(offset); } - virtual file_error flush() override { return m_file.flush(); } + virtual osd_file::error flush() override { return m_file.flush(); } private: core_file &m_file; @@ -232,7 +230,7 @@ public: } ~core_in_memory_file() override { purge(); } - virtual file_error compress(int level) override { return FILERR_INVALID_ACCESS; } + virtual osd_file::error compress(int level) override { return osd_file::error::INVALID_ACCESS; } virtual int seek(std::int64_t offset, int whence) override; virtual std::uint64_t tell() const override { return m_offset; } @@ -243,8 +241,8 @@ public: virtual void const *buffer() override { return m_data; } virtual std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; } - virtual file_error truncate(std::uint64_t offset) override; - virtual file_error flush() override { clear_putback(); return FILERR_NONE; } + virtual osd_file::error truncate(std::uint64_t offset) override; + virtual osd_file::error flush() override { clear_putback(); return osd_file::error::NONE; } protected: core_in_memory_file(std::uint32_t openflags, std::uint64_t length) @@ -295,9 +293,9 @@ private: class core_osd_file : public core_in_memory_file { public: - core_osd_file(std::uint32_t openmode, osd_file *file, std::uint64_t length) + core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length) : core_in_memory_file(openmode, length) - , m_file(file) + , m_file(std::move(file)) , m_zdata() , m_bufferbase(0) , m_bufferbytes(0) @@ -305,7 +303,7 @@ public: } ~core_osd_file() override; - virtual file_error compress(int level) override; + virtual osd_file::error compress(int level) override; virtual int seek(std::int64_t offset, int whence) override; @@ -313,8 +311,8 @@ public: virtual void const *buffer() override; virtual std::uint32_t write(void const *buffer, std::uint32_t length) override; - virtual file_error truncate(std::uint64_t offset) override; - virtual file_error flush() override; + virtual osd_file::error truncate(std::uint64_t offset) override; + virtual osd_file::error flush() override; protected: @@ -323,10 +321,10 @@ protected: private: static constexpr std::size_t FILE_BUFFER_SIZE = 512; - file_error osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - file_error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); + osd_file::error osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); + osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - osd_file * m_file; // OSD file handle + osd_file::ptr m_file; // OSD file handle zlib_data::ptr m_zdata; // compression data std::uint64_t m_bufferbase; // base offset of internal buffer std::uint32_t m_bufferbytes; // bytes currently loaded into buffer @@ -688,14 +686,14 @@ std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length) truncate - truncate a file -------------------------------------------------*/ -file_error core_in_memory_file::truncate(std::uint64_t offset) +osd_file::error core_in_memory_file::truncate(std::uint64_t offset) { if (m_length < offset) - return FILERR_FAILURE; + return osd_file::error::FAILURE; // adjust to new length and offset set_length(offset); - return FILERR_NONE; + return osd_file::error::NONE; } @@ -736,8 +734,6 @@ core_osd_file::~core_osd_file() // close files and free memory if (m_zdata) compress(FCOMPRESS_NONE); - if (m_file) - osd_close(m_file); } @@ -747,13 +743,13 @@ core_osd_file::~core_osd_file() compression, or up to 9 for max compression -------------------------------------------------*/ -file_error core_osd_file::compress(int level) +osd_file::error core_osd_file::compress(int level) { - file_error result = FILERR_NONE; + osd_file::error result = osd_file::error::NONE; // can only do this for read-only and write-only cases if (read_access() && write_access()) - return FILERR_INVALID_ACCESS; + return osd_file::error::INVALID_ACCESS; // if we have been compressing, flush and free the data if (m_zdata && (level == FCOMPRESS_NONE)) @@ -767,7 +763,7 @@ file_error core_osd_file::compress(int level) zerr = m_zdata->finalise(); if ((zerr != Z_STREAM_END) && (zerr != Z_OK)) { - result = FILERR_INVALID_DATA; + result = osd_file::error::INVALID_DATA; break; } @@ -775,8 +771,8 @@ file_error core_osd_file::compress(int level) if (m_zdata->has_output()) { std::uint32_t actualdata; - auto const filerr = osd_write(m_file, m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), &actualdata); - if (filerr != FILERR_NONE) + auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); + if (filerr != osd_file::error::NONE) break; m_zdata->add_realoffset(actualdata); m_zdata->reset_output(); @@ -800,7 +796,7 @@ file_error core_osd_file::compress(int level) // on error, return an error if (zerr != Z_OK) - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; // flush buffers m_bufferbytes = 0; @@ -888,13 +884,12 @@ void const *core_osd_file::buffer() // read the file std::uint32_t read_length = 0; auto const filerr = osd_or_zlib_read(buf, 0, length(), read_length); - if ((filerr != FILERR_NONE) || (read_length != length())) + if ((filerr != osd_file::error::NONE) || (read_length != length())) purge(); else { // close the file because we don't need it anymore - osd_close(m_file); - m_file = nullptr; + m_file.reset(); } } return core_in_memory_file::buffer(); @@ -931,19 +926,19 @@ std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length) truncate - truncate a file -------------------------------------------------*/ -file_error core_osd_file::truncate(std::uint64_t offset) +osd_file::error core_osd_file::truncate(std::uint64_t offset) { if (is_loaded()) return core_in_memory_file::truncate(offset); // truncate file - auto const err = osd_truncate(m_file, offset); - if (err != FILERR_NONE) + auto const err = m_file->truncate(offset); + if (err != osd_file::error::NONE) return err; // and adjust to new length and offset set_length(offset); - return FILERR_NONE; + return osd_file::error::NONE; } @@ -951,7 +946,7 @@ file_error core_osd_file::truncate(std::uint64_t offset) flush - flush file buffers -------------------------------------------------*/ -file_error core_osd_file::flush() +osd_file::error core_osd_file::flush() { if (is_loaded()) return core_in_memory_file::flush(); @@ -962,7 +957,7 @@ file_error core_osd_file::flush() // invalidate any buffered data m_bufferbytes = 0; - return osd_fflush(m_file); + return m_file->flush(); } @@ -971,18 +966,18 @@ file_error core_osd_file::flush() handles zlib-compressed data -------------------------------------------------*/ -file_error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) +osd_file::error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) { // if no compression, just pass through if (!m_zdata) - return osd_read(m_file, buffer, offset, length, &actual); + return m_file->read(buffer, offset, length, actual); // if the offset doesn't match the next offset, fail if (!m_zdata->is_nextoffset(offset)) - return FILERR_INVALID_ACCESS; + return osd_file::error::INVALID_ACCESS; // set up the destination - file_error filerr = FILERR_NONE; + osd_file::error filerr = osd_file::error::NONE; m_zdata->set_output(buffer, length); while (!m_zdata->output_full()) { @@ -992,7 +987,7 @@ file_error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, s auto const zerr = m_zdata->decompress(); if (Z_OK != zerr) { - if (Z_STREAM_END != zerr) filerr = FILERR_INVALID_DATA; + if (Z_STREAM_END != zerr) filerr = osd_file::error::INVALID_DATA; break; } } @@ -1001,8 +996,8 @@ file_error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, s if (!m_zdata->has_input()) { std::uint32_t actualdata = 0; - filerr = osd_read(m_file, m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), &actualdata); - if (filerr != FILERR_NONE) break; + filerr = m_file->read(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), actualdata); + if (filerr != osd_file::error::NONE) break; m_zdata->add_realoffset(actualdata); m_zdata->reset_input(actualdata); if (!m_zdata->has_input()) break; @@ -1022,7 +1017,7 @@ file_error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, s -------------------------------------------------*/ /** - * @fn file_error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t actual) + * @fn osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t actual) * * @brief OSD or zlib write. * @@ -1031,18 +1026,18 @@ file_error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, s * @param length The length. * @param [in,out] actual The actual. * - * @return A file_error. + * @return A osd_file::error. */ -file_error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) +osd_file::error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) { // if no compression, just pass through if (!m_zdata) - return osd_write(m_file, buffer, offset, length, &actual); + return m_file->write(buffer, offset, length, actual); // if the offset doesn't match the next offset, fail if (!m_zdata->is_nextoffset(offset)) - return FILERR_INVALID_ACCESS; + return osd_file::error::INVALID_ACCESS; // set up the source m_zdata->set_input(buffer, length); @@ -1054,15 +1049,15 @@ file_error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t of { actual = length - m_zdata->input_size(); m_zdata->add_nextoffset(actual); - return FILERR_INVALID_DATA; + return osd_file::error::INVALID_DATA; } // write more data if we are full up if (m_zdata->output_full()) { std::uint32_t actualdata = 0; - auto const filerr = osd_write(m_file, m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), &actualdata); - if (filerr != FILERR_NONE) + auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); + if (filerr != osd_file::error::NONE) return filerr; m_zdata->add_realoffset(actualdata); m_zdata->reset_output(); @@ -1072,7 +1067,7 @@ file_error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t of // we wrote everything actual = length; m_zdata->add_nextoffset(actual); - return FILERR_NONE; + return osd_file::error::NONE; } } // anonymous namespace @@ -1088,24 +1083,23 @@ file_error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t of return an error code -------------------------------------------------*/ -file_error core_file::open(char const *filename, std::uint32_t openflags, ptr &file) +osd_file::error core_file::open(char const *filename, std::uint32_t openflags, ptr &file) { - // attempt to open the file - osd_file *f = nullptr; - std::uint64_t length = 0; - auto const filerr = osd_open(filename, openflags, &f, &length); - if (filerr != FILERR_NONE) - return filerr; - try { - file = std::make_unique<core_osd_file>(openflags, f, length); - return FILERR_NONE; + // attempt to open the file + osd_file::ptr f; + std::uint64_t length = 0; + auto const filerr = osd_file::open(filename, openflags, f, length); + if (filerr != osd_file::error::NONE) + return filerr; + + file = std::make_unique<core_osd_file>(openflags, std::move(f), length); + return osd_file::error::NONE; } catch (...) { - osd_close(f); - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; } } @@ -1115,20 +1109,20 @@ file_error core_file::open(char const *filename, std::uint32_t openflags, ptr &f like access and return an error code -------------------------------------------------*/ -file_error core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +osd_file::error core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) { // can only do this for read access if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) - return FILERR_INVALID_ACCESS; + return osd_file::error::INVALID_ACCESS; try { file.reset(new core_in_memory_file(openflags, data, length, false)); - return FILERR_NONE; + return osd_file::error::NONE; } catch (...) { - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; } } @@ -1139,24 +1133,24 @@ file_error core_file::open_ram(void const *data, std::size_t length, std::uint32 error code -------------------------------------------------*/ -file_error core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +osd_file::error core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) { // can only do this for read access if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) - return FILERR_INVALID_ACCESS; + return osd_file::error::INVALID_ACCESS; try { ptr result(new core_in_memory_file(openflags, data, length, true)); if (!result->buffer()) - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; file = std::move(result); - return FILERR_NONE; + return osd_file::error::NONE; } catch (...) { - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; } } @@ -1166,16 +1160,16 @@ file_error core_file::open_ram_copy(void const *data, std::size_t length, std::u object and return an error code -------------------------------------------------*/ -file_error core_file::open_proxy(core_file &file, ptr &proxy) +osd_file::error core_file::open_proxy(core_file &file, ptr &proxy) { try { proxy = std::make_unique<core_proxy_file>(file); - return FILERR_NONE; + return osd_file::error::NONE; } catch (...) { - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; } } @@ -1195,19 +1189,19 @@ core_file::~core_file() pointer -------------------------------------------------*/ -file_error core_file::load(char const *filename, void **data, std::uint32_t &length) +osd_file::error core_file::load(char const *filename, void **data, std::uint32_t &length) { ptr file; // attempt to open the file auto const err = open(filename, OPEN_FLAG_READ, file); - if (err != FILERR_NONE) + if (err != osd_file::error::NONE) return err; // get the size auto const size = file->size(); if (std::uint32_t(size) != size) - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; // allocate memory *data = osd_malloc(size); @@ -1217,26 +1211,26 @@ file_error core_file::load(char const *filename, void **data, std::uint32_t &len if (file->read(*data, size) != size) { free(*data); - return FILERR_FAILURE; + return osd_file::error::FAILURE; } // close the file and return data - return FILERR_NONE; + return osd_file::error::NONE; } -file_error core_file::load(char const *filename, dynamic_buffer &data) +osd_file::error core_file::load(char const *filename, dynamic_buffer &data) { ptr file; // attempt to open the file auto const err = open(filename, OPEN_FLAG_READ, file); - if (err != FILERR_NONE) + if (err != osd_file::error::NONE) return err; // get the size auto const size = file->size(); if (std::uint32_t(size) != size) - return FILERR_OUT_OF_MEMORY; + return osd_file::error::OUT_OF_MEMORY; // allocate memory data.resize(size); @@ -1245,11 +1239,11 @@ file_error core_file::load(char const *filename, dynamic_buffer &data) if (file->read(&data[0], size) != size) { data.clear(); - return FILERR_FAILURE; + return osd_file::error::FAILURE; } // close the file and return data - return FILERR_NONE; + return osd_file::error::NONE; } diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h index 7ec76af7cb5..e66545440c2 100644 --- a/src/lib/util/corefile.h +++ b/src/lib/util/corefile.h @@ -10,8 +10,8 @@ #pragma once -#ifndef __COREFILE_H__ -#define __COREFILE_H__ +#ifndef MAME_LIB_UTIL_COREFILE_H +#define MAME_LIB_UTIL_COREFILE_H #include "corestr.h" #include "coretmpl.h" @@ -23,7 +23,6 @@ namespace util { - /*************************************************************************** ADDITIONAL OPEN FLAGS ***************************************************************************/ @@ -49,22 +48,22 @@ public: // ----- file open/close ----- // open a file with the specified filename - static file_error open(const char *filename, std::uint32_t openflags, ptr &file); + static osd_file::error open(const char *filename, std::uint32_t openflags, ptr &file); // open a RAM-based "file" using the given data and length (read-only) - static file_error open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); + static osd_file::error open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); // open a RAM-based "file" using the given data and length (read-only), copying the data - static file_error open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); + static osd_file::error open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); // open a proxy "file" that forwards requests to another file object - static file_error open_proxy(core_file &file, ptr &proxy); + static osd_file::error open_proxy(core_file &file, ptr &proxy); // close an open file virtual ~core_file(); // enable/disable streaming file compression via zlib; level is 0 to disable compression, or up to 9 for max compression - virtual file_error compress(int level) = 0; + virtual osd_file::error compress(int level) = 0; // ----- file positioning ----- @@ -101,8 +100,8 @@ public: virtual const void *buffer() = 0; // open a file with the specified filename, read it into memory, and return a pointer - static file_error load(const char *filename, void **data, std::uint32_t &length); - static file_error load(const char *filename, dynamic_buffer &data); + static osd_file::error load(const char *filename, void **data, std::uint32_t &length); + static osd_file::error load(const char *filename, dynamic_buffer &data); // ----- file write ----- @@ -121,10 +120,10 @@ public: } // file truncation - virtual file_error truncate(std::uint64_t offset) = 0; + virtual osd_file::error truncate(std::uint64_t offset) = 0; // flush file buffers - virtual file_error flush() = 0; + virtual osd_file::error flush() = 0; protected: @@ -147,4 +146,4 @@ std::string core_filename_extract_base(const char *name, bool strip_extension = int core_filename_ends_with(const char *filename, const char *extension); -#endif /* __COREFILE_H__ */ +#endif // MAME_LIB_UTIL_COREFILE_H diff --git a/src/lib/util/un7z.cpp b/src/lib/util/un7z.cpp index b9cfa13eafe..80c0a6bcba6 100644 --- a/src/lib/util/un7z.cpp +++ b/src/lib/util/un7z.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** un7z.c @@ -10,364 +10,285 @@ // this is based on unzip.c, with modifications needed to use the 7zip library -#include "osdcore.h" #include "un7z.h" -#include <ctype.h> -#include <stdlib.h> -#include <zlib.h> +#include "corestr.h" +#include "unicode.h" +#include "lzma/C/7z.h" +#include "lzma/C/7zCrc.h" +#include "lzma/C/7zVersion.h" + +#include <array> +#include <cassert> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <utility> +#include <vector> + + +namespace { /*************************************************************************** - 7Zip Memory / File handling (adapted from 7zfile.c/.h and 7zalloc.c/.h) + TYPE DEFINITIONS ***************************************************************************/ -void *SZipAlloc(void *p, size_t size) +struct CSzFile { - if (size == 0) - return nullptr; + CSzFile() : currfpos(0), length(0), osdfile() {} - return malloc(size); -} + long currfpos; + std::uint64_t length; + osd_file::ptr osdfile; -void SZipFree(void *p, void *address) -{ - free(address); -} + WRes read(void *data, std::size_t &size) + { + if (!osdfile) + { + std::printf("un7z.c: called File_Read without file\n"); + return 1; + } + if (!size) return 0; + size_t originalSize = size; + std::uint32_t read_length; + //osd_file::error err = + osdfile->read(data, currfpos, originalSize, read_length); + size = read_length; + currfpos += read_length; -void File_Construct(CSzFile *p) -{ - p->_7z_osdfile = nullptr; -} + if (size == originalSize) + return 0; -static WRes File_Open(CSzFile *p, const char *name, int writeMode) -{ - /* we handle this ourselves ... */ - if (!p->_7z_osdfile) return 1; - else return 0; -} + return 0; + } + + WRes seek(Int64 &pos, ESzSeek origin) + { + if (origin == 0) currfpos = pos; + if (origin == 1) currfpos = currfpos + pos; + if (origin == 2) currfpos = length -pos; -WRes InFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 0); } -WRes OutFile_Open(CSzFile *p, const char *name) { return File_Open(p, name, 1); } + pos = currfpos; + return 0; + } +}; -WRes File_Close(CSzFile *p) +struct CFileInStream : public ISeekInStream, public CSzFile { - /* we handle this ourselves ... */ - return 0; -} + CFileInStream(); +}; -WRes File_Read(CSzFile *p, void *data, size_t *size) + +class m7z_file_impl { -// file_error err; - UINT32 read_length; +public: + typedef std::unique_ptr<m7z_file_impl> ptr; - if (!p->_7z_osdfile) + m7z_file_impl(const std::string &filename); + ~m7z_file_impl() { - printf("un7z.c: called File_Read without file\n"); - return 1; + if (m_out_buffer) + IAlloc_Free(&m_alloc_imp, m_out_buffer); + if (m_inited) + SzArEx_Free(&m_db, &m_alloc_imp); } - size_t originalSize = *size; - if (originalSize == 0) - return 0; + static ptr find_cached(const std::string &filename) + { + for (std::size_t cachenum = 0; cachenum < s_cache.size(); cachenum++) + { + // if we have a valid entry and it matches our filename, use it and remove from the cache + if (s_cache[cachenum] && (filename == s_cache[cachenum]->m_filename)) + { + ptr result; + std::swap(s_cache[cachenum], result); + return result; + } + } + return ptr(); + } + static void close(ptr &&archive); + static void cache_clear() + { + // clear call cache entries + for (std::size_t cachenum = 0; cachenum < s_cache.size(); s_cache[cachenum++].reset()) { } + } -// err = - osd_read( p->_7z_osdfile, data, p->_7z_currfpos, originalSize, &read_length ); - *size = read_length; - p->_7z_currfpos += read_length; + _7z_file::error initialize(); - if (*size == originalSize) - return 0; + int first_file() { return search(0, 0, std::string(), false, false); } + int next_file() { return (m_curr_file_idx < 0) ? -1 : search(m_curr_file_idx + 1, 0, std::string(), false, false); } - return 0; -} + int search(std::uint32_t crc) { return search(0, crc, std::string(), true, false); } + int search(const std::string &filename) { return search(0, 0, filename, false, true); } + int search(std::uint32_t crc, const std::string &filename) { return search(0, crc, filename, true, true); } -WRes File_Write(CSzFile *p, const void *data, size_t *size) -{ - return 0; -} + const std::string ¤t_name() const { return m_curr_name; } + std::uint64_t current_uncompressed_length() const { return m_curr_length; } + std::uint32_t current_crc() const { return m_curr_crc; } -WRes File_Seek(CSzFile *p, Int64 *pos, ESzSeek origin) -{ - if (origin==0) p->_7z_currfpos = *pos; - if (origin==1) p->_7z_currfpos = p->_7z_currfpos + *pos; - if (origin==2) p->_7z_currfpos = p->_7z_length - *pos; + _7z_file::error decompress(void *buffer, std::uint32_t length); - *pos = p->_7z_currfpos; +private: + m7z_file_impl(const m7z_file_impl &) = delete; + m7z_file_impl(m7z_file_impl &&) = delete; + m7z_file_impl &operator=(const m7z_file_impl &) = delete; + m7z_file_impl &operator=(m7z_file_impl &&) = delete; - return 0; -} + int search(int i, std::uint32_t search_crc, const std::string &search_filename, bool matchcrc, bool matchname); + void make_utf8_name(int index); -WRes File_GetLength(CSzFile *p, UInt64 *length) -{ - *length = p->_7z_length; - return 0; -} + static constexpr std::size_t CACHE_SIZE = 8; + static std::array<ptr, CACHE_SIZE> s_cache; -/* ---------- FileSeqInStream ---------- */ + const std::string m_filename; // copy of _7Z filename (for caching) -static SRes FileSeqInStream_Read(void *pp, void *buf, size_t *size) -{ - CFileSeqInStream *p = (CFileSeqInStream *)pp; - return File_Read(&p->file, buf, size) == 0 ? SZ_OK : SZ_ERROR_READ; -} + int m_curr_file_idx; // current file index + std::string m_curr_name; // current file name + std::uint64_t m_curr_length; // current file uncompressed length + std::uint32_t m_curr_crc; // current file crc -void FileSeqInStream_CreateVTable(CFileSeqInStream *p) -{ - p->s.Read = FileSeqInStream_Read; -} + std::vector<UInt16> m_utf16_buf; + std::vector<unicode_char> m_uchar_buf; + std::vector<char> m_utf8_buf; + CFileInStream m_archive_stream; + CLookToRead m_look_stream; + CSzArEx m_db; + ISzAlloc m_alloc_imp; + ISzAlloc m_alloc_temp_imp; + bool m_inited; -/* ---------- FileInStream ---------- */ + // cached stuff for solid blocks + UInt32 m_block_index; + Byte * m_out_buffer; + std::size_t m_out_buffer_size; -static SRes FileInStream_Read(void *pp, void *buf, size_t *size) -{ - CFileInStream *p = (CFileInStream *)pp; - return (File_Read(&p->file, buf, size) == 0) ? SZ_OK : SZ_ERROR_READ; -} +}; -static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin) -{ - CFileInStream *p = (CFileInStream *)pp; - return File_Seek(&p->file, pos, origin); -} -void FileInStream_CreateVTable(CFileInStream *p) +class m7z_file_wrapper : public _7z_file { - p->s.Read = FileInStream_Read; - p->s.Seek = FileInStream_Seek; -} +public: + m7z_file_wrapper(m7z_file_impl::ptr &&impl) : m_impl(std::move(impl)) { assert(m_impl); } + virtual ~m7z_file_wrapper() override { m7z_file_impl::close(std::move(m_impl)); } -/* ---------- FileOutStream ---------- */ + virtual int first_file() override { return m_impl->first_file(); } + virtual int next_file() override { return m_impl->next_file(); } -static size_t FileOutStream_Write(void *pp, const void *data, size_t size) -{ - CFileOutStream *p = (CFileOutStream *)pp; - File_Write(&p->file, data, &size); - return size; -} + virtual int search(std::uint32_t crc) override { return m_impl->search(crc); } + virtual int search(const std::string &filename) override { return m_impl->search(filename); } + virtual int search(std::uint32_t crc, const std::string &filename) override { return m_impl->search(crc, filename); } -void FileOutStream_CreateVTable(CFileOutStream *p) -{ - p->s.Write = FileOutStream_Write; -} + virtual const std::string ¤t_name() const override { return m_impl->current_name(); } + virtual std::uint64_t current_uncompressed_length() const override { return m_impl->current_uncompressed_length(); } + virtual std::uint32_t current_crc() const override { return m_impl->current_crc(); } -/*************************************************************************** - CONSTANTS -***************************************************************************/ + virtual error decompress(void *buffer, std::uint32_t length) override { return m_impl->decompress(buffer, length); } + +private: + m7z_file_impl::ptr m_impl; +}; -/* number of open files to cache */ -#define _7Z_CACHE_SIZE 8 /*************************************************************************** GLOBAL VARIABLES ***************************************************************************/ -static _7z_file *_7z_cache[_7Z_CACHE_SIZE]; - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ +std::array<m7z_file_impl::ptr, m7z_file_impl::CACHE_SIZE> m7z_file_impl::s_cache; -/* cache management */ -static void free__7z_file(_7z_file *_7z); /*************************************************************************** - _7Z FILE ACCESS + 7Zip Memory / File handling (adapted from 7zfile.c/.h and 7zalloc.c/.h) ***************************************************************************/ -/*------------------------------------------------- - _7z_file_open - opens a _7Z file for reading --------------------------------------------------*/ - -int _7z_search_crc_match(_7z_file *new_7z, UINT32 search_crc, const char* search_filename, int search_filename_length, bool matchcrc, bool matchname) -{ - UInt16 *temp = nullptr; - size_t tempSize = 0; - - for (int i = 0; i < new_7z->db.db.NumFiles; i++) - { - const CSzFileItem *f = new_7z->db.db.Files + i; - size_t len; - - len = SzArEx_GetFileNameUtf16(&new_7z->db, i, nullptr); - - // if it's a directory entry we don't care about it.. - if (f->IsDir) - continue; - - if (len > tempSize) - { - SZipFree(nullptr, temp); - tempSize = len; - temp = (UInt16 *)SZipAlloc(nullptr, tempSize * sizeof(temp[0])); - if (temp == nullptr) - { - return -1; // memory error - } - } - - bool crcmatch = false; - bool namematch = false; - - UINT64 size = f->Size; - UINT32 crc = f->Crc; - - /* Check for a name match */ - SzArEx_GetFileNameUtf16(&new_7z->db, i, temp); - - if (len == search_filename_length+1) - { - int j; - for (j=0;j<search_filename_length;j++) - { - UINT8 sn = search_filename[j]; - UINT16 zn = temp[j]; // these are utf16 - - // MAME filenames are always lowercase so be case insensitive - if ((zn>=0x41) && (zn<=0x5a)) zn+=0x20; - - if (sn != zn) break; - } - if (j==search_filename_length) namematch = true; - } - - - /* Check for a CRC match */ - if (crc==search_crc) crcmatch = true; - - bool found = false; - - if (matchcrc && matchname) - { - if (crcmatch && namematch) - found = true; - } - else if (matchcrc) - { - if (crcmatch) - found = true; - } - else if (matchname) - { - if (namematch) - found = true; - } +/* ---------- FileInStream ---------- */ - if (found) - { - // printf("found %S %d %08x %08x %08x %s %d\n", temp, len, crc, search_crc, size, search_filename, search_filename_length); - new_7z->curr_file_idx = i; - new_7z->uncompressed_length = size; - new_7z->crc = crc; +extern "C" { - SZipFree(nullptr, temp); - return i; - } - } +static void *SZipAlloc(void *p, std::size_t size) +{ + return (size == 0) ? nullptr : std::malloc(size); +} - SZipFree(nullptr, temp); - return -1; +static void SZipFree(void *p, void *address) +{ + std::free(address); } -_7z_error _7z_file_open(const char *filename, _7z_file **_7z) +static SRes FileInStream_Read(void *pp, void *buf, size_t *size) { - file_error err; - _7z_error _7zerr = _7ZERR_NONE; - + return (reinterpret_cast<CFileInStream *>(pp)->read(buf, *size) == 0) ? SZ_OK : SZ_ERROR_READ; +} - _7z_file *new_7z; - char *string; - int cachenum; +static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin) +{ + return reinterpret_cast<CFileInStream *>(pp)->seek(*pos, origin); +} - SRes res; +} // extern "C" - /* ensure we start with a NULL result */ - *_7z = nullptr; - /* see if we are in the cache, and reopen if so */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(_7z_cache); cachenum++) - { - _7z_file *cached = _7z_cache[cachenum]; +CFileInStream::CFileInStream() +{ + Read = &FileInStream_Read; + Seek = &FileInStream_Seek; +} - /* if we have a valid entry and it matches our filename, use it and remove from the cache */ - if (cached != nullptr && cached->filename != nullptr && strcmp(filename, cached->filename) == 0) - { - *_7z = cached; - _7z_cache[cachenum] = nullptr; - return _7ZERR_NONE; - } - } - /* allocate memory for the _7z_file structure */ - new_7z = (_7z_file *)malloc(sizeof(*new_7z)); - if (new_7z == nullptr) - return _7ZERR_OUT_OF_MEMORY; - memset(new_7z, 0, sizeof(*new_7z)); - new_7z->inited = false; - new_7z->archiveStream.file._7z_currfpos = 0; - err = osd_open(filename, OPEN_FLAG_READ, &new_7z->archiveStream.file._7z_osdfile, &new_7z->archiveStream.file._7z_length); - if (err != FILERR_NONE) - { - _7zerr = _7ZERR_FILE_ERROR; - goto error; - } +/*************************************************************************** + CACHE MANAGEMENT +***************************************************************************/ - new_7z->allocImp.Alloc = SZipAlloc; - new_7z->allocImp.Free = SZipFree; +m7z_file_impl::m7z_file_impl(const std::string &filename) + : m_filename(filename) + , m_curr_file_idx(-1) + , m_curr_name() + , m_curr_length(0) + , m_curr_crc(0) + , m_utf16_buf(128) + , m_uchar_buf(128) + , m_utf8_buf(512) + , m_inited(false) + , m_block_index(0xffffffff) // it can have any value before first call (if outBuffer = 0) + , m_out_buffer(nullptr) // it must be 0 before first call for each new archive + , m_out_buffer_size(0) // it can have any value before first call (if outBuffer = 0) +{ + m_alloc_imp.Alloc = SZipAlloc; + m_alloc_imp.Free = SZipFree; - new_7z->allocTempImp.Alloc = SZipAlloc; - new_7z->allocTempImp.Free = SZipFree; + m_alloc_temp_imp.Alloc = SZipAlloc; + m_alloc_temp_imp.Free = SZipFree; +} - if (InFile_Open(&new_7z->archiveStream.file, filename)) - { - _7zerr = _7ZERR_FILE_ERROR; - goto error; - } - FileInStream_CreateVTable(&new_7z->archiveStream); - LookToRead_CreateVTable(&new_7z->lookStream, False); +_7z_file::error m7z_file_impl::initialize() +{ + osd_file::error const err = osd_file::open(m_filename, OPEN_FLAG_READ, m_archive_stream.osdfile, m_archive_stream.length); + if (err != osd_file::error::NONE) + return _7z_file::error::FILE_ERROR; - new_7z->lookStream.realStream = &new_7z->archiveStream.s; - LookToRead_Init(&new_7z->lookStream); + LookToRead_CreateVTable(&m_look_stream, False); + m_look_stream.realStream = &m_archive_stream; + LookToRead_Init(&m_look_stream); CrcGenerateTable(); - SzArEx_Init(&new_7z->db); - new_7z->inited = true; + SzArEx_Init(&m_db); + m_inited = true; - res = SzArEx_Open(&new_7z->db, &new_7z->lookStream.s, &new_7z->allocImp, &new_7z->allocTempImp); + SRes const res = SzArEx_Open(&m_db, &m_look_stream.s, &m_alloc_imp, &m_alloc_temp_imp); if (res != SZ_OK) - { - _7zerr = _7ZERR_FILE_ERROR; - goto error; - } - - new_7z->blockIndex = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ - new_7z->outBuffer = nullptr; /* it must be 0 before first call for each new archive. */ - new_7z->outBufferSize = 0; /* it can have any value before first call (if outBuffer = 0) */ + return _7z_file::error::FILE_ERROR; - /* make a copy of the filename for caching purposes */ - string = (char *)malloc(strlen(filename) + 1); - if (string == nullptr) - { - _7zerr = _7ZERR_OUT_OF_MEMORY; - goto error; - } - strcpy(string, filename); - new_7z->filename = string; - *_7z = new_7z; - return _7ZERR_NONE; - -error: - free__7z_file(new_7z); - return _7zerr; + return _7z_file::error::NONE; } @@ -376,119 +297,187 @@ error: to the cache -------------------------------------------------*/ -void _7z_file_close(_7z_file *_7z) +void m7z_file_impl::close(ptr &&archive) { - int cachenum; + if (!archive) return; - /* close the open files */ - if (_7z->archiveStream.file._7z_osdfile != nullptr) - osd_close(_7z->archiveStream.file._7z_osdfile); - _7z->archiveStream.file._7z_osdfile = nullptr; + // close the open files + archive->m_archive_stream.osdfile.reset(); - /* find the first NULL entry in the cache */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(_7z_cache); cachenum++) - if (_7z_cache[cachenum] == nullptr) + // find the first NULL entry in the cache + std::size_t cachenum; + for (cachenum = 0; cachenum < s_cache.size(); cachenum++) + if (!s_cache[cachenum]) break; - /* if no room left in the cache, free the bottommost entry */ - if (cachenum == ARRAY_LENGTH(_7z_cache)) - free__7z_file(_7z_cache[--cachenum]); + // if no room left in the cache, free the bottommost entry + if (cachenum == s_cache.size()) + s_cache[--cachenum].reset(); - /* move everyone else down and place us at the top */ - if (cachenum != 0) - memmove(&_7z_cache[1], &_7z_cache[0], cachenum * sizeof(_7z_cache[0])); - _7z_cache[0] = _7z; + // move everyone else down and place us at the top + for ( ; cachenum > 0; cachenum--) + s_cache[cachenum] = std::move(s_cache[cachenum - 1]); + s_cache[0] = std::move(archive); } -/*------------------------------------------------- - _7z_file_cache_clear - clear the _7Z file - cache and free all memory --------------------------------------------------*/ - -void _7z_file_cache_clear(void) -{ - int cachenum; - - /* clear call cache entries */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(_7z_cache); cachenum++) - if (_7z_cache[cachenum] != nullptr) - { - free__7z_file(_7z_cache[cachenum]); - _7z_cache[cachenum] = nullptr; - } -} +/*************************************************************************** + 7Z FILE ACCESS +***************************************************************************/ /*------------------------------------------------- _7z_file_decompress - decompress a file from a _7Z into the target buffer -------------------------------------------------*/ -_7z_error _7z_file_decompress(_7z_file *new_7z, void *buffer, UINT32 length) +_7z_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length) { - file_error err; - SRes res; - int index = new_7z->curr_file_idx; - - /* make sure the file is open.. */ - if (new_7z->archiveStream.file._7z_osdfile==nullptr) + // make sure the file is open.. + if (!m_archive_stream.osdfile) { - new_7z->archiveStream.file._7z_currfpos = 0; - err = osd_open(new_7z->filename, OPEN_FLAG_READ, &new_7z->archiveStream.file._7z_osdfile, &new_7z->archiveStream.file._7z_length); - if (err != FILERR_NONE) - return _7ZERR_FILE_ERROR; + m_archive_stream.currfpos = 0; + osd_file::error const err = osd_file::open(m_filename, OPEN_FLAG_READ, m_archive_stream.osdfile, m_archive_stream.length); + if (err != osd_file::error::NONE) + return _7z_file::error::FILE_ERROR; } size_t offset = 0; - size_t outSizeProcessed = 0; + size_t out_size_processed = 0; - res = SzArEx_Extract(&new_7z->db, &new_7z->lookStream.s, index, - &new_7z->blockIndex, &new_7z->outBuffer, &new_7z->outBufferSize, - &offset, &outSizeProcessed, - &new_7z->allocImp, &new_7z->allocTempImp); + SRes const res = SzArEx_Extract( + &m_db, &m_look_stream.s, m_curr_file_idx, + &m_block_index, + &m_out_buffer, &m_out_buffer_size, + &offset, &out_size_processed, + &m_alloc_imp, &m_alloc_temp_imp); if (res != SZ_OK) - return _7ZERR_FILE_ERROR; + return _7z_file::error::FILE_ERROR; - memcpy(buffer, new_7z->outBuffer + offset, length); + std::memcpy(buffer, m_out_buffer + offset, length); - return _7ZERR_NONE; + return _7z_file::error::NONE; } +int m7z_file_impl::search(int i, std::uint32_t search_crc, const std::string &search_filename, bool matchcrc, bool matchname) +{ + for ( ; i < m_db.db.NumFiles; i++) + { + const CSzFileItem &f(m_db.db.Files[i]); -/*************************************************************************** - CACHE MANAGEMENT -***************************************************************************/ + // if it's a directory entry we don't care about it.. + if (!f.IsDir) + { + make_utf8_name(i); + const std::uint64_t size(f.Size); + const std::uint32_t crc(f.Crc); + const bool crcmatch(crc == search_crc); + const bool namematch(core_stricmp(search_filename.c_str(), &m_utf8_buf[0]) == 0); + + const bool found = (!matchcrc || crcmatch) && (!matchname || namematch); + if (found) + { + m_curr_file_idx = i; + m_curr_name = &m_utf8_buf[0]; + m_curr_length = size; + m_curr_crc = crc; -/*------------------------------------------------- - free__7z_file - free all the data for a - _7z_file --------------------------------------------------*/ + return i; + } + } + } + + return -1; +} -/** - * @fn static void free__7z_file(_7z_file *_7z) - * - * @brief Free 7z file. - * - * @param [in,out] _7z If non-null, the 7z. - */ -static void free__7z_file(_7z_file *_7z) +void m7z_file_impl::make_utf8_name(int index) { - if (_7z != nullptr) + std::size_t len, out_pos; + + len = SzArEx_GetFileNameUtf16(&m_db, index, nullptr); + m_utf16_buf.resize((std::max<std::size_t>)(m_utf16_buf.size(), len)); + SzArEx_GetFileNameUtf16(&m_db, index, &m_utf16_buf[0]); + + m_uchar_buf.resize((std::max<std::size_t>)(m_uchar_buf.size(), len)); + out_pos = 0; + for (std::size_t in_pos = 0; in_pos < (len - 1); ) + { + const int used = uchar_from_utf16(&m_uchar_buf[out_pos], &m_utf16_buf[in_pos], len - in_pos); + if (used < 0) + { + in_pos++; + m_uchar_buf[out_pos++] = 0x00fffd; // Unicode REPLACEMENT CHARACTER + } + else + { + assert(used > 0); + in_pos += used; + out_pos++; + } + } + len = out_pos; + + m_utf8_buf.resize((std::max<std::size_t>)(m_utf8_buf.size(), 4 * len + 1)); + out_pos = 0; + for (std::size_t in_pos = 0; in_pos < len; in_pos++) { - if (_7z->archiveStream.file._7z_osdfile != nullptr) - osd_close(_7z->archiveStream.file._7z_osdfile); - if (_7z->filename != nullptr) - free((void *)_7z->filename); + int produced = utf8_from_uchar(&m_utf8_buf[out_pos], m_utf8_buf.size() - out_pos, m_uchar_buf[in_pos]); + if (produced < 0) + produced = utf8_from_uchar(&m_utf8_buf[out_pos], m_utf8_buf.size() - out_pos, 0x00fffd); + if (produced >= 0) + out_pos += produced; + assert(out_pos < m_utf8_buf.size()); + } + m_utf16_buf[out_pos++] = '\0'; +} + +} // anonymous namespace + + +_7z_file::~_7z_file() +{ +} + +_7z_file::error _7z_file::open(const std::string &filename, ptr &result) +{ + // ensure we start with a NULL result + result.reset(); - if (_7z->outBuffer) IAlloc_Free(&_7z->allocImp, _7z->outBuffer); - if (_7z->inited) SzArEx_Free(&_7z->db, &_7z->allocImp); + // see if we are in the cache, and reopen if so + m7z_file_impl::ptr newimpl(m7z_file_impl::find_cached(filename)); + if (!newimpl) + { + // allocate memory for the 7z file structure + try { newimpl = std::make_unique<m7z_file_impl>(filename); } + catch (...) { return error::OUT_OF_MEMORY; } + error const err = newimpl->initialize(); + if (err != error::NONE) return err; + } - free(_7z); + try + { + result = std::make_unique<m7z_file_wrapper>(std::move(newimpl)); + return error::NONE; + } + catch (...) + { + m7z_file_impl::close(std::move(newimpl)); + return error::OUT_OF_MEMORY; } } + + +/*------------------------------------------------- + _7z_file_cache_clear - clear the _7Z file + cache and free all memory +-------------------------------------------------*/ + +void _7z_file::cache_clear() +{ + m7z_file_impl::cache_clear(); +} diff --git a/src/lib/util/un7z.h b/src/lib/util/un7z.h index d2c0c9f098a..d2d2e18e25f 100644 --- a/src/lib/util/un7z.h +++ b/src/lib/util/un7z.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** un7z.h @@ -12,132 +12,72 @@ #pragma once -#ifndef __UN_7Z_H__ -#define __UN_7Z_H__ +#ifndef MAME_LIB_UTIL_UN7Z_H +#define MAME_LIB_UTIL_UN7Z_H #include "osdcore.h" -#include "lzma/C/7z.h" -#include "lzma/C/7zCrc.h" -#include "lzma/C/7zVersion.h" - - -void *SZipAlloc(void *p, size_t size); -void SZipFree(void *p, void *address); -void *SZipAllocTemp(void *p, size_t size); -void SZipFreeTemp(void *p, void *address); - -struct CSzFile -{ - long _7z_currfpos; - UINT64 _7z_length; - osd_file * _7z_osdfile; /* OSD file handle */ - -}; - - -struct CFileSeqInStream -{ - ISeqInStream s; - CSzFile file; -}; - -void FileSeqInStream_CreateVTable(CFileSeqInStream *p); - - -struct CFileInStream -{ - ISeekInStream s; - CSzFile file; -}; - -void FileInStream_CreateVTable(CFileInStream *p); - - -struct CFileOutStream -{ - ISeqOutStream s; - CSzFile file; -} ; - -void FileOutStream_CreateVTable(CFileOutStream *p); - - - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - - -/* Error types */ -enum _7z_error -{ - _7ZERR_NONE = 0, - _7ZERR_OUT_OF_MEMORY, - _7ZERR_FILE_ERROR, - _7ZERR_BAD_SIGNATURE, - _7ZERR_DECOMPRESS_ERROR, - _7ZERR_FILE_TRUNCATED, - _7ZERR_FILE_CORRUPT, - _7ZERR_UNSUPPORTED, - _7ZERR_BUFFER_TOO_SMALL -}; - +#include <cstdint> +#include <memory> +#include <string> /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ -/* describes an open _7Z file */ -struct _7z_file +// describes an open _7Z file +class _7z_file { - const char * filename; /* copy of _7Z filename (for caching) */ - - int curr_file_idx; /* current file index */ - UINT64 uncompressed_length; /* current file uncompressed length */ - UINT64 crc; /* current file crc */ - - CFileInStream archiveStream; - CLookToRead lookStream; - CSzArEx db; - SRes res; - ISzAlloc allocImp; - ISzAlloc allocTempImp; - bool inited; - - // cached stuff for solid blocks - UInt32 blockIndex;// = 0xFFFFFFFF; /* it can have any value before first call (if outBuffer = 0) */ - Byte *outBuffer;// = 0; /* it must be 0 before first call for each new archive. */ - size_t outBufferSize;// = 0; /* it can have any value before first call (if outBuffer = 0) */ -}; +public: + // Error types + enum class error + { + NONE = 0, + OUT_OF_MEMORY, + FILE_ERROR, + BAD_SIGNATURE, + DECOMPRESS_ERROR, + FILE_TRUNCATED, + FILE_CORRUPT, + UNSUPPORTED, + BUFFER_TOO_SMALL + }; + typedef std::unique_ptr<_7z_file> ptr; -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ + virtual ~_7z_file(); -/* ----- _7Z file access ----- */ + /* ----- 7Z file access ----- */ -/* open a _7Z file and parse its central directory */ -_7z_error _7z_file_open(const char *filename, _7z_file **_7z); + // open a 7Z file and parse its central directory + static error open(const std::string &filename, ptr &result); -/* close a _7Z file (may actually be left open due to caching) */ -void _7z_file_close(_7z_file *_7z); + // clear out all open 7Z files from the cache + static void cache_clear(); -/* clear out all open _7Z files from the cache */ -void _7z_file_cache_clear(void); + /* ----- contained file access ----- */ -/* ----- contained file access ----- */ + // iterating over files + virtual int first_file() = 0; + virtual int next_file() = 0; -/* find a file index by crc, filename or both */ -int _7z_search_crc_match(_7z_file *new_7z, UINT32 crc, const char *search_filename, int search_filename_length, bool matchcrc, bool matchname); + // find a file index by crc, filename or both + virtual int search(std::uint32_t crc) = 0; + virtual int search(const std::string &filename) = 0; + virtual int search(std::uint32_t crc, const std::string &filename) = 0; -/* decompress the most recently found file in the _7Z */ -_7z_error _7z_file_decompress(_7z_file *_7z, void *buffer, UINT32 length); + // information on most recently found file + virtual const std::string ¤t_name() const = 0; + virtual std::uint64_t current_uncompressed_length() const = 0; + virtual std::uint32_t current_crc() const = 0; + // decompress the most recently found file in the _7Z + virtual error decompress(void *buffer, std::uint32_t length) = 0; + +}; -#endif /* __UN_7Z_H__ */ +#endif // MAME_LIB_UTIL_UN7Z_H diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp index 876d8ae90c5..321f4e0b69f 100644 --- a/src/lib/util/unzip.cpp +++ b/src/lib/util/unzip.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** unzip.c @@ -11,19 +11,23 @@ #include "osdcore.h" #include "unzip.h" -#include <ctype.h> -#include <stdlib.h> +#include <algorithm> +#include <array> +#include <cassert> +#include <cstring> +#include <cstdlib> +#include <utility> +#include <vector> + #include <zlib.h> +namespace { /*************************************************************************** CONSTANTS ***************************************************************************/ -/* number of open files to cache */ -#define ZIP_CACHE_SIZE 8 - /* offsets in end of central directory structure */ #define ZIPESIG 0x00 #define ZIPEDSK 0x04 @@ -95,6 +99,162 @@ /*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +class zip_file_impl +{ +public: + typedef std::unique_ptr<zip_file_impl> ptr; + + zip_file_impl(const std::string &filename) + : m_filename(filename) + , m_file() + , m_length(0) + , m_ecd() + , m_cd() + , m_cd_pos(0) + , m_header() + , m_buffer() + { + std::memset(&m_header, 0, sizeof(m_header)); + std::fill(m_buffer.begin(), m_buffer.end(), 0); + } + + static ptr find_cached(const std::string &filename) + { + for (std::size_t cachenum = 0; cachenum < s_cache.size(); cachenum++) + { + // if we have a valid entry and it matches our filename, use it and remove from the cache + if (s_cache[cachenum] && (filename == s_cache[cachenum]->m_filename)) + { + ptr result; + std::swap(s_cache[cachenum], result); + return result; + } + } + return ptr(); + } + static void close(ptr &&zip); + static void cache_clear() + { + // clear call cache entries + for (std::size_t cachenum = 0; cachenum < s_cache.size(); s_cache[cachenum++].reset()) { } + } + + zip_file::error initialize() + { + // read ecd data + auto const ziperr = read_ecd(); + if (ziperr != zip_file::error::NONE) + return ziperr; + + // verify that we can work with this zipfile (no disk spanning allowed) + if ((m_ecd.disk_number != m_ecd.cd_start_disk_number) || (m_ecd.cd_disk_entries != m_ecd.cd_total_entries)) + return zip_file::error::UNSUPPORTED; + + // allocate memory for the central directory + try { m_cd.resize(m_ecd.cd_size + 1); } + catch (...) { return zip_file::error::OUT_OF_MEMORY; } + + // read the central directory + std::uint32_t read_length; + auto const filerr = m_file->read(&m_cd[0], m_ecd.cd_start_disk_offset, m_ecd.cd_size, read_length); + if ((filerr != osd_file::error::NONE) || (read_length != m_ecd.cd_size)) + return (filerr == osd_file::error::NONE) ? zip_file::error::FILE_TRUNCATED : zip_file::error::FILE_ERROR; + + return zip_file::error::NONE; + } + + // contained file access + const zip_file::file_header *first_file(); + const zip_file::file_header *next_file(); + zip_file::error decompress(void *buffer, std::uint32_t length); + +private: + zip_file_impl(const zip_file_impl &) = delete; + zip_file_impl(zip_file_impl &&) = delete; + zip_file_impl &operator=(const zip_file_impl &) = delete; + zip_file_impl &operator=(zip_file_impl &&) = delete; + + zip_file::error reopen() + { + if (!m_file) + { + auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, m_file, m_length); + if (filerr != osd_file::error::NONE) + return zip_file::error::FILE_ERROR; + } + return zip_file::error::NONE; + } + + // ZIP file parsing + zip_file::error read_ecd(); + zip_file::error get_compressed_data_offset(std::uint64_t &offset); + + // decompression interfaces + zip_file::error decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length); + zip_file::error decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length); + + struct file_header_int : zip_file::file_header + { + std::uint8_t * raw; // pointer to the raw data + std::uint32_t rawlength; // length of the raw data + std::uint8_t saved; // saved byte from after filename + }; + + // contains extracted end of central directory information + struct ecd + { + std::uint32_t signature; // end of central dir signature + std::uint16_t disk_number; // number of this disk + std::uint16_t cd_start_disk_number; // number of the disk with the start of the central directory + std::uint16_t cd_disk_entries; // total number of entries in the central directory on this disk + std::uint16_t cd_total_entries; // total number of entries in the central directory + std::uint32_t cd_size; // size of the central directory + std::uint32_t cd_start_disk_offset; // offset of start of central directory with respect to the starting disk number + std::uint16_t comment_length; // .ZIP file comment length + const char * comment; // .ZIP file comment + + std::unique_ptr<std::uint8_t []> raw; // pointer to the raw data + std::uint32_t rawlength; // length of the raw data + }; + + static constexpr std::size_t DECOMPRESS_BUFSIZE = 16384; + static constexpr std::size_t CACHE_SIZE = 8; // number of open files to cache + static std::array<ptr, CACHE_SIZE> s_cache; + + const std::string m_filename; // copy of ZIP filename (for caching) + osd_file::ptr m_file; // OSD file handle + std::uint64_t m_length; // length of zip file + + ecd m_ecd; // end of central directory + + std::vector<std::uint8_t> m_cd; // central directory raw data + std::uint32_t m_cd_pos; // position in central directory + file_header_int m_header; // current file header + + std::array<std::uint8_t, DECOMPRESS_BUFSIZE> m_buffer; // buffer for decompression +}; + + +class zip_file_wrapper : public zip_file +{ +public: + zip_file_wrapper(zip_file_impl::ptr &&impl) : m_impl(std::move(impl)) { assert(m_impl); } + virtual ~zip_file_wrapper() { zip_file_impl::close(std::move(m_impl)); } + + virtual const file_header *first_file() override { return m_impl->first_file(); } + virtual const file_header *next_file() override { return m_impl->next_file(); } + virtual error decompress(void *buffer, std::uint32_t length) override { return m_impl->decompress(buffer, length); } + +private: + zip_file_impl::ptr m_impl; +}; + + + +/*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ @@ -108,7 +268,7 @@ * @return The word. */ -static inline UINT16 read_word(UINT8 *buf) +inline UINT16 read_word(UINT8 *buf) { return (buf[1] << 8) | buf[0]; } @@ -123,7 +283,7 @@ static inline UINT16 read_word(UINT8 *buf) * @return The double word. */ -static inline UINT32 read_dword(UINT8 *buf) +inline UINT32 read_dword(UINT8 *buf) { return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; } @@ -135,130 +295,8 @@ static inline UINT32 read_dword(UINT8 *buf) ***************************************************************************/ /** @brief The zip cache[ zip cache size]. */ -static zip_file *zip_cache[ZIP_CACHE_SIZE]; - - - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -/* cache management */ -static void free_zip_file(zip_file *zip); +std::array<zip_file_impl::ptr, zip_file_impl::CACHE_SIZE> zip_file_impl::s_cache; -/* ZIP file parsing */ -static zip_error read_ecd(zip_file *zip); -static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset); - -/* decompression interfaces */ -static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buffer, UINT32 length); -static zip_error decompress_data_type_8(zip_file *zip, UINT64 offset, void *buffer, UINT32 length); - - - -/*************************************************************************** - ZIP FILE ACCESS -***************************************************************************/ - -/*------------------------------------------------- - zip_file_open - opens a ZIP file for reading --------------------------------------------------*/ - -/** - * @fn zip_error zip_file_open(const char *filename, zip_file **zip) - * - * @brief Queries if a given zip file open. - * - * @param filename Filename of the file. - * @param [in,out] zip If non-null, the zip. - * - * @return A zip_error. - */ - -zip_error zip_file_open(const char *filename, zip_file **zip) -{ - zip_error ziperr = ZIPERR_NONE; - file_error filerr; - UINT32 read_length; - zip_file *newzip; - char *string; - int cachenum; - - /* ensure we start with a NULL result */ - *zip = nullptr; - - /* see if we are in the cache, and reopen if so */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) - { - zip_file *cached = zip_cache[cachenum]; - - /* if we have a valid entry and it matches our filename, use it and remove from the cache */ - if (cached != nullptr && cached->filename != nullptr && strcmp(filename, cached->filename) == 0) - { - *zip = cached; - zip_cache[cachenum] = nullptr; - return ZIPERR_NONE; - } - } - - /* allocate memory for the zip_file structure */ - newzip = (zip_file *)malloc(sizeof(*newzip)); - if (newzip == nullptr) - return ZIPERR_OUT_OF_MEMORY; - memset(newzip, 0, sizeof(*newzip)); - - /* open the file */ - filerr = osd_open(filename, OPEN_FLAG_READ, &newzip->file, &newzip->length); - if (filerr != FILERR_NONE) - { - ziperr = ZIPERR_FILE_ERROR; - goto error; - } - - /* read ecd data */ - ziperr = read_ecd(newzip); - if (ziperr != ZIPERR_NONE) - goto error; - - /* verify that we can work with this zipfile (no disk spanning allowed) */ - if (newzip->ecd.disk_number != newzip->ecd.cd_start_disk_number || newzip->ecd.cd_disk_entries != newzip->ecd.cd_total_entries) - { - ziperr = ZIPERR_UNSUPPORTED; - goto error; - } - - /* allocate memory for the central directory */ - newzip->cd = (UINT8 *)malloc(newzip->ecd.cd_size + 1); - if (newzip->cd == nullptr) - { - ziperr = ZIPERR_OUT_OF_MEMORY; - goto error; - } - - /* read the central directory */ - filerr = osd_read(newzip->file, newzip->cd, newzip->ecd.cd_start_disk_offset, newzip->ecd.cd_size, &read_length); - if (filerr != FILERR_NONE || read_length != newzip->ecd.cd_size) - { - ziperr = (filerr == FILERR_NONE) ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR; - goto error; - } - - /* make a copy of the filename for caching purposes */ - string = (char *)malloc(strlen(filename) + 1); - if (string == nullptr) - { - ziperr = ZIPERR_OUT_OF_MEMORY; - goto error; - } - strcpy(string, filename); - newzip->filename = string; - *zip = newzip; - return ZIPERR_NONE; - -error: - free_zip_file(newzip); - return ziperr; -} /*------------------------------------------------- @@ -274,57 +312,30 @@ error: * @param [in,out] zip If non-null, the zip. */ -void zip_file_close(zip_file *zip) +void zip_file_impl::close(ptr &&zip) { - int cachenum; + if (!zip) return; - /* close the open files */ - if (zip->file != nullptr) - osd_close(zip->file); - zip->file = nullptr; + // close the open files + zip->m_file.reset(); - /* find the first NULL entry in the cache */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) - if (zip_cache[cachenum] == nullptr) + // find the first NULL entry in the cache + std::size_t cachenum; + for (cachenum = 0; cachenum < s_cache.size(); cachenum++) + if (!s_cache[cachenum]) break; - /* if no room left in the cache, free the bottommost entry */ - if (cachenum == ARRAY_LENGTH(zip_cache)) - free_zip_file(zip_cache[--cachenum]); - - /* move everyone else down and place us at the top */ - if (cachenum != 0) - memmove(&zip_cache[1], &zip_cache[0], cachenum * sizeof(zip_cache[0])); - zip_cache[0] = zip; -} - - -/*------------------------------------------------- - zip_file_cache_clear - clear the ZIP file - cache and free all memory --------------------------------------------------*/ - -/** - * @fn void zip_file_cache_clear(void) - * - * @brief Zip file cache clear. - */ - -void zip_file_cache_clear(void) -{ - int cachenum; + // if no room left in the cache, free the bottommost entry + if (cachenum == s_cache.size()) + s_cache[--cachenum].reset(); - /* clear call cache entries */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) - if (zip_cache[cachenum] != nullptr) - { - free_zip_file(zip_cache[cachenum]); - zip_cache[cachenum] = nullptr; - } + // move everyone else down and place us at the top + for ( ; cachenum > 0; cachenum--) + s_cache[cachenum] = std::move(s_cache[cachenum - 1]); + s_cache[0] = std::move(zip); } - /*************************************************************************** CONTAINED FILE ACCESS ***************************************************************************/ @@ -344,11 +355,11 @@ void zip_file_cache_clear(void) * @return null if it fails, else a zip_file_header*. */ -const zip_file_header *zip_file_first_file(zip_file *zip) +const zip_file::file_header *zip_file_impl::first_file() { /* reset the position and go from there */ - zip->cd_pos = 0; - return zip_file_next_file(zip); + m_cd_pos = 0; + return next_file(); } @@ -367,55 +378,55 @@ const zip_file_header *zip_file_first_file(zip_file *zip) * @return null if it fails, else a zip_file_header*. */ -const zip_file_header *zip_file_next_file(zip_file *zip) +const zip_file::file_header *zip_file_impl::next_file() { - /* fix up any modified data */ - if (zip->header.raw != nullptr) + // fix up any modified data + if (m_header.raw) { - zip->header.raw[ZIPCFN + zip->header.filename_length] = zip->header.saved; - zip->header.raw = nullptr; + m_header.raw[ZIPCFN + m_header.filename_length] = m_header.saved; + m_header.raw = nullptr; } - /* if we're at or past the end, we're done */ - if (zip->cd_pos >= zip->ecd.cd_size) + // if we're at or past the end, we're done + if (m_cd_pos >= m_ecd.cd_size) return nullptr; - /* extract file header info */ - zip->header.raw = zip->cd + zip->cd_pos; - zip->header.rawlength = ZIPCFN; - zip->header.signature = read_dword(zip->header.raw + ZIPCENSIG); - zip->header.version_created = read_word (zip->header.raw + ZIPCVER); - zip->header.version_needed = read_word (zip->header.raw + ZIPCVXT); - zip->header.bit_flag = read_word (zip->header.raw + ZIPCFLG); - zip->header.compression = read_word (zip->header.raw + ZIPCMTHD); - zip->header.file_time = read_word (zip->header.raw + ZIPCTIM); - zip->header.file_date = read_word (zip->header.raw + ZIPCDAT); - zip->header.crc = read_dword(zip->header.raw + ZIPCCRC); - zip->header.compressed_length = read_dword(zip->header.raw + ZIPCSIZ); - zip->header.uncompressed_length = read_dword(zip->header.raw + ZIPCUNC); - zip->header.filename_length = read_word (zip->header.raw + ZIPCFNL); - zip->header.extra_field_length = read_word (zip->header.raw + ZIPCXTL); - zip->header.file_comment_length = read_word (zip->header.raw + ZIPCCML); - zip->header.start_disk_number = read_word (zip->header.raw + ZIPDSK); - zip->header.internal_attributes = read_word (zip->header.raw + ZIPINT); - zip->header.external_attributes = read_dword(zip->header.raw + ZIPEXT); - zip->header.local_header_offset = read_dword(zip->header.raw + ZIPOFST); - zip->header.filename = (char *)zip->header.raw + ZIPCFN; - - /* make sure we have enough data */ - zip->header.rawlength += zip->header.filename_length; - zip->header.rawlength += zip->header.extra_field_length; - zip->header.rawlength += zip->header.file_comment_length; - if (zip->cd_pos + zip->header.rawlength > zip->ecd.cd_size) + // extract file header info + m_header.raw = &m_cd[0] + m_cd_pos; + m_header.rawlength = ZIPCFN; + m_header.signature = read_dword(m_header.raw + ZIPCENSIG); + m_header.version_created = read_word (m_header.raw + ZIPCVER); + m_header.version_needed = read_word (m_header.raw + ZIPCVXT); + m_header.bit_flag = read_word (m_header.raw + ZIPCFLG); + m_header.compression = read_word (m_header.raw + ZIPCMTHD); + m_header.file_time = read_word (m_header.raw + ZIPCTIM); + m_header.file_date = read_word (m_header.raw + ZIPCDAT); + m_header.crc = read_dword(m_header.raw + ZIPCCRC); + m_header.compressed_length = read_dword(m_header.raw + ZIPCSIZ); + m_header.uncompressed_length = read_dword(m_header.raw + ZIPCUNC); + m_header.filename_length = read_word (m_header.raw + ZIPCFNL); + m_header.extra_field_length = read_word (m_header.raw + ZIPCXTL); + m_header.file_comment_length = read_word (m_header.raw + ZIPCCML); + m_header.start_disk_number = read_word (m_header.raw + ZIPDSK); + m_header.internal_attributes = read_word (m_header.raw + ZIPINT); + m_header.external_attributes = read_dword(m_header.raw + ZIPEXT); + m_header.local_header_offset = read_dword(m_header.raw + ZIPOFST); + m_header.filename = reinterpret_cast<const char *>(m_header.raw + ZIPCFN); + + // make sure we have enough data + m_header.rawlength += m_header.filename_length; + m_header.rawlength += m_header.extra_field_length; + m_header.rawlength += m_header.file_comment_length; + if (m_cd_pos + m_header.rawlength > m_ecd.cd_size) return nullptr; - /* NULL terminate the filename */ - zip->header.saved = zip->header.raw[ZIPCFN + zip->header.filename_length]; - zip->header.raw[ZIPCFN + zip->header.filename_length] = 0; + // NULL terminate the filename + m_header.saved = m_header.raw[ZIPCFN + m_header.filename_length]; + m_header.raw[ZIPCFN + m_header.filename_length] = 0; - /* advance the position */ - zip->cd_pos += zip->header.rawlength; - return &zip->header; + // advance the position + m_cd_pos += m_header.rawlength; + return &m_header; } @@ -436,38 +447,38 @@ const zip_file_header *zip_file_next_file(zip_file *zip) * @return A zip_error. */ -zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length) +zip_file::error zip_file_impl::decompress(void *buffer, UINT32 length) { - zip_error ziperr; - UINT64 offset; + zip_file::error ziperr; + std::uint64_t offset; - /* if we don't have enough buffer, error */ - if (length < zip->header.uncompressed_length) - return ZIPERR_BUFFER_TOO_SMALL; + // if we don't have enough buffer, error + if (length < m_header.uncompressed_length) + return zip_file::error::BUFFER_TOO_SMALL; - /* make sure the info in the header aligns with what we know */ - if (zip->header.start_disk_number != zip->ecd.disk_number) - return ZIPERR_UNSUPPORTED; + // make sure the info in the header aligns with what we know + if (m_header.start_disk_number != m_ecd.disk_number) + return zip_file::error::UNSUPPORTED; - /* get the compressed data offset */ - ziperr = get_compressed_data_offset(zip, &offset); - if (ziperr != ZIPERR_NONE) + // get the compressed data offset + ziperr = get_compressed_data_offset(offset); + if (ziperr != zip_file::error::NONE) return ziperr; - /* handle compression types */ - switch (zip->header.compression) + // handle compression types + switch (m_header.compression) { - case 0: - ziperr = decompress_data_type_0(zip, offset, buffer, length); - break; + case 0: + ziperr = decompress_data_type_0(offset, buffer, length); + break; - case 8: - ziperr = decompress_data_type_8(zip, offset, buffer, length); - break; + case 8: + ziperr = decompress_data_type_8(offset, buffer, length); + break; - default: - ziperr = ZIPERR_UNSUPPORTED; - break; + default: + ziperr = zip_file::error::UNSUPPORTED; + break; } return ziperr; } @@ -475,41 +486,6 @@ zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length) /*************************************************************************** - CACHE MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - free_zip_file - free all the data for a - zip_file --------------------------------------------------*/ - -/** - * @fn static void free_zip_file(zip_file *zip) - * - * @brief Free zip file. - * - * @param [in,out] zip If non-null, the zip. - */ - -static void free_zip_file(zip_file *zip) -{ - if (zip != nullptr) - { - if (zip->file != nullptr) - osd_close(zip->file); - if (zip->filename != nullptr) - free((void *)zip->filename); - if (zip->ecd.raw != nullptr) - free(zip->ecd.raw); - if (zip->cd != nullptr) - free(zip->cd); - free(zip); - } -} - - - -/*************************************************************************** ZIP FILE PARSING ***************************************************************************/ @@ -527,72 +503,69 @@ static void free_zip_file(zip_file *zip) * @return The ecd. */ -static zip_error read_ecd(zip_file *zip) +zip_file::error zip_file_impl::read_ecd() { - UINT32 buflen = 1024; - UINT8 *buffer; + // make sure the file handle is open + auto const ziperr = reopen(); + if (ziperr != zip_file::error::NONE) + return ziperr; - /* we may need multiple tries */ + // we may need multiple tries + std::uint32_t buflen = 1024; while (buflen < 65536) { - file_error error; - UINT32 read_length; - INT32 offset; - - /* max out the buffer length at the size of the file */ - if (buflen > zip->length) - buflen = zip->length; - - /* allocate buffer */ - buffer = (UINT8 *)malloc(buflen + 1); - if (buffer == nullptr) - return ZIPERR_OUT_OF_MEMORY; - - /* read in one buffers' worth of data */ - error = osd_read(zip->file, buffer, zip->length - buflen, buflen, &read_length); - if (error != FILERR_NONE || read_length != buflen) - { - free(buffer); - return ZIPERR_FILE_ERROR; - } - - /* find the ECD signature */ + // max out the buffer length at the size of the file + if (buflen > m_length) + buflen = m_length; + + // allocate buffer + std::unique_ptr<std::uint8_t []> buffer; + try { buffer.reset(new std::uint8_t[buflen + 1]); } + catch (...) { return zip_file::error::OUT_OF_MEMORY; } + + // read in one buffers' worth of data + std::uint32_t read_length; + auto const error = m_file->read(&buffer[0], m_length - buflen, buflen, read_length); + if (error != osd_file::error::NONE || read_length != buflen) + return zip_file::error::FILE_ERROR; + + // find the ECD signature + std::int32_t offset; for (offset = buflen - 22; offset >= 0; offset--) if (buffer[offset + 0] == 'P' && buffer[offset + 1] == 'K' && buffer[offset + 2] == 0x05 && buffer[offset + 3] == 0x06) break; - /* if we found it, fill out the data */ + // if we found it, fill out the data if (offset >= 0) { - /* reuse the buffer as our ECD buffer */ - zip->ecd.raw = buffer; - zip->ecd.rawlength = buflen - offset; + // reuse the buffer as our ECD buffer + m_ecd.raw = std::move(buffer); + m_ecd.rawlength = buflen - offset; /* append a NULL terminator to the comment */ - memmove(&buffer[0], &buffer[offset], zip->ecd.rawlength); - zip->ecd.raw[zip->ecd.rawlength] = 0; + memmove(&m_ecd.raw[0], &m_ecd.raw[offset], m_ecd.rawlength); + m_ecd.raw[m_ecd.rawlength] = 0; /* extract ecd info */ - zip->ecd.signature = read_dword(zip->ecd.raw + ZIPESIG); - zip->ecd.disk_number = read_word (zip->ecd.raw + ZIPEDSK); - zip->ecd.cd_start_disk_number = read_word (zip->ecd.raw + ZIPECEN); - zip->ecd.cd_disk_entries = read_word (zip->ecd.raw + ZIPENUM); - zip->ecd.cd_total_entries = read_word (zip->ecd.raw + ZIPECENN); - zip->ecd.cd_size = read_dword(zip->ecd.raw + ZIPECSZ); - zip->ecd.cd_start_disk_offset = read_dword(zip->ecd.raw + ZIPEOFST); - zip->ecd.comment_length = read_word (zip->ecd.raw + ZIPECOML); - zip->ecd.comment = (const char *)(zip->ecd.raw + ZIPECOM); - return ZIPERR_NONE; + m_ecd.signature = read_dword(&m_ecd.raw[ZIPESIG]); + m_ecd.disk_number = read_word (&m_ecd.raw[ZIPEDSK]); + m_ecd.cd_start_disk_number = read_word (&m_ecd.raw[ZIPECEN]); + m_ecd.cd_disk_entries = read_word (&m_ecd.raw[ZIPENUM]); + m_ecd.cd_total_entries = read_word (&m_ecd.raw[ZIPECENN]); + m_ecd.cd_size = read_dword(&m_ecd.raw[ZIPECSZ]); + m_ecd.cd_start_disk_offset = read_dword(&m_ecd.raw[ZIPEOFST]); + m_ecd.comment_length = read_word (&m_ecd.raw[ZIPECOML]); + m_ecd.comment = reinterpret_cast<const char *>(&m_ecd.raw[ZIPECOM]); + return zip_file::error::NONE; } - /* didn't find it; free this buffer and expand our search */ - free(buffer); - if (buflen < zip->length) + // didn't find it; free this buffer and expand our search + if (buflen < m_length) buflen *= 2; else - return ZIPERR_BAD_SIGNATURE; + return zip_file::error::BAD_SIGNATURE; } - return ZIPERR_OUT_OF_MEMORY; + return zip_file::error::OUT_OF_MEMORY; } @@ -612,30 +585,25 @@ static zip_error read_ecd(zip_file *zip) * @return The compressed data offset. */ -static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset) +zip_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &offset) { - file_error error; - UINT32 read_length; - - /* make sure the file handle is open */ - if (zip->file == nullptr) - { - int filerr = osd_open(zip->filename, OPEN_FLAG_READ, &zip->file, &zip->length); - if (filerr != FILERR_NONE) - return ZIPERR_FILE_ERROR; - } + // make sure the file handle is open + auto const ziperr = reopen(); + if (ziperr != zip_file::error::NONE) + return ziperr; - /* now go read the fixed-sized part of the local file header */ - error = osd_read(zip->file, zip->buffer, zip->header.local_header_offset, ZIPNAME, &read_length); - if (error != FILERR_NONE || read_length != ZIPNAME) - return (error == FILERR_NONE) ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR; + // now go read the fixed-sized part of the local file header + std::uint32_t read_length; + auto const error = m_file->read(&m_buffer[0], m_header.local_header_offset, ZIPNAME, read_length); + if (error != osd_file::error::NONE || read_length != ZIPNAME) + return (error == osd_file::error::NONE) ? zip_file::error::FILE_TRUNCATED : zip_file::error::FILE_ERROR; /* compute the final offset */ - *offset = zip->header.local_header_offset + ZIPNAME; - *offset += read_word(zip->buffer + ZIPFNLN); - *offset += read_word(zip->buffer + ZIPXTRALN); + offset = m_header.local_header_offset + ZIPNAME; + offset += read_word(&m_buffer[ZIPFNLN]); + offset += read_word(&m_buffer[ZIPXTRALN]); - return ZIPERR_NONE; + return zip_file::error::NONE; } @@ -662,19 +630,18 @@ static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset) * @return A zip_error. */ -static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buffer, UINT32 length) +zip_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length) { - file_error filerr; - UINT32 read_length; - - /* the data is uncompressed; just read it */ - filerr = osd_read(zip->file, buffer, offset, zip->header.compressed_length, &read_length); - if (filerr != FILERR_NONE) - return ZIPERR_FILE_ERROR; - else if (read_length != zip->header.compressed_length) - return ZIPERR_FILE_TRUNCATED; + std::uint32_t read_length; + + // the data is uncompressed; just read it + auto const filerr = m_file->read(buffer, offset, m_header.compressed_length, read_length); + if (filerr != osd_file::error::NONE) + return zip_file::error::FILE_ERROR; + else if (read_length != m_header.compressed_length) + return zip_file::error::FILE_TRUNCATED; else - return ZIPERR_NONE; + return zip_file::error::NONE; } @@ -696,75 +663,148 @@ static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buff * @return A zip_error. */ -static zip_error decompress_data_type_8(zip_file *zip, UINT64 offset, void *buffer, UINT32 length) +zip_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length) { - UINT32 input_remaining = zip->header.compressed_length; - UINT32 read_length; - z_stream stream; - int filerr; + std::uint32_t input_remaining = m_header.compressed_length; int zerr; - /* make sure we don't need a newer mechanism */ - if (zip->header.version_needed > 0x14) - return ZIPERR_UNSUPPORTED; + // make sure we don't need a newer mechanism + if (m_header.version_needed > 0x14) + return zip_file::error::UNSUPPORTED; /* reset the stream */ + z_stream stream; memset(&stream, 0, sizeof(stream)); stream.next_out = (Bytef *)buffer; stream.avail_out = length; - /* initialize the decompressor */ + // initialize the decompressor zerr = inflateInit2(&stream, -MAX_WBITS); if (zerr != Z_OK) - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; - /* loop until we're done */ + // loop until we're done while (1) { - /* read in the next chunk of data */ - filerr = osd_read(zip->file, zip->buffer, offset, MIN(input_remaining, sizeof(zip->buffer)), &read_length); - if (filerr != FILERR_NONE) + // read in the next chunk of data + std::uint32_t read_length; + auto const filerr = m_file->read(&m_buffer[0], offset, (std::min<std::uint32_t>)(input_remaining, m_buffer.size()), read_length); + if (filerr != osd_file::error::NONE) { inflateEnd(&stream); - return ZIPERR_FILE_ERROR; + return zip_file::error::FILE_ERROR; } offset += read_length; - /* if we read nothing, but still have data left, the file is truncated */ + // if we read nothing, but still have data left, the file is truncated if (read_length == 0 && input_remaining > 0) { inflateEnd(&stream); - return ZIPERR_FILE_TRUNCATED; + return zip_file::error::FILE_TRUNCATED; } - /* fill out the input data */ - stream.next_in = zip->buffer; + // fill out the input data + stream.next_in = &m_buffer[0]; stream.avail_in = read_length; input_remaining -= read_length; - /* add a dummy byte at end of compressed data */ + // add a dummy byte at end of compressed data if (input_remaining == 0) stream.avail_in++; - /* now inflate */ + // now inflate zerr = inflate(&stream, Z_NO_FLUSH); if (zerr == Z_STREAM_END) break; if (zerr != Z_OK) { inflateEnd(&stream); - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; } } - /* finish decompression */ + // finish decompression zerr = inflateEnd(&stream); if (zerr != Z_OK) - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; /* if anything looks funny, report an error */ if (stream.avail_out > 0 || input_remaining > 0) - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; + + return zip_file::error::NONE; +} - return ZIPERR_NONE; +} // anonymous namespace + + + +/*************************************************************************** + ZIP FILE ACCESS +***************************************************************************/ + +/*------------------------------------------------- + zip_file_open - opens a ZIP file for reading +-------------------------------------------------*/ + +/** + * @fn zip_error zip_file_open(const char *filename, zip_file **zip) + * + * @brief Queries if a given zip file open. + * + * @param filename Filename of the file. + * @param [in,out] zip If non-null, the zip. + * + * @return A zip_error. + */ + +zip_file::error zip_file::open(const std::string &filename, ptr &zip) +{ + // ensure we start with a NULL result + zip.reset(); + + // see if we are in the cache, and reopen if so + zip_file_impl::ptr newimpl(zip_file_impl::find_cached(filename)); + + if (!newimpl) + { + // allocate memory for the zip_file structure + try { newimpl = std::make_unique<zip_file_impl>(filename); } + catch (...) { return error::OUT_OF_MEMORY; } + auto const ziperr = newimpl->initialize(); + if (ziperr != error::NONE) return ziperr; + } + + try + { + zip = std::make_unique<zip_file_wrapper>(std::move(newimpl)); + return error::NONE; + } + catch (...) + { + zip_file_impl::close(std::move(newimpl)); + return error::OUT_OF_MEMORY; + } +} + + +/*------------------------------------------------- + zip_file_cache_clear - clear the ZIP file + cache and free all memory +-------------------------------------------------*/ + +/** + * @fn void zip_file_cache_clear(void) + * + * @brief Zip file cache clear. + */ + +void zip_file::cache_clear() +{ + zip_file_impl::cache_clear(); +} + + +zip_file::~zip_file() +{ } diff --git a/src/lib/util/unzip.h b/src/lib/util/unzip.h index fa3956be8c3..2882cda0fc3 100644 --- a/src/lib/util/unzip.h +++ b/src/lib/util/unzip.h @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** unzip.h @@ -10,129 +10,88 @@ #pragma once -#ifndef __UNZIP_H__ -#define __UNZIP_H__ +#ifndef MAME_LIB_UTIL_UNZIP_H +#define MAME_LIB_UTIL_UNZIP_H #include "osdcore.h" - -/*************************************************************************** - CONSTANTS -***************************************************************************/ - -#define ZIP_DECOMPRESS_BUFSIZE 16384 - -/* Error types */ -enum zip_error -{ - ZIPERR_NONE = 0, - ZIPERR_OUT_OF_MEMORY, - ZIPERR_FILE_ERROR, - ZIPERR_BAD_SIGNATURE, - ZIPERR_DECOMPRESS_ERROR, - ZIPERR_FILE_TRUNCATED, - ZIPERR_FILE_CORRUPT, - ZIPERR_UNSUPPORTED, - ZIPERR_BUFFER_TOO_SMALL -}; - +#include <cstdint> +#include <memory> +#include <string> /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ -/* contains extracted file header information */ -struct zip_file_header +// describes an open ZIP file +class zip_file { - UINT32 signature; /* central file header signature */ - UINT16 version_created; /* version made by */ - UINT16 version_needed; /* version needed to extract */ - UINT16 bit_flag; /* general purpose bit flag */ - UINT16 compression; /* compression method */ - UINT16 file_time; /* last mod file time */ - UINT16 file_date; /* last mod file date */ - UINT32 crc; /* crc-32 */ - UINT32 compressed_length; /* compressed size */ - UINT32 uncompressed_length; /* uncompressed size */ - UINT16 filename_length; /* filename length */ - UINT16 extra_field_length; /* extra field length */ - UINT16 file_comment_length; /* file comment length */ - UINT16 start_disk_number; /* disk number start */ - UINT16 internal_attributes; /* internal file attributes */ - UINT32 external_attributes; /* external file attributes */ - UINT32 local_header_offset; /* relative offset of local header */ - const char * filename; /* filename */ - - UINT8 * raw; /* pointer to the raw data */ - UINT32 rawlength; /* length of the raw data */ - UINT8 saved; /* saved byte from after filename */ -}; - - -/* contains extracted end of central directory information */ -struct zip_ecd -{ - UINT32 signature; /* end of central dir signature */ - UINT16 disk_number; /* number of this disk */ - UINT16 cd_start_disk_number; /* number of the disk with the start of the central directory */ - UINT16 cd_disk_entries; /* total number of entries in the central directory on this disk */ - UINT16 cd_total_entries; /* total number of entries in the central directory */ - UINT32 cd_size; /* size of the central directory */ - UINT32 cd_start_disk_offset; /* offset of start of central directory with respect to the starting disk number */ - UINT16 comment_length; /* .ZIP file comment length */ - const char * comment; /* .ZIP file comment */ - - UINT8 * raw; /* pointer to the raw data */ - UINT32 rawlength; /* length of the raw data */ -}; - - -/* describes an open ZIP file */ -struct zip_file -{ - const char * filename; /* copy of ZIP filename (for caching) */ - osd_file * file; /* OSD file handle */ - UINT64 length; /* length of zip file */ - - zip_ecd ecd; /* end of central directory */ - - UINT8 * cd; /* central directory raw data */ - UINT32 cd_pos; /* position in central directory */ - zip_file_header header; /* current file header */ - - UINT8 buffer[ZIP_DECOMPRESS_BUFSIZE]; /* buffer for decompression */ +public: + + // Error types + enum class error + { + NONE = 0, + OUT_OF_MEMORY, + FILE_ERROR, + BAD_SIGNATURE, + DECOMPRESS_ERROR, + FILE_TRUNCATED, + FILE_CORRUPT, + UNSUPPORTED, + BUFFER_TOO_SMALL + }; + + // contains extracted file header information + struct file_header + { + std::uint32_t signature; // central file header signature + std::uint16_t version_created; // version made by + std::uint16_t version_needed; // version needed to extract + std::uint16_t bit_flag; // general purpose bit flag + std::uint16_t compression; // compression method + std::uint16_t file_time; // last mod file time + std::uint16_t file_date; // last mod file date + std::uint32_t crc; // crc-32 + std::uint32_t compressed_length; // compressed size + std::uint32_t uncompressed_length; // uncompressed size + std::uint16_t filename_length; // filename length + std::uint16_t extra_field_length; // extra field length + std::uint16_t file_comment_length; // file comment length + std::uint16_t start_disk_number; // disk number start + std::uint16_t internal_attributes; // internal file attributes + std::uint32_t external_attributes; // external file attributes + std::uint32_t local_header_offset; // relative offset of local header + const char * filename; // filename + }; + + typedef std::unique_ptr<zip_file> ptr; + + + /* ----- ZIP file access ----- */ + + // open a ZIP file and parse its central directory + static error open(const std::string &filename, ptr &zip); + + // close a ZIP file (may actually be left open due to caching) + virtual ~zip_file(); + + // clear out all open ZIP files from the cache + static void cache_clear(); + + + /* ----- contained file access ----- */ + + // find the first file in the ZIP + virtual const file_header *first_file() = 0; + + // find the next file in the ZIP + virtual const file_header *next_file() = 0; + + // decompress the most recently found file in the ZIP + virtual error decompress(void *buffer, std::uint32_t length) = 0; }; - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - - -/* ----- ZIP file access ----- */ - -/* open a ZIP file and parse its central directory */ -zip_error zip_file_open(const char *filename, zip_file **zip); - -/* close a ZIP file (may actually be left open due to caching) */ -void zip_file_close(zip_file *zip); - -/* clear out all open ZIP files from the cache */ -void zip_file_cache_clear(void); - - -/* ----- contained file access ----- */ - -/* find the first file in the ZIP */ -const zip_file_header *zip_file_first_file(zip_file *zip); - -/* find the next file in the ZIP */ -const zip_file_header *zip_file_next_file(zip_file *zip); - -/* decompress the most recently found file in the ZIP */ -zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length); - - -#endif /* __UNZIP_H__ */ +#endif // MAME_LIB_UTIL_UNZIP_H diff --git a/src/lib/util/zippath.cpp b/src/lib/util/zippath.cpp index efe7c710481..18e2031dcdd 100644 --- a/src/lib/util/zippath.cpp +++ b/src/lib/util/zippath.cpp @@ -66,7 +66,7 @@ public: /** @brief true to called zip first. */ bool called_zip_first; /** @brief The zipfile. */ - zip_file *zipfile; + zip_file::ptr zipfile; /** @brief The zipprefix. */ std::string zipprefix; /** @brief The returned dirlist. */ @@ -78,7 +78,7 @@ public: FUNCTION PROTOTYPES ***************************************************************************/ -static const zip_file_header *zippath_find_sub_path(zip_file *zipfile, const char *subpath, osd_dir_entry_type *type); +static const zip_file::file_header *zippath_find_sub_path(zip_file &zipfile, const char *subpath, osd_dir_entry_type *type); static int is_zip_file(const char *path); static int is_zip_file_separator(char c); static int is_7z_file(const char *path); @@ -251,42 +251,42 @@ std::string &zippath_combine(std::string &dst, const char *path1, const char *pa /*------------------------------------------------- file_error_from_zip_error - translates a - file_error to a zip_error + osd_file::error to a zip_error -------------------------------------------------*/ /** - * @fn static file_error file_error_from_zip_error(zip_error ziperr) + * @fn static osd_file::error file_error_from_zip_error(zip_file::error ziperr) * * @brief File error from zip error. * * @param ziperr The ziperr. * - * @return A file_error. + * @return A osd_file::error. */ -static file_error file_error_from_zip_error(zip_error ziperr) +static osd_file::error file_error_from_zip_error(zip_file::error ziperr) { - file_error filerr; + osd_file::error filerr; switch(ziperr) { - case ZIPERR_NONE: - filerr = FILERR_NONE; - break; - case ZIPERR_OUT_OF_MEMORY: - filerr = FILERR_OUT_OF_MEMORY; - break; - case ZIPERR_BAD_SIGNATURE: - case ZIPERR_DECOMPRESS_ERROR: - case ZIPERR_FILE_TRUNCATED: - case ZIPERR_FILE_CORRUPT: - case ZIPERR_UNSUPPORTED: - case ZIPERR_FILE_ERROR: - filerr = FILERR_INVALID_DATA; - break; - case ZIPERR_BUFFER_TOO_SMALL: - default: - filerr = FILERR_FAILURE; - break; + case zip_file::error::NONE: + filerr = osd_file::error::NONE; + break; + case zip_file::error::OUT_OF_MEMORY: + filerr = osd_file::error::OUT_OF_MEMORY; + break; + case zip_file::error::BAD_SIGNATURE: + case zip_file::error::DECOMPRESS_ERROR: + case zip_file::error::FILE_TRUNCATED: + case zip_file::error::FILE_CORRUPT: + case zip_file::error::UNSUPPORTED: + case zip_file::error::FILE_ERROR: + filerr = osd_file::error::INVALID_DATA; + break; + case zip_file::error::BUFFER_TOO_SMALL: + default: + filerr = osd_file::error::FAILURE; + break; } return filerr; } @@ -298,7 +298,7 @@ static file_error file_error_from_zip_error(zip_error ziperr) -------------------------------------------------*/ /** - * @fn static file_error create_core_file_from_zip(zip_file *zip, const zip_file_header *header, util::core_file::ptr &file) + * @fn static osd_file::error create_core_file_from_zip(zip_file *zip, const zip_file_header *header, util::core_file::ptr &file) * * @brief Creates core file from zip. * @@ -309,28 +309,28 @@ static file_error file_error_from_zip_error(zip_error ziperr) * @return The new core file from zip. */ -static file_error create_core_file_from_zip(zip_file *zip, const zip_file_header *header, util::core_file::ptr &file) +static osd_file::error create_core_file_from_zip(zip_file &zip, const zip_file::file_header *header, util::core_file::ptr &file) { - file_error filerr; - zip_error ziperr; + osd_file::error filerr; + zip_file::error ziperr; void *ptr; ptr = malloc(header->uncompressed_length); if (ptr == nullptr) { - filerr = FILERR_OUT_OF_MEMORY; + filerr = osd_file::error::OUT_OF_MEMORY; goto done; } - ziperr = zip_file_decompress(zip, ptr, header->uncompressed_length); - if (ziperr != ZIPERR_NONE) + ziperr = zip.decompress(ptr, header->uncompressed_length); + if (ziperr != zip_file::error::NONE) { filerr = file_error_from_zip_error(ziperr); goto done; } filerr = util::core_file::open_ram_copy(ptr, header->uncompressed_length, OPEN_FLAG_READ, file); - if (filerr != FILERR_NONE) + if (filerr != osd_file::error::NONE) goto done; done: @@ -345,7 +345,7 @@ done: -------------------------------------------------*/ /** - * @fn file_error zippath_fopen(const char *filename, UINT32 openflags, util::core_file::ptr &file, std::string &revised_path) + * @fn osd_file::error zippath_fopen(const char *filename, UINT32 openflags, util::core_file::ptr &file, std::string &revised_path) * * @brief Zippath fopen. * @@ -354,17 +354,16 @@ done: * @param [in,out] file [in,out] If non-null, the file. * @param [in,out] revised_path Full pathname of the revised file. * - * @return A file_error. + * @return A osd_file::error. */ -file_error zippath_fopen(const char *filename, UINT32 openflags, util::core_file::ptr &file, std::string &revised_path) +osd_file::error zippath_fopen(const char *filename, UINT32 openflags, util::core_file::ptr &file, std::string &revised_path) { - file_error filerr = FILERR_NOT_FOUND; - zip_error ziperr; - zip_file *zip = nullptr; - const zip_file_header *header; + osd_file::error filerr = osd_file::error::NOT_FOUND; + zip_file::error ziperr; + zip_file::ptr zip; + const zip_file::file_header *header; osd_dir_entry_type entry_type; - char *alloc_fullpath = nullptr; int len; /* first, set up the two types of paths */ @@ -380,30 +379,30 @@ file_error zippath_fopen(const char *filename, UINT32 openflags, util::core_file if (is_zip_file(mainpath.c_str())) { /* this file might be a zip file - lets take a look */ - ziperr = zip_file_open(mainpath.c_str(), &zip); - if (ziperr == ZIPERR_NONE) + ziperr = zip_file::open(mainpath, zip); + if (ziperr == zip_file::error::NONE) { /* it is a zip file - error if we're not opening for reading */ if (openflags != OPEN_FLAG_READ) { - filerr = FILERR_ACCESS_DENIED; + filerr = osd_file::error::ACCESS_DENIED; goto done; } if (subpath.length() > 0) - header = zippath_find_sub_path(zip, subpath.c_str(), &entry_type); + header = zippath_find_sub_path(*zip, subpath.c_str(), &entry_type); else - header = zip_file_first_file(zip); + header = zip->first_file(); if (header == nullptr) { - filerr = FILERR_NOT_FOUND; + filerr = osd_file::error::NOT_FOUND; goto done; } /* attempt to read the file */ - filerr = create_core_file_from_zip(zip, header, file); - if (filerr != FILERR_NONE) + filerr = create_core_file_from_zip(*zip, header, file); + if (filerr != osd_file::error::NONE) goto done; /* update subpath, if appropriate */ @@ -416,17 +415,17 @@ file_error zippath_fopen(const char *filename, UINT32 openflags, util::core_file } else if (is_7z_file(mainpath.c_str())) { - filerr = FILERR_INVALID_DATA; + filerr = osd_file::error::INVALID_DATA; goto done; } if (subpath.length() == 0) filerr = util::core_file::open(filename, openflags, file); else - filerr = FILERR_NOT_FOUND; + filerr = osd_file::error::NOT_FOUND; /* if we errored, then go up a directory */ - if (filerr != FILERR_NONE) + if (filerr != osd_file::error::NONE) { /* go up a directory */ std::string temp; @@ -457,23 +456,19 @@ file_error zippath_fopen(const char *filename, UINT32 openflags, util::core_file done: /* store the revised path */ revised_path.clear(); - if (filerr == FILERR_NONE) + if (filerr == osd_file::error::NONE) { /* cannonicalize mainpath */ - filerr = osd_get_full_path(&alloc_fullpath, mainpath.c_str()); - if (filerr == FILERR_NONE) + std::string alloc_fullpath; + filerr = osd_get_full_path(alloc_fullpath, mainpath); + if (filerr == osd_file::error::NONE) { + revised_path = alloc_fullpath; if (subpath.length() > 0) - revised_path.assign(alloc_fullpath).append(PATH_SEPARATOR).append(subpath); - else - revised_path.assign(alloc_fullpath); + revised_path.append(PATH_SEPARATOR).append(subpath); } } - if (zip != nullptr) - zip_file_close(zip); - if (alloc_fullpath != nullptr) - osd_free(alloc_fullpath); return filerr; } @@ -672,13 +667,13 @@ static char next_path_char(const char *s, int *pos) * @return null if it fails, else a zip_file_header*. */ -static const zip_file_header *zippath_find_sub_path(zip_file *zipfile, const char *subpath, osd_dir_entry_type *type) +static const zip_file::file_header *zippath_find_sub_path(zip_file &zipfile, const char *subpath, osd_dir_entry_type *type) { int i, j; char c1, c2, last_char; - const zip_file_header *header; + const zip_file::file_header *header; - for (header = zip_file_first_file(zipfile); header != nullptr; header = zip_file_next_file(zipfile)) + for (header = zipfile.first_file(); header != nullptr; header = zipfile.next_file()) { /* special case */ if (subpath == nullptr) @@ -726,7 +721,7 @@ static const zip_file_header *zippath_find_sub_path(zip_file *zipfile, const cha -------------------------------------------------*/ /** - * @fn static file_error zippath_resolve(const char *path, osd_dir_entry_type &entry_type, zip_file *&zipfile, std::string &newpath) + * @fn static osd_file::error zippath_resolve(const char *path, osd_dir_entry_type &entry_type, zip_file *&zipfile, std::string &newpath) * * @brief Zippath resolve. * @@ -735,12 +730,12 @@ static const zip_file_header *zippath_find_sub_path(zip_file *zipfile, const cha * @param [in,out] zipfile [in,out] If non-null, the zipfile. * @param [in,out] newpath The newpath. * - * @return A file_error. + * @return A osd_file::error. */ -static file_error zippath_resolve(const char *path, osd_dir_entry_type &entry_type, zip_file *&zipfile, std::string &newpath) +static osd_file::error zippath_resolve(const char *path, osd_dir_entry_type &entry_type, zip_file::ptr &zipfile, std::string &newpath) { - file_error err; + osd_file::error err; osd_directory_entry *current_entry = nullptr; osd_dir_entry_type current_entry_type; int went_up = FALSE; @@ -750,7 +745,7 @@ static file_error zippath_resolve(const char *path, osd_dir_entry_type &entry_ty /* be conservative */ entry_type = ENTTYPE_NONE; - zipfile = nullptr; + zipfile.reset(); std::string apath(path); std::string apath_trimmed; @@ -788,13 +783,13 @@ static file_error zippath_resolve(const char *path, osd_dir_entry_type &entry_ty /* if we did not find anything, then error out */ if (current_entry_type == ENTTYPE_NONE) { - err = FILERR_NOT_FOUND; + err = osd_file::error::NOT_FOUND; goto done; } /* is this file a ZIP file? */ if ((current_entry_type == ENTTYPE_FILE) && is_zip_file(apath_trimmed.c_str()) - && (zip_file_open(apath_trimmed.c_str(), &zipfile) == ZIPERR_NONE)) + && (zip_file::open(apath_trimmed, zipfile) == zip_file::error::NONE)) { i = strlen(path + apath.length()); while (i > 0 && is_zip_path_separator(path[apath.length() + i - 1])) @@ -802,10 +797,10 @@ static file_error zippath_resolve(const char *path, osd_dir_entry_type &entry_ty newpath.assign(path + apath.length(), i); /* this was a true ZIP path - attempt to identify the type of path */ - zippath_find_sub_path(zipfile, newpath.c_str(), ¤t_entry_type); + zippath_find_sub_path(*zipfile, newpath.c_str(), ¤t_entry_type); if (current_entry_type == ENTTYPE_NONE) { - err = FILERR_NOT_FOUND; + err = osd_file::error::NOT_FOUND; goto done; } } @@ -814,7 +809,7 @@ static file_error zippath_resolve(const char *path, osd_dir_entry_type &entry_ty /* this was a normal path */ if (went_up) { - err = FILERR_NOT_FOUND; + err = osd_file::error::NOT_FOUND; goto done; } newpath.assign(path); @@ -822,7 +817,7 @@ static file_error zippath_resolve(const char *path, osd_dir_entry_type &entry_ty /* success! */ entry_type = current_entry_type; - err = FILERR_NONE; + err = osd_file::error::NONE; done: return err; @@ -834,19 +829,19 @@ done: -------------------------------------------------*/ /** - * @fn file_error zippath_opendir(const char *path, zippath_directory **directory) + * @fn osd_file::error zippath_opendir(const char *path, zippath_directory **directory) * * @brief Zippath opendir. * * @param path Full pathname of the file. * @param [in,out] directory If non-null, pathname of the directory. * - * @return A file_error. + * @return A osd_file::error. */ -file_error zippath_opendir(const char *path, zippath_directory **directory) +osd_file::error zippath_opendir(const char *path, zippath_directory **directory) { - file_error err; + osd_file::error err; /* allocate a directory */ zippath_directory *result = nullptr; @@ -856,19 +851,19 @@ file_error zippath_opendir(const char *path, zippath_directory **directory) } catch (std::bad_alloc &) { - err = FILERR_OUT_OF_MEMORY; + err = osd_file::error::OUT_OF_MEMORY; goto done; } /* resolve the path */ osd_dir_entry_type entry_type; err = zippath_resolve(path, entry_type, result->zipfile, result->zipprefix); - if (err != FILERR_NONE) + if (err != osd_file::error::NONE) goto done; /* we have to be a directory */ if (entry_type != ENTTYPE_DIR) { - err = FILERR_NOT_FOUND; + err = osd_file::error::NOT_FOUND; goto done; } @@ -879,7 +874,7 @@ file_error zippath_opendir(const char *path, zippath_directory **directory) result->directory = osd_opendir(path); if (result->directory == nullptr) { - err = FILERR_FAILURE; + err = osd_file::error::FAILURE; goto done; } @@ -889,7 +884,7 @@ file_error zippath_opendir(const char *path, zippath_directory **directory) } done: - if ((directory == nullptr || err != FILERR_NONE) && result != nullptr) + if ((directory == nullptr || err != osd_file::error::NONE) && result != nullptr) { zippath_closedir(result); result = nullptr; @@ -918,7 +913,7 @@ void zippath_closedir(zippath_directory *directory) osd_closedir(directory->directory); if (directory->zipfile != nullptr) - zip_file_close(directory->zipfile); + directory->zipfile.reset(); while (directory->returned_dirlist != nullptr) { @@ -948,7 +943,7 @@ void zippath_closedir(zippath_directory *directory) * @return null if it fails, else the relative path. */ -static const char *get_relative_path(zippath_directory *directory, const zip_file_header *header) +static const char *get_relative_path(zippath_directory *directory, const zip_file::file_header *header) { const char *result = nullptr; int len = directory->zipprefix.length(); @@ -982,7 +977,7 @@ static const char *get_relative_path(zippath_directory *directory, const zip_fil const osd_directory_entry *zippath_readdir(zippath_directory *directory) { const osd_directory_entry *result = nullptr; - const zip_file_header *header; + const zip_file::file_header *header; const char *relpath; const char *separator; const char *s; @@ -1023,9 +1018,9 @@ const osd_directory_entry *zippath_readdir(zippath_directory *directory) do { if (!directory->called_zip_first) - header = zip_file_first_file(directory->zipfile); + header = directory->zipfile->first_file(); else - header = zip_file_next_file(directory->zipfile); + header = directory->zipfile->next_file(); directory->called_zip_first = true; relpath = nullptr; } diff --git a/src/lib/util/zippath.h b/src/lib/util/zippath.h index ce40c87ba8d..d54dd68c38d 100644 --- a/src/lib/util/zippath.h +++ b/src/lib/util/zippath.h @@ -45,13 +45,13 @@ std::string &zippath_combine(std::string &dst, const char *path1, const char *pa /* ----- file operations ----- */ /* opens a zip path file */ -file_error zippath_fopen(const char *filename, UINT32 openflags, util::core_file::ptr &file, std::string &revised_path); +osd_file::error zippath_fopen(const char *filename, UINT32 openflags, util::core_file::ptr &file, std::string &revised_path); /* ----- directory operations ----- */ /* opens a directory */ -file_error zippath_opendir(const char *path, zippath_directory **directory); +osd_file::error zippath_opendir(const char *path, zippath_directory **directory); /* closes a directory */ void zippath_closedir(zippath_directory *directory); |