diff options
author | 2016-03-13 13:54:57 +1100 | |
---|---|---|
committer | 2016-03-14 18:55:00 +1100 | |
commit | 42fbb9c3967fcda37f2d9ae17d5796a79cf027b9 (patch) | |
tree | a43047ee00431e5e22bfc5f8f612ff775586e415 /src/lib/util | |
parent | 5fc27747031b6ed6f6c0582502098ebfb960be67 (diff) |
Make osd_file a polymorphic class that's held with smart pointers
Make avi_file a class that's held with smart pointers, encapsulate various AVI I/O structures
Make zip_file and _7z_file classes rather than having free functions everywhere
Hide zip/7z class implementation behind an interface, no longer need to call close() to send back to the cache
Don't dump as much crap in global namespace
Add solaris PTY implementation
Improve variable expansion for SDL OSD - supports ~/$FOO/${BAR} syntax
Rearrange stuff so the same things are in file module for all OSDs
Move file stuff into its own module
7z/zip open and destruct are still not thread-safe due to lack of interlocks around cache access
Directory functions still need to be moved to file module
SDL OSD may not initialise WinSock on Windows
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); |