1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles, Vas Crabb
3 /***************************************************************************
4 
5     aviio.c
6 
7     AVI movie format parsing helpers.
8 
9 ***************************************************************************/
10 
11 #include <array>
12 #include <cassert>
13 #include <cstdlib>
14 #include <cstring>
15 
16 #include "aviio.h"
17 
18 
19 /***************************************************************************
20     CONSTANTS
21 ***************************************************************************/
22 
23 /**
24  * @def FILETYPE_READ
25  *
26  * @brief   A macro that defines filetype read.
27  */
28 
29 #define FILETYPE_READ           1
30 
31 /**
32  * @def FILETYPE_CREATE
33  *
34  * @brief   A macro that defines filetype create.
35  */
36 
37 #define FILETYPE_CREATE         2
38 
39 /** @brief  Size of the maximum riff. */
40 #define MAX_RIFF_SIZE           (2UL * 1024 * 1024 * 1024 - 1024)   /* just under 2GB */
41 /** @brief  The maximum avi size in gigabytes. */
42 #define MAX_AVI_SIZE_IN_GB      (1024)
43 
44 /**
45  * @def FOUR_GB
46  *
47  * @brief   A macro that defines four gigabytes.
48  */
49 
50 #define FOUR_GB                 (std::uint64_t(1) << 32)
51 
52 /**
53  * @def MAX_SOUND_CHANNELS
54  *
55  * @brief   A macro that defines maximum sound channels.
56  */
57 
58 #define MAX_SOUND_CHANNELS      2
59 
60 /**
61  * @def SOUND_BUFFER_MSEC
62  *
63  * @brief   A macro that defines sound buffer msec.
64  */
65 
66 #define SOUND_BUFFER_MSEC       2000        /* milliseconds of sound buffering */
67 
68 /** @brief  The chunktype riff. */
69 #define CHUNKTYPE_RIFF          AVI_FOURCC('R','I','F','F')
70 /** @brief  List of chunktypes. */
71 #define CHUNKTYPE_LIST          AVI_FOURCC('L','I','S','T')
72 /** @brief  The chunktype junk. */
73 #define CHUNKTYPE_JUNK          AVI_FOURCC('J','U','N','K')
74 /** @brief  The chunktype avih. */
75 #define CHUNKTYPE_AVIH          AVI_FOURCC('a','v','i','h')
76 /** @brief  The chunktype strh. */
77 #define CHUNKTYPE_STRH          AVI_FOURCC('s','t','r','h')
78 /** @brief  The chunktype strf. */
79 #define CHUNKTYPE_STRF          AVI_FOURCC('s','t','r','f')
80 /** @brief  The first chunktype index. */
81 #define CHUNKTYPE_IDX1          AVI_FOURCC('i','d','x','1')
82 /** @brief  The chunktype indx. */
83 #define CHUNKTYPE_INDX          AVI_FOURCC('i','n','d','x')
84 /** @brief  The chunktype xxdb. */
85 #define CHUNKTYPE_XXDB          AVI_FOURCC(0x00,0x00,'d','b')
86 /** @brief  The chunktype xxdc. */
87 #define CHUNKTYPE_XXDC          AVI_FOURCC(0x00,0x00,'d','c')
88 /** @brief  The chunktype xxwb. */
89 #define CHUNKTYPE_XXWB          AVI_FOURCC(0x00,0x00,'w','b')
90 /** @brief  The chunktype ixxx. */
91 #define CHUNKTYPE_IXXX          AVI_FOURCC('i','x',0x00,0x00)
92 /** @brief  The chunktype xx mask. */
93 #define CHUNKTYPE_XX_MASK       AVI_FOURCC(0x00,0x00,0xff,0xff)
94 
95 /** @brief  The listtype avi. */
96 #define LISTTYPE_AVI            AVI_FOURCC('A','V','I',' ')
97 /** @brief  The listtype avix. */
98 #define LISTTYPE_AVIX           AVI_FOURCC('A','V','I','X')
99 /** @brief  The listtype hdrl. */
100 #define LISTTYPE_HDRL           AVI_FOURCC('h','d','r','l')
101 /** @brief  The listtype strl. */
102 #define LISTTYPE_STRL           AVI_FOURCC('s','t','r','l')
103 /** @brief  The listtype movi. */
104 #define LISTTYPE_MOVI           AVI_FOURCC('m','o','v','i')
105 
106 /** @brief  The streamtype vids. */
107 #define STREAMTYPE_VIDS         AVI_FOURCC('v','i','d','s')
108 /** @brief  The streamtype auds. */
109 #define STREAMTYPE_AUDS         AVI_FOURCC('a','u','d','s')
110 
111 /** @brief  The handler bitmap. */
112 #define HANDLER_DIB             AVI_FOURCC('D','I','B',' ')
113 /** @brief  The handler hfyu. */
114 #define HANDLER_HFYU            AVI_FOURCC('h','f','y','u')
115 
116 /* main AVI header files */
117 
118 /**
119  * @def AVIF_HASINDEX
120  *
121  * @brief   A macro that defines avif hasindex.
122  */
123 
124 #define AVIF_HASINDEX           0x00000010
125 
126 /**
127  * @def AVIF_MUSTUSEINDEX
128  *
129  * @brief   A macro that defines avif mustuseindex.
130  */
131 
132 #define AVIF_MUSTUSEINDEX       0x00000020
133 
134 /**
135  * @def AVIF_ISINTERLEAVED
136  *
137  * @brief   A macro that defines avif isinterleaved.
138  */
139 
140 #define AVIF_ISINTERLEAVED      0x00000100
141 
142 /**
143  * @def AVIF_COPYRIGHTED
144  *
145  * @brief   A macro that defines avif copyrighted.
146  */
147 
148 #define AVIF_COPYRIGHTED        0x00010000
149 
150 /**
151  * @def AVIF_WASCAPTUREFILE
152  *
153  * @brief   A macro that defines avif wascapturefile.
154  */
155 
156 #define AVIF_WASCAPTUREFILE     0x00020000
157 
158 /* index definitions */
159 
160 /**
161  * @def AVI_INDEX_OF_INDEXES
162  *
163  * @brief   A macro that defines avi index of indexes.
164  */
165 
166 #define AVI_INDEX_OF_INDEXES    0x00
167 
168 /**
169  * @def AVI_INDEX_OF_CHUNKS
170  *
171  * @brief   A macro that defines avi index of chunks.
172  */
173 
174 #define AVI_INDEX_OF_CHUNKS     0x01
175 
176 /**
177  * @def AVI_INDEX_IS_DATA
178  *
179  * @brief   A macro that defines avi index is data.
180  */
181 
182 #define AVI_INDEX_IS_DATA       0x80
183 
184 /**
185  * @def AVI_INDEX_2FIELD
186  *
187  * @brief   A macro that defines avi index 2 field.
188  */
189 
190 #define AVI_INDEX_2FIELD        0x01
191 
192 	/**
193 	* @def AVI_INTEGRAL_MULTIPLE
194 	*
195 	* @brief   Ensures the integral multiple of the video dimension, because most video players are not capable to playback a video stream with a lower multiple.
196 	*/
197 
198 #define AVI_INTEGRAL_MULTIPLE   4
199 
200 /* HuffYUV definitions */
201 
202 /**
203  * @def HUFFYUV_PREDICT_LEFT
204  *
205  * @brief   A macro that defines huffyuv predict left.
206  */
207 
208 #define HUFFYUV_PREDICT_LEFT     0
209 
210 /**
211  * @def HUFFYUV_PREDICT_GRADIENT
212  *
213  * @brief   A macro that defines huffyuv predict gradient.
214  */
215 
216 #define HUFFYUV_PREDICT_GRADIENT 1
217 
218 /**
219  * @def HUFFYUV_PREDICT_MEDIAN
220  *
221  * @brief   A macro that defines huffyuv predict median.
222  */
223 
224 #define HUFFYUV_PREDICT_MEDIAN   2
225 
226 /**
227  * @def HUFFYUV_PREDICT_DECORR
228  *
229  * @brief   A macro that defines huffyuv predict decorr.
230  */
231 
232 #define HUFFYUV_PREDICT_DECORR   0x40
233 
234 
235 namespace {
236 /***************************************************************************
237     TYPE DEFINITIONS
238 ***************************************************************************/
239 
240 /**
241  * @struct  avi_chunk
242  *
243  * @brief   An avi chunk.
244  */
245 
246 struct avi_chunk
247 {
avi_chunk__anon6e7e86150111::avi_chunk248 	avi_chunk() : offset(0), size(0), type(0), listtype(0) { }
249 
250 	std::uint64_t       offset;                 /* file offset of chunk header */
251 	std::uint64_t       size;                   /* size of this chunk */
252 	std::uint32_t       type;                   /* type of this chunk */
253 	std::uint32_t       listtype;               /* type of this list (if we are a list) */
254 };
255 
256 /**
257  * @struct  avi_chunk_list
258  *
259  * @brief   List of avi chunks.
260  */
261 
262 struct avi_chunk_list
263 {
264 	std::uint64_t       offset;                 /* offset in the file of header */
265 	std::uint32_t       length;                 /* length of the chunk including header */
266 };
267 
268 /**
269  * @struct  avi_stream
270  *
271  * @brief   An avi stream.
272  */
273 
274 class avi_stream
275 {
276 public:
avi_stream()277 	avi_stream()
278 		: m_type(0)
279 		, m_format(0)
280 		, m_rate(0)
281 		, m_scale(0)
282 		, m_samples(0)
283 		, m_chunks()
284 		, m_width(0)
285 		, m_height(0)
286 		, m_depth(0)
287 		, m_huffyuv()
288 		, m_channels(0)
289 		, m_samplebits(0)
290 		, m_samplerate(0)
291 		, m_saved_strh_offset(0)
292 		, m_saved_indx_offset(0)
293 	{
294 	}
295 
initialize_video(avi_file::movie_info const & info)296 	void initialize_video(avi_file::movie_info const &info)
297 	{
298 		std::uint32_t width = info.video_width;
299 		std::uint32_t height = info.video_height;
300 
301 		auto integal_multiple = std::uint32_t(AVI_INTEGRAL_MULTIPLE);
302 		if (integal_multiple > 1)
303 		{
304 			width = width - (width % integal_multiple);
305 			height = height - (height % integal_multiple);
306 		}
307 
308 		m_type = STREAMTYPE_VIDS;
309 		m_format = info.video_format;
310 		m_rate = info.video_timescale;
311 		m_scale = info.video_sampletime;
312 		m_width = width;
313 		m_height = height;
314 		m_depth = info.video_depth;
315 	}
316 
initialize_audio(avi_file::movie_info const & info)317 	void initialize_audio(avi_file::movie_info const &info)
318 	{
319 		m_type = STREAMTYPE_AUDS;
320 		m_format = info.audio_format;
321 		m_rate = info.audio_timescale;
322 		m_scale = info.audio_sampletime;
323 		m_channels = info.audio_channels;
324 		m_samplebits = info.audio_samplebits;
325 		m_samplerate = info.audio_samplerate;
326 	}
327 
type() const328 	std::uint32_t type() const { return m_type; }
format() const329 	std::uint32_t format() const { return m_format; }
330 
rate() const331 	std::uint32_t rate() const { return m_rate; }
scale() const332 	std::uint32_t scale() const { return m_scale; }
samples() const333 	std::uint32_t samples() const { return m_samples; }
set_samples(std::uint32_t value)334 	void set_samples(std::uint32_t value) { m_samples = value; }
add_samples(std::uint32_t value)335 	std::uint32_t add_samples(std::uint32_t value) { return m_samples += value; }
336 
chunks() const337 	std::uint32_t chunks() const { return m_chunks.size(); }
chunk(std::uint32_t index) const338 	avi_chunk_list const &chunk(std::uint32_t index) const { assert(index < m_chunks.size()); return m_chunks[index]; }
chunk(std::uint32_t index)339 	avi_chunk_list &chunk(std::uint32_t index) { assert(index < m_chunks.size()); return m_chunks[index]; }
340 	avi_file::error set_chunk_info(std::uint32_t index, std::uint64_t offset, std::uint32_t length);
set_chunks(std::uint32_t count)341 	void set_chunks(std::uint32_t count) { assert(count <= m_chunks.size()); m_chunks.resize(count); }
342 
width() const343 	std::uint32_t width() const { return m_width; }
height() const344 	std::uint32_t height() const { return m_height; }
depth() const345 	std::uint32_t depth() const { return m_depth; }
346 
channels() const347 	std::uint16_t channels() const { return m_channels; }
samplebits() const348 	std::uint16_t samplebits() const { return m_samplebits; }
samplerate() const349 	std::uint32_t samplerate() const { return m_samplerate; }
bytes_per_sample() const350 	std::uint32_t bytes_per_sample() const { return (m_samplebits / 8) * m_channels; }
351 
352 	avi_file::error set_strh_data(std::uint8_t const *data, std::uint32_t size);
353 	avi_file::error set_strf_data(std::uint8_t const *data, std::uint32_t size);
354 
saved_strh_offset()355 	std::uint64_t &saved_strh_offset() { return m_saved_strh_offset; }
saved_indx_offset()356 	std::uint64_t &saved_indx_offset() { return m_saved_indx_offset; }
357 
358 	// RGB helpers
359 	avi_file::error rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const;
360 
361 	// YUY helpers
362 	avi_file::error yuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const;
363 	avi_file::error yuy16_compress_to_yuy(const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const;
364 
365 	// HuffYUV helpers
366 	avi_file::error huffyuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const;
367 
368 	// Uncompressed helpers
369 	avi_file::error uncompressed_rgb24_to_argb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_argb32 &bitmap) const;
370 	avi_file::error uncompressed_yuv420p_to_argb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_argb32 &bitmap) const;
371 
372 private:
373 	struct huffyuv_table
374 	{
375 		std::uint8_t        shift[256];             /* bit shift amounts */
376 		std::uint32_t       bits[256];              /* bit match values */
377 		std::uint32_t       mask[256];              /* bit mask values */
378 		std::uint16_t       baselookup[65536];      /* base lookup table */
379 		std::vector<std::uint16_t> extralookup;     /* extra lookup tables */
380 	};
381 
382 	struct huffyuv_data
383 	{
384 		std::uint8_t        predictor;              /* predictor */
385 		huffyuv_table       table[3];               /* array of tables */
386 	};
387 
388 	avi_file::error huffyuv_extract_tables(const std::uint8_t *chunkdata, std::uint32_t size);
389 
390 	std::uint32_t       m_type;                 /* subtype of stream */
391 	std::uint32_t       m_format;               /* format of stream data */
392 
393 	std::uint32_t       m_rate;                 /* timescale for stream */
394 	std::uint32_t       m_scale;                /* duration of one sample in the stream */
395 	std::uint32_t       m_samples;              /* number of samples */
396 
397 	std::vector<avi_chunk_list> m_chunks;       /* list of chunks */
398 
399 	std::uint32_t       m_width;                /* width of video */
400 	std::uint32_t       m_height;               /* height of video */
401 	std::uint32_t       m_depth;                /* depth of video */
402 	std::unique_ptr<huffyuv_data const> m_huffyuv; /* huffyuv decompression data */
403 
404 	std::uint16_t       m_channels;             /* audio channels */
405 	std::uint16_t       m_samplebits;           /* audio bits per sample */
406 	std::uint32_t       m_samplerate;           /* audio sample rate */
407 
408 	/* only used when creating */
409 	std::uint64_t       m_saved_strh_offset;    /* writeoffset of strh chunk */
410 	std::uint64_t       m_saved_indx_offset;    /* writeoffset of indx chunk */
411 };
412 
413 /**
414  * @class   avi_file_impl
415  *
416  * @brief   An avi file.
417  */
418 
419 class avi_file_impl : public avi_file
420 {
421 public:
avi_file_impl(osd_file::ptr && file,std::uint64_t length)422 	avi_file_impl(osd_file::ptr &&file, std::uint64_t length) : avi_file_impl()
423 	{
424 		m_file = std::move(file);
425 		m_type = FILETYPE_READ;
426 
427 		// make a root atom
428 		m_rootchunk.offset = 0;
429 		m_rootchunk.size = length;
430 		m_rootchunk.type = 0;
431 		m_rootchunk.listtype = 0;
432 	}
433 
avi_file_impl(osd_file::ptr && file,movie_info const & info)434 	avi_file_impl(osd_file::ptr &&file, movie_info const &info) : avi_file_impl()
435 	{
436 		m_file = std::move(file);
437 		m_type = FILETYPE_CREATE;
438 
439 		// copy the movie info
440 		m_info = info;
441 		m_info.video_numsamples = 0;
442 		m_info.audio_numsamples = 0;
443 
444 		// initialize the video track
445 		m_streams.emplace_back();
446 		m_streams.back().initialize_video(m_info);
447 
448 		// initialize the audio track
449 		if (m_info.audio_channels > 0)
450 		{
451 			m_streams.emplace_back();
452 			m_streams.back().initialize_audio(m_info);
453 		}
454 	}
455 
456 	virtual ~avi_file_impl() override;
457 
458 	virtual void printf_chunks() override;
459 
460 	virtual movie_info const &get_movie_info() const override;
461 	virtual std::uint32_t first_sample_in_frame(std::uint32_t framenum) const override;
462 
463 	virtual error read_uncompressed_video_frame(std::uint32_t framenum, bitmap_argb32 &bitmap) override;
464 	virtual error read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap) override;
465 	virtual error read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output) override;
466 
467 	virtual error append_video_frame(bitmap_yuy16 &bitmap) override;
468 	virtual error append_video_frame(bitmap_rgb32 &bitmap) override;
469 	virtual error append_sound_samples(int channel, std::int16_t const *samples, std::uint32_t numsamples, std::uint32_t sampleskip) override;
470 
471 	error read_movie_data();
472 	error write_initial_headers();
473 	error soundbuf_initialize();
474 
475 private:
avi_file_impl()476 	avi_file_impl()
477 		: m_file()
478 		, m_type(0)
479 		, m_info({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 })
480 		, m_tempbuffer()
481 		, m_streams()
482 		, m_rootchunk()
483 		, m_writeoffs(0)
484 		, m_riffbase(0)
485 		, m_chunkstack()
486 		, m_chunksp(0)
487 		, m_saved_movi_offset(0)
488 		, m_saved_avih_offset(0)
489 		, m_soundbuf()
490 		, m_soundbuf_samples(0)
491 		, m_soundbuf_chunks(0)
492 		, m_soundbuf_frames(0)
493 	{
494 		std::fill(std::begin(m_soundbuf_chansamples), std::end(m_soundbuf_chansamples), 0);
495 	}
496 
497 	avi_stream *get_video_stream();
498 	avi_stream *get_audio_stream(int channel, int &offset);
499 	std::uint32_t compute_idx1_size() const;
500 	std::uint32_t get_chunkid_for_stream(const avi_stream *stream) const;
501 	std::uint32_t framenum_to_samplenum(std::uint32_t framenum) const;
502 	error expand_tempbuffer(std::uint32_t length);
503 
504 	// core chunk read routines
505 	error get_first_chunk(avi_chunk const *parent, avi_chunk &newchunk);
506 	error get_next_chunk(avi_chunk const *parent, avi_chunk &newchunk);
507 	error find_first_chunk(std::uint32_t findme, avi_chunk const *container, avi_chunk &result);
508 	error find_next_chunk(std::uint32_t findme, avi_chunk const *container, avi_chunk &result);
509 	error find_first_list(std::uint32_t findme, avi_chunk const *container, avi_chunk &result);
510 	error find_next_list(std::uint32_t findme, avi_chunk const *container, avi_chunk &result);
511 	error get_next_chunk_internal(avi_chunk const *parent, avi_chunk &newchunk, std::uint64_t offset);
512 	error read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer);
513 
514 	// chunk read helpers
515 	error extract_movie_info();
516 	error parse_avih_chunk(avi_chunk const &avih);
517 	error parse_strh_chunk(avi_stream &stream, avi_chunk const &strh);
518 	error parse_strf_chunk(avi_stream &stream, avi_chunk const &strf);
519 	error parse_indx_chunk(avi_stream &stream, avi_chunk const &strf);
520 	error parse_idx1_chunk(std::uint64_t baseoffset, avi_chunk const &idx1);
521 
522 	// core chunk write routines
523 	error chunk_open(std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength);
524 	error chunk_close();
525 	error chunk_write(std::uint32_t type, const void *data, std::uint32_t length);
526 	error chunk_overwrite(std::uint32_t type, const void *data, std::uint32_t length, std::uint64_t &offset, bool initial_write);
527 
528 	// chunk write helpers
529 	error write_avih_chunk(bool initial_write);
530 	error write_strh_chunk(avi_stream &stream, bool initial_write);
531 	error write_strf_chunk(avi_stream const &stream);
532 	error write_indx_chunk(avi_stream &stream, bool initial_write);
533 	error write_idx1_chunk();
534 
535 	// sound buffering helpers
536 	error soundbuf_write_chunk(std::uint32_t framenum);
537 	error soundbuf_flush(bool only_flush_full);
538 
539 	// debugging
540 	void printf_chunk_recursive(avi_chunk const *chunk, int indent);
541 
542 	/* shared data */
543 	osd_file::ptr       m_file;                 /* pointer to open file */
544 	int                 m_type;                 /* type of access (read/create) */
545 	movie_info          m_info;                 /* movie info structure */
546 	std::vector<std::uint8_t> m_tempbuffer;     /* temporary buffer */
547 
548 	/* only used when reading */
549 	std::vector<avi_stream> m_streams;          /* allocated array of stream information */
550 	avi_chunk           m_rootchunk;            /* dummy root chunk that wraps the whole file */
551 
552 	/* only used when creating */
553 	std::uint64_t       m_writeoffs;            /* current file write offset */
554 	std::uint64_t       m_riffbase;             /* base of the current RIFF */
555 
556 	std::array<avi_chunk, 8> m_chunkstack;      /* stack of chunks we are writing */
557 	int                 m_chunksp;              /* stack pointer for the current chunk */
558 
559 	std::uint64_t       m_saved_movi_offset;    /* writeoffset of movi list */
560 	std::uint64_t       m_saved_avih_offset;    /* writeoffset of avih chunk */
561 
562 	std::vector<std::int16_t> m_soundbuf;       /* buffer for sound data */
563 	std::uint32_t       m_soundbuf_samples;     /* length of sound buffer in samples */
564 	std::uint32_t       m_soundbuf_chansamples[MAX_SOUND_CHANNELS]; /* samples in buffer for each channel */
565 	std::uint32_t       m_soundbuf_chunks;      /* number of chunks completed so far */
566 	std::uint32_t       m_soundbuf_frames;      /* number of frames ahead of the video */
567 };
568 
569 
570 
571 /***************************************************************************
572     INLINE FUNCTIONS
573 ***************************************************************************/
574 
575 /*-------------------------------------------------
576     fetch_16bits - read 16 bits in LSB order
577     from the given buffer
578 -------------------------------------------------*/
579 
580 /**
581  * @fn  static inline std::uint16_t fetch_16bits(const std::uint8_t *data)
582  *
583  * @brief   Fetches the 16bits.
584  *
585  * @param   data    The data.
586  *
587  * @return  The 16bits.
588  */
589 
fetch_16bits(const std::uint8_t * data)590 inline std::uint16_t fetch_16bits(const std::uint8_t *data)
591 {
592 	return data[0] | (data[1] << 8);
593 }
594 
595 
596 /*-------------------------------------------------
597     fetch_32bits - read 32 bits in LSB order
598     from the given buffer
599 -------------------------------------------------*/
600 
601 /**
602  * @fn  static inline std::uint32_t fetch_32bits(const std::uint8_t *data)
603  *
604  * @brief   Fetches the 32bits.
605  *
606  * @param   data    The data.
607  *
608  * @return  The 32bits.
609  */
610 
fetch_32bits(const std::uint8_t * data)611 inline std::uint32_t fetch_32bits(const std::uint8_t *data)
612 {
613 	return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
614 }
615 
616 
617 /*-------------------------------------------------
618     fetch_64bits - read 64 bits in LSB order
619     from the given buffer
620 -------------------------------------------------*/
621 
622 /**
623  * @fn  static inline std::uint64_t fetch_64bits(const std::uint8_t *data)
624  *
625  * @brief   Fetches the 64bits.
626  *
627  * @param   data    The data.
628  *
629  * @return  The 64bits.
630  */
631 
fetch_64bits(const std::uint8_t * data)632 inline std::uint64_t fetch_64bits(const std::uint8_t *data)
633 {
634 	return std::uint64_t(data[0]) | (std::uint64_t(data[1]) << 8) |
635 			(std::uint64_t(data[2]) << 16) | (std::uint64_t(data[3]) << 24) |
636 			(std::uint64_t(data[4]) << 32) | (std::uint64_t(data[5]) << 40) |
637 			(std::uint64_t(data[6]) << 48) | (std::uint64_t(data[7]) << 56);
638 }
639 
640 
641 /*-------------------------------------------------
642     put_16bits - write 16 bits in LSB order
643     to the given buffer
644 -------------------------------------------------*/
645 
646 /**
647  * @fn  static inline void put_16bits(std::uint8_t *data, std::uint16_t value)
648  *
649  * @brief   Puts the 16bits.
650  *
651  * @param [in,out]  data    If non-null, the data.
652  * @param   value           The value.
653  */
654 
put_16bits(std::uint8_t * data,std::uint16_t value)655 inline void put_16bits(std::uint8_t *data, std::uint16_t value)
656 {
657 	data[0] = value >> 0;
658 	data[1] = value >> 8;
659 }
660 
661 
662 /*-------------------------------------------------
663     put_32bits - write 32 bits in LSB order
664     to the given buffer
665 -------------------------------------------------*/
666 
667 /**
668  * @fn  static inline void put_32bits(std::uint8_t *data, std::uint32_t value)
669  *
670  * @brief   Puts the 32bits.
671  *
672  * @param [in,out]  data    If non-null, the data.
673  * @param   value           The value.
674  */
675 
put_32bits(std::uint8_t * data,std::uint32_t value)676 inline void put_32bits(std::uint8_t *data, std::uint32_t value)
677 {
678 	data[0] = value >> 0;
679 	data[1] = value >> 8;
680 	data[2] = value >> 16;
681 	data[3] = value >> 24;
682 }
683 
684 
685 /*-------------------------------------------------
686     put_64bits - write 64 bits in LSB order
687     to the given buffer
688 -------------------------------------------------*/
689 
690 /**
691  * @fn  static inline void put_64bits(std::uint8_t *data, std::uint64_t value)
692  *
693  * @brief   Puts the 64bits.
694  *
695  * @param [in,out]  data    If non-null, the data.
696  * @param   value           The value.
697  */
698 
put_64bits(std::uint8_t * data,std::uint64_t value)699 inline void put_64bits(std::uint8_t *data, std::uint64_t value)
700 {
701 	data[0] = value >> 0;
702 	data[1] = value >> 8;
703 	data[2] = value >> 16;
704 	data[3] = value >> 24;
705 	data[4] = value >> 32;
706 	data[5] = value >> 40;
707 	data[6] = value >> 48;
708 	data[7] = value >> 56;
709 }
710 
711 
712 /**
713  * @fn  static void u64toa(std::uint64_t val, char *output)
714  *
715  * @brief   64toas.
716  *
717  * @param   val             The value.
718  * @param [in,out]  output  If non-null, the output.
719  */
720 
u64toa(std::uint64_t val,char * output)721 inline void u64toa(std::uint64_t val, char *output)
722 {
723 	auto lo = std::uint32_t(val & 0xffffffff);
724 	auto hi = std::uint32_t(val >> 32);
725 	if (hi != 0)
726 		sprintf(output, "%X%08X", hi, lo);
727 	else
728 		sprintf(output, "%X", lo);
729 }
730 
731 
732 /*-------------------------------------------------
733     set_stream_chunk_info - set the chunk info
734     for a given chunk within a stream
735 -------------------------------------------------*/
736 
737 /**
738  * @fn  static inline avi_error set_stream_chunk_info(avi_stream *stream, std::uint32_t index, std::uint64_t offset, std::uint32_t length)
739  *
740  * @brief   Sets stream chunk information.
741  *
742  * @param [in,out]  stream  If non-null, the stream.
743  * @param   index           Zero-based index of the.
744  * @param   offset          The offset.
745  * @param   length          The length.
746  *
747  * @return  An avi_error.
748  */
749 
set_chunk_info(std::uint32_t index,std::uint64_t offset,std::uint32_t length)750 inline avi_file::error avi_stream::set_chunk_info(std::uint32_t index, std::uint64_t offset, std::uint32_t length)
751 {
752 	/* if we need to allocate more, allocate more */
753 	if (index >= m_chunks.capacity())
754 	{
755 		try { m_chunks.reserve((std::max<std::size_t>)(index, m_chunks.capacity() + 1000)); }
756 		catch (...) { return avi_file::error::NO_MEMORY; }
757 	}
758 
759 	/* update the number of chunks */
760 	m_chunks.resize((std::max<std::size_t>)(m_chunks.size(), index + 1));
761 
762 	/* set the data */
763 	m_chunks[index].offset = offset;
764 	m_chunks[index].length = length;
765 
766 	return avi_file::error::NONE;
767 }
768 
769 
set_strh_data(std::uint8_t const * data,std::uint32_t size)770 inline avi_file::error avi_stream::set_strh_data(std::uint8_t const *data, std::uint32_t size)
771 {
772 	m_type = fetch_32bits(&data[0]);
773 	m_scale = fetch_32bits(&data[20]);
774 	m_rate = fetch_32bits(&data[24]);
775 	m_samples = fetch_32bits(&data[32]);
776 	return avi_file::error::NONE;
777 }
778 
779 
set_strf_data(std::uint8_t const * data,std::uint32_t size)780 inline avi_file::error avi_stream::set_strf_data(std::uint8_t const *data, std::uint32_t size)
781 {
782 	/* audio and video streams have differing headers */
783 	if (m_type == STREAMTYPE_VIDS)
784 	{
785 		m_width = fetch_32bits(&data[4]);
786 		m_height = fetch_32bits(&data[8]);
787 		m_depth = fetch_16bits(&data[14]);
788 		m_format = fetch_32bits(&data[16]);
789 
790 		/* extra extraction for HuffYUV data */
791 		if ((m_format == FORMAT_HFYU) && (size >= 56))
792 		{
793 			avi_file::error const avierr = huffyuv_extract_tables(data, size);
794 			if (avierr != avi_file::error::NONE)
795 				return avierr;
796 		}
797 	}
798 	else if (m_type == STREAMTYPE_AUDS)
799 	{
800 		m_channels = fetch_16bits(&data[2]);
801 		m_samplebits = fetch_16bits(&data[14]);
802 		m_samplerate = fetch_32bits(&data[4]);
803 	}
804 	return avi_file::error::NONE;
805 }
806 
807 
808 /*-------------------------------------------------
809     get_video_stream - return a pointer to the
810     video stream
811 -------------------------------------------------*/
812 
813 /**
814  * @fn  static inline avi_stream *get_video_stream()
815  *
816  * @brief   Gets video stream.
817  *
818  * @return  null if it fails, else the video stream.
819  */
820 
get_video_stream()821 inline avi_stream *avi_file_impl::get_video_stream()
822 {
823 	for (int streamnum = 0; streamnum < m_streams.size(); streamnum++)
824 		if (m_streams[streamnum].type() == STREAMTYPE_VIDS)
825 			return &m_streams[streamnum];
826 
827 	return nullptr;
828 }
829 
830 
831 /*-------------------------------------------------
832     get_audio_stream - return a pointer to the
833     audio stream for the 'n'th channel
834 -------------------------------------------------*/
835 
836 /**
837  * @fn  static inline avi_stream *get_audio_stream(avi_file *file, int channel, int *offset)
838  *
839  * @brief   Gets audio stream.
840  *
841  * @param [in,out]  file    If non-null, the file.
842  * @param   channel         The channel.
843  * @param [in,out]  offset  If non-null, the offset.
844  *
845  * @return  null if it fails, else the audio stream.
846  */
847 
get_audio_stream(int channel,int & offset)848 inline avi_stream *avi_file_impl::get_audio_stream(int channel, int &offset)
849 {
850 	/* find the audios stream */
851 	for (int streamnum = 0; streamnum < m_streams.size(); streamnum++)
852 		if (m_streams[streamnum].type() == STREAMTYPE_AUDS)
853 		{
854 			if (channel < m_streams[streamnum].channels())
855 			{
856 				offset = channel;
857 				return &m_streams[streamnum];
858 			}
859 			channel -= m_streams[streamnum].channels();
860 		}
861 
862 	return nullptr;
863 }
864 
865 
866 /*-------------------------------------------------
867     compute_idx1_size - compute the size of the
868     idx1 chunk
869 -------------------------------------------------*/
870 
871 /**
872  * @fn  static inline std::uint32_t compute_idx1_size(avi_file *file)
873  *
874  * @brief   Calculates the index 1 size.
875  *
876  * @param [in,out]  file    If non-null, the file.
877  *
878  * @return  The calculated index 1 size.
879  */
880 
compute_idx1_size() const881 inline std::uint32_t avi_file_impl::compute_idx1_size() const
882 {
883 	int chunks = 0;
884 
885 	/* count chunks in streams */
886 	for (int strnum = 0; strnum < m_streams.size(); strnum++)
887 		chunks += m_streams[strnum].chunks();
888 
889 	return chunks * 16 + 8;
890 }
891 
892 
893 /*-------------------------------------------------
894     get_chunkid_for_stream - make a chunk id for
895     a given stream
896 -------------------------------------------------*/
897 
898 /**
899  * @fn  static inline std::uint32_t get_chunkid_for_stream(avi_file *file, avi_stream *stream)
900  *
901  * @brief   Gets chunkid for stream.
902  *
903  * @param [in,out]  file    If non-null, the file.
904  * @param [in,out]  stream  If non-null, the stream.
905  *
906  * @return  The chunkid for stream.
907  */
908 
get_chunkid_for_stream(const avi_stream * stream) const909 inline std::uint32_t avi_file_impl::get_chunkid_for_stream(const avi_stream *stream) const
910 {
911 	std::uint32_t chunkid;
912 
913 	chunkid = AVI_FOURCC('0' + (stream - &m_streams[0]) / 10, '0' +  (stream - &m_streams[0]) % 10, 0, 0);
914 	if (stream->type() == STREAMTYPE_VIDS)
915 		chunkid |= (stream->format() == 0) ? CHUNKTYPE_XXDB : CHUNKTYPE_XXDC;
916 	else if (stream->type() == STREAMTYPE_AUDS)
917 		chunkid |= CHUNKTYPE_XXWB;
918 
919 	return chunkid;
920 }
921 
922 
923 /*-------------------------------------------------
924     framenum_to_samplenum - given a video frame
925     number, get the first sample number
926 -------------------------------------------------*/
927 
928 /**
929  * @fn  static inline std::uint32_t framenum_to_samplenum(avi_file *file, std::uint32_t framenum)
930  *
931  * @brief   Framenum to samplenum.
932  *
933  * @param [in,out]  file    If non-null, the file.
934  * @param   framenum        The framenum.
935  *
936  * @return  An std::uint32_t.
937  */
938 
framenum_to_samplenum(std::uint32_t framenum) const939 inline std::uint32_t avi_file_impl::framenum_to_samplenum(std::uint32_t framenum) const
940 {
941 	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);
942 }
943 
944 
945 /*-------------------------------------------------
946     expand_tempbuffer - expand the file's
947     tempbuffer if necessary to contain the
948     requested amount of data
949 -------------------------------------------------*/
950 
951 /**
952  * @fn  static inline avi_error expand_tempbuffer(avi_file *file, std::uint32_t length)
953  *
954  * @brief   Expand tempbuffer.
955  *
956  * @param [in,out]  file    If non-null, the file.
957  * @param   length          The length.
958  *
959  * @return  An avi_error.
960  */
961 
expand_tempbuffer(std::uint32_t length)962 inline avi_file::error avi_file_impl::expand_tempbuffer(std::uint32_t length)
963 {
964 	/* expand the tempbuffer to hold the data if necessary */
965 	if (length > m_tempbuffer.size())
966 	{
967 		try { m_tempbuffer.resize(2 * length); }
968 		catch (...) { return error::NO_MEMORY; }
969 	}
970 	return error::NONE;
971 }
972 
973 
974 /*-------------------------------------------------
975     rgb32_compress_to_rgb - "compress" an RGB32
976     bitmap to an RGB encoded frame
977 -------------------------------------------------*/
978 
979 /**
980  * @fn  static avi_error rgb32_compress_to_rgb(avi_stream *stream, const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes)
981  *
982  * @brief   RGB 32 compress to RGB.
983  *
984  * @param [in,out]  stream  If non-null, the stream.
985  * @param   bitmap          The bitmap.
986  * @param [in,out]  data    If non-null, the data.
987  * @param   numbytes        The numbytes.
988  *
989  * @return  An avi_error.
990  */
991 
rgb32_compress_to_rgb(const bitmap_rgb32 & bitmap,std::uint8_t * data,std::uint32_t numbytes) const992 avi_file::error avi_stream::rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const
993 {
994 	int const height = (std::min<int>)(m_height, bitmap.height());
995 	int const width = (std::min<int>)(m_width, bitmap.width());
996 	std::uint8_t *const dataend = data + numbytes;
997 	int x, y;
998 
999 	/* compressed video */
1000 	for (y = 0; y < height; y++)
1001 	{
1002 		const std::uint32_t *source = &bitmap.pix(y);
1003 		std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3;
1004 
1005 		for (x = 0; x < width && dest < dataend; x++)
1006 		{
1007 			rgb_t pix = *source++;
1008 			*dest++ = pix.b();
1009 			*dest++ = pix.g();
1010 			*dest++ = pix.r();
1011 		}
1012 
1013 		/* fill in any blank space on the right */
1014 		for ( ; x < m_width && dest < dataend; x++)
1015 		{
1016 			*dest++ = 0;
1017 			*dest++ = 0;
1018 			*dest++ = 0;
1019 		}
1020 	}
1021 
1022 	/* fill in any blank space on the bottom */
1023 	for ( ; y < m_height; y++)
1024 	{
1025 		std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3;
1026 		for (x = 0; x < m_width && dest < dataend; x++)
1027 		{
1028 			*dest++ = 0;
1029 			*dest++ = 0;
1030 			*dest++ = 0;
1031 		}
1032 	}
1033 
1034 	return avi_file::error::NONE;
1035 }
1036 
1037 
1038 /*-------------------------------------------------
1039     yuv_decompress_to_yuy16 - decompress a YUV
1040     encoded frame to a YUY16 bitmap
1041 -------------------------------------------------*/
1042 
1043 /**
1044  * @fn  static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap)
1045  *
1046  * @brief   Yuv decompress to yuy 16.
1047  *
1048  * @param [in,out]  stream  If non-null, the stream.
1049  * @param   data            The data.
1050  * @param   numbytes        The numbytes.
1051  * @param [in,out]  bitmap  The bitmap.
1052  *
1053  * @return  An avi_error.
1054  */
1055 
yuv_decompress_to_yuy16(const std::uint8_t * data,std::uint32_t numbytes,bitmap_yuy16 & bitmap) const1056 avi_file::error avi_stream::yuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const
1057 {
1058 	auto const *const dataend = reinterpret_cast<const std::uint16_t *>(data + numbytes);
1059 	int x, y;
1060 
1061 	/* compressed video */
1062 	for (y = 0; y < m_height; y++)
1063 	{
1064 		const std::uint16_t *source = reinterpret_cast<const std::uint16_t *>(data) + y * m_width;
1065 		std::uint16_t *dest = &bitmap.pix(y);
1066 
1067 		/* switch off the compression */
1068 		switch (m_format)
1069 		{
1070 		case FORMAT_UYVY:
1071 			for (x = 0; x < m_width && source < dataend; x++)
1072 				*dest++ = *source++;
1073 			break;
1074 
1075 		case FORMAT_VYUY:
1076 		case FORMAT_YUY2:
1077 			for (x = 0; x < m_width && source < dataend; x++)
1078 			{
1079 				std::uint16_t pix = *source++;
1080 				*dest++ = (pix >> 8) | (pix << 8);
1081 			}
1082 			break;
1083 		}
1084 	}
1085 
1086 	return avi_file::error::NONE;
1087 }
1088 
1089 
1090 /*-------------------------------------------------
1091     yuy16_compress_to_yuy - "compress" a YUY16
1092     bitmap to a YUV encoded frame
1093 -------------------------------------------------*/
1094 
1095 /**
1096  * @fn  static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes)
1097  *
1098  * @brief   Yuy 16 compress to yuy.
1099  *
1100  * @param [in,out]  stream  If non-null, the stream.
1101  * @param   bitmap          The bitmap.
1102  * @param [in,out]  data    If non-null, the data.
1103  * @param   numbytes        The numbytes.
1104  *
1105  * @return  An avi_error.
1106  */
1107 
yuy16_compress_to_yuy(const bitmap_yuy16 & bitmap,std::uint8_t * data,std::uint32_t numbytes) const1108 avi_file::error avi_stream::yuy16_compress_to_yuy(const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const
1109 {
1110 	auto *const dataend = reinterpret_cast<std::uint16_t *>(data + numbytes);
1111 	int x, y;
1112 
1113 	/* compressed video */
1114 	for (y = 0; y < m_height; y++)
1115 	{
1116 		const std::uint16_t *source = &bitmap.pix(y);
1117 		std::uint16_t *dest = reinterpret_cast<std::uint16_t *>(data) + y * m_width;
1118 
1119 		/* switch off the compression */
1120 		switch (m_format)
1121 		{
1122 		case FORMAT_UYVY:
1123 			for (x = 0; x < m_width && dest < dataend; x++)
1124 				*dest++ = *source++;
1125 			break;
1126 
1127 		case FORMAT_VYUY:
1128 		case FORMAT_YUY2:
1129 			for (x = 0; x < m_width && dest < dataend; x++)
1130 			{
1131 				std::uint16_t pix = *source++;
1132 				*dest++ = (pix >> 8) | (pix << 8);
1133 			}
1134 			break;
1135 		}
1136 	}
1137 
1138 	return avi_file::error::NONE;
1139 }
1140 
1141 
1142 /*-------------------------------------------------
1143     huffyuv_extract_tables - extract HuffYUV
1144     tables
1145 -------------------------------------------------*/
1146 
1147 /**
1148  * @fn  static avi_error huffyuv_extract_tables(avi_stream *stream, const std::uint8_t *chunkdata, std::uint32_t size)
1149  *
1150  * @brief   Huffyuv extract tables.
1151  *
1152  * @param [in,out]  stream  If non-null, the stream.
1153  * @param   chunkdata       The chunkdata.
1154  * @param   size            The size.
1155  *
1156  * @return  An avi_error.
1157  */
1158 
huffyuv_extract_tables(const std::uint8_t * chunkdata,std::uint32_t size)1159 avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata, std::uint32_t size)
1160 {
1161 	const std::uint8_t *const chunkend = chunkdata + size;
1162 
1163 	/* allocate memory for the data */
1164 	std::unique_ptr<huffyuv_data> huffyuv;
1165 	try { huffyuv = std::make_unique<huffyuv_data>(); }
1166 	catch (...) { return avi_file::error::NO_MEMORY; }
1167 
1168 	/* extract predictor information */
1169 	if (&chunkdata[40] >= chunkend)
1170 		return avi_file::error::INVALID_DATA;
1171 	huffyuv->predictor = chunkdata[40];
1172 
1173 	/* make sure it's the left predictor */
1174 	if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) != HUFFYUV_PREDICT_LEFT)
1175 		return avi_file::error::UNSUPPORTED_VIDEO_FORMAT;
1176 
1177 	/* make sure it's 16bpp YUV data */
1178 	if (chunkdata[41] != 16)
1179 		return avi_file::error::UNSUPPORTED_VIDEO_FORMAT;
1180 	chunkdata += 44;
1181 
1182 	/* loop over tables */
1183 	for (int tabnum = 0; tabnum < 3; tabnum++)
1184 	{
1185 		huffyuv_table &table = huffyuv->table[tabnum];
1186 		std::uint32_t curbits, bitadd;
1187 		std::uint16_t bitsat16 = 0;
1188 		int offset = 0, bits;
1189 
1190 		/* loop until we populate the whole table */
1191 		while (offset < 256)
1192 		{
1193 			int data, shift, count, i;
1194 
1195 			/* extract the next run */
1196 			if (chunkdata >= chunkend)
1197 				return avi_file::error::INVALID_DATA;
1198 			data = *chunkdata++;
1199 			shift = data & 0x1f;
1200 			count = data >> 5;
1201 
1202 			/* zero count means next whole byte is a count */
1203 			if (count == 0)
1204 			{
1205 				if (chunkdata >= chunkend)
1206 					return avi_file::error::INVALID_DATA;
1207 				count = *chunkdata++;
1208 			}
1209 			for (i = 0; i < count; i++)
1210 				table.shift[offset++] = shift;
1211 		}
1212 
1213 		/* now determine bit patterns and masks */
1214 		curbits = 0;
1215 		for (bits = 31; bits >= 0; bits--)
1216 		{
1217 			bitadd = 1 << (32 - bits);
1218 
1219 			/* make sure we've cleared out all the bits below */
1220 			if ((curbits & (bitadd - 1)) != 0)
1221 				return avi_file::error::INVALID_DATA;
1222 
1223 			/* find all entries with this shift count and assign them */
1224 			for (offset = 0; offset < 256; offset++)
1225 				if (table.shift[offset] == bits)
1226 				{
1227 					table.bits[offset] = curbits;
1228 					table.mask[offset] = ~(bitadd - 1);
1229 					curbits += bitadd;
1230 				}
1231 
1232 			/* remember the bit pattern when we complete all the 17-bit codes */
1233 			if (bits == 17)
1234 				bitsat16 = curbits >> 16;
1235 		}
1236 
1237 		/* allocate the number of extra lookup tables we need */
1238 		if (bitsat16 > 0)
1239 		{
1240 			try { table.extralookup.resize(bitsat16 * 65536); }
1241 			catch (...) { return avi_file::error::NO_MEMORY; }
1242 			for (offset = 0; offset < bitsat16; offset++)
1243 				table.baselookup[offset] = (offset << 8) | 0;
1244 		}
1245 
1246 		/* then create lookup tables */
1247 		for (offset = 0; offset < 256; offset++)
1248 			if (table.shift[offset] > 16)
1249 			{
1250 				std::uint16_t *tablebase = &table.extralookup[(table.bits[offset] >> 16) * 65536];
1251 				std::uint32_t start = table.bits[offset] & 0xffff;
1252 				std::uint32_t end = start + ((1 << (32 - table.shift[offset])) - 1);
1253 				while (start <= end)
1254 					tablebase[start++] = (offset << 8) | (table.shift[offset] - 16);
1255 			}
1256 			else if (table.shift[offset] > 0)
1257 			{
1258 				std::uint32_t start = table.bits[offset] >> 16;
1259 				std::uint32_t end = start + ((1 << (16 - table.shift[offset])) - 1);
1260 				while (start <= end)
1261 					table.baselookup[start++] = (offset << 8) | table.shift[offset];
1262 			}
1263 	}
1264 
1265 	m_huffyuv = std::move(huffyuv);
1266 	return avi_file::error::NONE;
1267 }
1268 
1269 
1270 /*-------------------------------------------------
1271     huffyuv_decompress_to_yuy16 - decompress a
1272     HuffYUV-encoded frame to a YUY16 bitmap
1273 -------------------------------------------------*/
1274 
1275 /**
1276  * @fn  static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap)
1277  *
1278  * @brief   Huffyuv decompress to yuy 16.
1279  *
1280  * @param [in,out]  stream  If non-null, the stream.
1281  * @param   data            The data.
1282  * @param   numbytes        The numbytes.
1283  * @param [in,out]  bitmap  The bitmap.
1284  *
1285  * @return  An avi_error.
1286  */
1287 
huffyuv_decompress_to_yuy16(const std::uint8_t * data,std::uint32_t numbytes,bitmap_yuy16 & bitmap) const1288 avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const
1289 {
1290 	int prevlines = (m_height > 288) ? 2 : 1;
1291 	std::uint8_t lastprevy = 0, lastprevcb = 0, lastprevcr = 0;
1292 	std::uint8_t lasty = 0, lastcb = 0, lastcr = 0;
1293 	std::uint8_t bitsinbuffer = 0;
1294 	std::uint32_t bitbuffer = 0;
1295 	std::uint32_t dataoffs = 0;
1296 	int x, y;
1297 
1298 	/* compressed video */
1299 	for (y = 0; y < m_height; y++)
1300 	{
1301 		std::uint16_t *dest = &bitmap.pix(y);
1302 
1303 		/* handle the first four bytes independently */
1304 		x = 0;
1305 		if (y == 0)
1306 		{
1307 			/* first DWORD is stored as YUY2 */
1308 			lasty = data[dataoffs++];
1309 			lastcb = data[dataoffs++];
1310 			*dest++ = (lasty << 8) | lastcb;
1311 			lasty = data[dataoffs++];
1312 			lastcr = data[dataoffs++];
1313 			*dest++ = (lasty << 8) | lastcr;
1314 			x = 2;
1315 		}
1316 
1317 		/* loop over pixels */
1318 		for ( ; x < m_width; x++)
1319 		{
1320 			huffyuv_table const &ytable = m_huffyuv->table[0];
1321 			huffyuv_table const &ctable = m_huffyuv->table[1 + (x & 1)];
1322 			std::uint16_t huffdata;
1323 			int shift;
1324 
1325 			/* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
1326 			while (bitsinbuffer <= 24 && dataoffs < numbytes)
1327 			{
1328 				bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
1329 				bitsinbuffer += 8;
1330 			}
1331 
1332 			/* look up the Y component */
1333 			huffdata = ytable.baselookup[bitbuffer >> 16];
1334 			shift = huffdata & 0xff;
1335 			if (shift == 0)
1336 			{
1337 				bitsinbuffer -= 16;
1338 				bitbuffer <<= 16;
1339 
1340 				/* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
1341 				while (bitsinbuffer <= 24 && dataoffs < numbytes)
1342 				{
1343 					bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
1344 					bitsinbuffer += 8;
1345 				}
1346 
1347 				huffdata = ytable.extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)];
1348 				shift = huffdata & 0xff;
1349 			}
1350 			bitsinbuffer -= shift;
1351 			bitbuffer <<= shift;
1352 			std::uint16_t const pixel = huffdata & 0xff00;
1353 
1354 			/* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
1355 			while (bitsinbuffer <= 24 && dataoffs < numbytes)
1356 			{
1357 				bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
1358 				bitsinbuffer += 8;
1359 			}
1360 
1361 			/* look up the Cb/Cr component */
1362 			huffdata = ctable.baselookup[bitbuffer >> 16];
1363 			shift = huffdata & 0xff;
1364 			if (shift == 0)
1365 			{
1366 				bitsinbuffer -= 16;
1367 				bitbuffer <<= 16;
1368 
1369 				/* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
1370 				while (bitsinbuffer <= 24 && dataoffs < numbytes)
1371 				{
1372 					bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
1373 					bitsinbuffer += 8;
1374 				}
1375 
1376 				huffdata = ctable.extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)];
1377 				shift = huffdata & 0xff;
1378 			}
1379 			bitsinbuffer -= shift;
1380 			bitbuffer <<= shift;
1381 			*dest++ = pixel | (huffdata >> 8);
1382 		}
1383 	}
1384 
1385 	/* apply deltas */
1386 	lastprevy = lastprevcb = lastprevcr = 0;
1387 	for (y = 0; y < m_height; y++)
1388 	{
1389 		std::uint16_t *prevrow = &bitmap.pix(y - prevlines);
1390 		std::uint16_t *dest = &bitmap.pix(y);
1391 
1392 		/* handle the first four bytes independently */
1393 		x = 0;
1394 		if (y == 0)
1395 		{
1396 			/* lasty, lastcr, lastcb are set up previously */
1397 			x = 2;
1398 		}
1399 
1400 		/* left predict or gradient predict */
1401 		if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_LEFT ||
1402 			((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT))
1403 		{
1404 			/* first do left deltas */
1405 			for ( ; x < m_width; x += 2)
1406 			{
1407 				std::uint16_t pixel0 = dest[x + 0];
1408 				std::uint16_t pixel1 = dest[x + 1];
1409 
1410 				lasty += pixel0 >> 8;
1411 				lastcb += pixel0;
1412 				dest[x + 0] = (lasty << 8) | lastcb;
1413 
1414 				lasty += pixel1 >> 8;
1415 				lastcr += pixel1;
1416 				dest[x + 1] = (lasty << 8) | lastcr;
1417 			}
1418 
1419 			/* for gradient, we then add in the previous row */
1420 			if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT && y >= prevlines)
1421 				for (x = 0; x < m_width; x++)
1422 				{
1423 					std::uint16_t curpix = dest[x];
1424 					std::uint16_t prevpix = prevrow[x];
1425 					std::uint8_t ysum = (curpix >> 8) + (prevpix >> 8);
1426 					std::uint8_t csum = curpix + prevpix;
1427 					dest[x] = (ysum << 8) | csum;
1428 				}
1429 		}
1430 
1431 		/* median predict on rows > 0 */
1432 		else if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_MEDIAN && y >= prevlines)
1433 		{
1434 			for ( ; x < m_width; x += 2)
1435 			{
1436 				std::uint16_t prevpixel0 = prevrow[x + 0];
1437 				std::uint16_t prevpixel1 = prevrow[x + 1];
1438 				std::uint16_t pixel0 = dest[x + 0];
1439 				std::uint16_t pixel1 = dest[x + 1];
1440 				std::uint8_t a, b, c;
1441 
1442 				/* compute previous, above, and (prev + above - above-left) */
1443 				a = lasty;
1444 				b = prevpixel0 >> 8;
1445 				c = lastprevy;
1446 				lastprevy = b;
1447 				if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
1448 				if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
1449 				if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
1450 				lasty = (pixel0 >> 8) + b;
1451 
1452 				/* compute previous, above, and (prev + above - above-left) */
1453 				a = lastcb;
1454 				b = prevpixel0 & 0xff;
1455 				c = lastprevcb;
1456 				lastprevcb = b;
1457 				if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
1458 				if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
1459 				if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
1460 				lastcb = (pixel0 & 0xff) + b;
1461 				dest[x + 0] = (lasty << 8) | lastcb;
1462 
1463 				/* compute previous, above, and (prev + above - above-left) */
1464 				a = lasty;
1465 				b = prevpixel1 >> 8;
1466 				c = lastprevy;
1467 				lastprevy = b;
1468 				if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
1469 				if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
1470 				if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
1471 				lasty = (pixel1 >> 8) + b;
1472 
1473 				/* compute previous, above, and (prev + above - above-left) */
1474 				a = lastcr;
1475 				b = prevpixel1 & 0xff;
1476 				c = lastprevcr;
1477 				lastprevcr = b;
1478 				if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
1479 				if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
1480 				if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
1481 				lastcr = (pixel1 & 0xff) + b;
1482 				dest[x + 1] = (lasty << 8) | lastcr;
1483 			}
1484 		}
1485 	}
1486 
1487 	return avi_file::error::NONE;
1488 }
1489 
1490 
1491 /*-------------------------------------------------
1492     uncompressed_rgb24_to_argb32 - convert a raw
1493     RGB24-encoded frame to an ARGB32 bitmap
1494 -------------------------------------------------*/
1495 
uncompressed_rgb24_to_argb32(const std::uint8_t * data,std::uint32_t numbytes,bitmap_argb32 & bitmap) const1496 avi_file::error avi_stream::uncompressed_rgb24_to_argb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_argb32 &bitmap) const
1497 {
1498 	std::uint32_t dataoffs = 0;
1499 
1500 	/* uncompressed video */
1501 	for (int y = 0; y < m_height; y++)
1502 	{
1503 		std::uint32_t *dest = &bitmap.pix(y);
1504 
1505 		/* loop over pixels */
1506 		for (int x = 0; x < m_width; x++)
1507 		{
1508 			const uint8_t b = data[dataoffs++];
1509 			const uint8_t g = data[dataoffs++];
1510 			const uint8_t r = data[dataoffs++];
1511 			*dest++ = 0xff << 24 | r << 16 | g << 8 | b;
1512 		}
1513 	}
1514 
1515 	return avi_file::error::NONE;
1516 }
1517 
1518 
1519 /*-------------------------------------------------
1520     uncompressed_yuv420p_to_argb32 - convert a
1521     YUV420p-encoded frame to an ARGB32 bitmap
1522 -------------------------------------------------*/
1523 
uncompressed_yuv420p_to_argb32(const std::uint8_t * data,std::uint32_t numbytes,bitmap_argb32 & bitmap) const1524 avi_file::error avi_stream::uncompressed_yuv420p_to_argb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_argb32 &bitmap) const
1525 {
1526 	const int width = bitmap.width();
1527 	const int height = bitmap.height();
1528 	const int size_total = width * height;
1529 
1530 	/* uncompressed video */
1531 	for (int y = 0; y < m_height; y++)
1532 	{
1533 		std::uint32_t *dest = &bitmap.pix(y);
1534 
1535 		/* loop over pixels */
1536 		for (int x = 0; x < m_width; x++)
1537 		{
1538 			const uint8_t luma = data[y * width + x];
1539 			const uint8_t u = data[(y / 2) * (width / 2) + x / 2 + size_total];
1540 			const uint8_t v = data[(y / 2) * (width / 2) + x / 2 + size_total + size_total / 4];
1541 
1542 			int r = luma + (1.370705f * (v - 0x80));
1543 			int g = luma - (0.698001f * (v - 0x80)) - (0.337633f * (u - 0x80));
1544 			int b = luma + (1.732446f * (u - 0x80));
1545 
1546 			r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
1547 			g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
1548 			b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
1549 
1550 			*dest++ = 0xff << 24 | r << 16 | g << 8 | b;
1551 		}
1552 	}
1553 
1554 	return avi_file::error::NONE;
1555 }
1556 
1557 
1558 /*-------------------------------------------------
1559     avi_close - close an AVI movie file
1560 -------------------------------------------------*/
1561 
1562 /**
1563  * @fn  avi_error avi_close(avi_file *file)
1564  *
1565  * @brief   Avi close.
1566  *
1567  * @param [in,out]  file    If non-null, the file.
1568  *
1569  * @return  An avi_error.
1570  */
1571 
~avi_file_impl()1572 avi_file_impl::~avi_file_impl()
1573 {
1574 	error avierr = error::NONE;
1575 
1576 	/* if we're creating a new file, finalize it by writing out the non-media chunks */
1577 	if (m_type == FILETYPE_CREATE)
1578 	{
1579 		/* flush any pending sound data */
1580 		avierr = soundbuf_flush(false);
1581 
1582 		/* close the movi chunk */
1583 		if (avierr == error::NONE)
1584 			avierr = chunk_close();
1585 
1586 		/* if this is the first RIFF chunk, write an idx1 */
1587 		if (avierr == error::NONE && m_riffbase == 0)
1588 			avierr = write_idx1_chunk();
1589 
1590 		/* update the strh and indx chunks for each stream */
1591 		for (int strnum = 0; strnum < m_streams.size(); strnum++)
1592 		{
1593 			if (avierr == error::NONE)
1594 				avierr = write_strh_chunk(m_streams[strnum], false);
1595 			if (avierr == error::NONE)
1596 				avierr = write_indx_chunk(m_streams[strnum], false);
1597 		}
1598 
1599 		/* update the avih chunk */
1600 		if (avierr == error::NONE)
1601 			avierr = write_avih_chunk(false);
1602 
1603 		/* close the RIFF chunk */
1604 		if (avierr == error::NONE)
1605 			avierr = chunk_close();
1606 	}
1607 
1608 	/* close the file */
1609 	m_file.reset();
1610 
1611 	//return avierr;
1612 }
1613 
1614 
1615 /*-------------------------------------------------
1616     avi_printf_chunks - print the chunks in a file
1617 -------------------------------------------------*/
1618 
1619 /**
1620  * @fn  void avi_printf_chunks(avi_file *file)
1621  *
1622  * @brief   Avi printf chunks.
1623  *
1624  * @param [in,out]  file    If non-null, the file.
1625  */
1626 
printf_chunks()1627 void avi_file_impl::printf_chunks()
1628 {
1629 	printf_chunk_recursive(&m_rootchunk, 0);
1630 }
1631 
1632 
1633 /*-------------------------------------------------
1634     get_movie_info - return a pointer to the
1635     movie info
1636 -------------------------------------------------*/
1637 
1638 /**
1639  * @fn  const avi_movie_info *avi_get_movie_info(avi_file *file)
1640  *
1641  * @brief   Avi get movie information.
1642  *
1643  * @param [in,out]  file    If non-null, the file.
1644  *
1645  * @return  null if it fails, else an avi_movie_info*.
1646  */
1647 
get_movie_info() const1648 avi_file::movie_info const &avi_file_impl::get_movie_info() const
1649 {
1650 	return m_info;
1651 }
1652 
1653 
1654 /*-------------------------------------------------
1655     avi_frame_to_sample - convert a frame index
1656     to a sample index
1657 -------------------------------------------------*/
1658 
1659 /**
1660  * @fn  std::uint32_t avi_first_sample_in_frame(avi_file *file, std::uint32_t framenum)
1661  *
1662  * @brief   Avi first sample in frame.
1663  *
1664  * @param [in,out]  file    If non-null, the file.
1665  * @param   framenum        The framenum.
1666  *
1667  * @return  An std::uint32_t.
1668  */
1669 
first_sample_in_frame(std::uint32_t framenum) const1670 std::uint32_t avi_file_impl::first_sample_in_frame(std::uint32_t framenum) const
1671 {
1672 	return framenum_to_samplenum(framenum);
1673 }
1674 
1675 
1676 /*-------------------------------------------------
1677     read_uncompressed_video_frame - read raw video
1678     data for a particular frame from the AVI file,
1679     converting to ARGB32 format
1680 -------------------------------------------------*/
1681 
read_uncompressed_video_frame(std::uint32_t framenum,bitmap_argb32 & bitmap)1682 avi_file::error avi_file_impl::read_uncompressed_video_frame(std::uint32_t framenum, bitmap_argb32 &bitmap)
1683 {
1684 	/* get the video stream */
1685 	avi_stream *const stream = get_video_stream();
1686 	if (!stream)
1687 		return error::INVALID_STREAM;
1688 
1689 	if (stream->format() != FORMAT_UNCOMPRESSED && stream->format() != FORMAT_DIB && stream->format() != FORMAT_RGB && stream->format() != FORMAT_RAW && stream->format() != FORMAT_I420)
1690 		return error::UNSUPPORTED_VIDEO_FORMAT;
1691 
1692 	if (bitmap.width() < stream->width() || bitmap.height() < stream->height())
1693 		bitmap.resize(stream->width(), stream->height());
1694 
1695 	/* assume one chunk == one frame */
1696 	if (framenum >= stream->chunks())
1697 		return error::INVALID_FRAME;
1698 
1699 	/* we only support YUY-style bitmaps (16bpp) */
1700 	if (bitmap.width() < stream->width() || bitmap.height() < stream->height())
1701 		return error::INVALID_BITMAP;
1702 
1703 	/* expand the tempbuffer to hold the data if necessary */
1704 	error avierr = error::NONE;
1705 	avierr = expand_tempbuffer(stream->chunk(framenum).length);
1706 	if (avierr != error::NONE)
1707 		return avierr;
1708 
1709 	/* read in the data */
1710 	std::uint32_t bytes_read;
1711 	osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read);
1712 	if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length)
1713 		return error::READ_ERROR;
1714 
1715 	/* validate this is good data */
1716 	std::uint32_t const chunkid = fetch_32bits(&m_tempbuffer[0]);
1717 	if (chunkid == get_chunkid_for_stream(stream))
1718 	{
1719 		/* uncompressed YUV420p */
1720 		if (stream->format() == FORMAT_I420)
1721 			avierr = stream->uncompressed_yuv420p_to_argb32(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap);
1722 		else
1723 			avierr = stream->uncompressed_rgb24_to_argb32(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap);
1724 	}
1725 	else
1726 	{
1727 		avierr = error::INVALID_DATA;
1728 	}
1729 
1730 	return avierr;
1731 }
1732 
1733 /*-------------------------------------------------
1734     read_video_frame - read video data for a
1735     particular frame from the AVI file, converting
1736     to YUY16 format
1737 -------------------------------------------------*/
1738 
read_video_frame(std::uint32_t framenum,bitmap_yuy16 & bitmap)1739 avi_file::error avi_file_impl::read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap)
1740 {
1741 	/* get the video stream */
1742 	avi_stream *const stream = get_video_stream();
1743 	if (!stream)
1744 		return error::INVALID_STREAM;
1745 
1746 	/* validate our ability to handle the data */
1747 	if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU)
1748 		return error::UNSUPPORTED_VIDEO_FORMAT;
1749 
1750 	/* assume one chunk == one frame */
1751 	if (framenum >= stream->chunks())
1752 		return error::INVALID_FRAME;
1753 
1754 	/* we only support YUY-style bitmaps (16bpp) */
1755 	if (bitmap.width() < stream->width() || bitmap.height() < stream->height())
1756 		return error::INVALID_BITMAP;
1757 
1758 	/* expand the tempbuffer to hold the data if necessary */
1759 	error avierr = error::NONE;
1760 	avierr = expand_tempbuffer(stream->chunk(framenum).length);
1761 	if (avierr != error::NONE)
1762 		return avierr;
1763 
1764 	/* read in the data */
1765 	std::uint32_t bytes_read;
1766 	osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read);
1767 	if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length)
1768 		return error::READ_ERROR;
1769 
1770 	/* validate this is good data */
1771 	std::uint32_t const chunkid = fetch_32bits(&m_tempbuffer[0]);
1772 	if (chunkid == get_chunkid_for_stream(stream))
1773 	{
1774 		/* HuffYUV-compressed */
1775 		if (stream->format() == FORMAT_HFYU)
1776 			avierr = stream->huffyuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap);
1777 
1778 		/* other YUV-compressed */
1779 		else
1780 			avierr = stream->yuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap);
1781 	}
1782 	else
1783 		avierr = error::INVALID_DATA;
1784 
1785 	return avierr;
1786 }
1787 
1788 
1789 /*-------------------------------------------------
1790     avi_read_sound_samples - read sound sample
1791     data from an AVI file
1792 -------------------------------------------------*/
1793 
1794 /**
1795  * @fn  avi_error avi_read_sound_samples(avi_file *file, int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output)
1796  *
1797  * @brief   Avi read sound samples.
1798  *
1799  * @param [in,out]  file    If non-null, the file.
1800  * @param   channel         The channel.
1801  * @param   firstsample     The firstsample.
1802  * @param   numsamples      The numsamples.
1803  * @param [in,out]  output  If non-null, the output.
1804  *
1805  * @return  An avi_error.
1806  */
1807 
read_sound_samples(int channel,std::uint32_t firstsample,std::uint32_t numsamples,std::int16_t * output)1808 avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output)
1809 {
1810 	/* get the audio stream */
1811 	int offset = 0;
1812 	avi_stream *const stream = get_audio_stream(channel, offset);
1813 	if (!stream)
1814 		return error::INVALID_STREAM;
1815 
1816 	/* validate our ability to handle the data */
1817 	if (stream->format() != 0 || (stream->samplebits() != 8 && stream->samplebits() != 16))
1818 		return error::UNSUPPORTED_AUDIO_FORMAT;
1819 
1820 	/* verify we are in range */
1821 	if (firstsample >= stream->samples())
1822 		return error::INVALID_FRAME;
1823 	if (firstsample + numsamples > stream->samples())
1824 		numsamples = stream->samples() - firstsample;
1825 
1826 	/* determine bytes per sample */
1827 	std::uint32_t const bytes_per_sample = stream->bytes_per_sample();
1828 
1829 	/* loop until all samples have been extracted */
1830 	while (numsamples > 0)
1831 	{
1832 		std::uint32_t chunkbase = 0, chunkend = 0, chunkid;
1833 		std::uint32_t bytes_read, samples_this_chunk;
1834 		int chunknum, sampnum;
1835 
1836 		/* locate the chunk with the first sample */
1837 		for (chunknum = 0; chunknum < stream->chunks(); chunknum++)
1838 		{
1839 			chunkend = chunkbase + (stream->chunk(chunknum).length - 8) / bytes_per_sample;
1840 			if (firstsample < chunkend)
1841 				break;
1842 			chunkbase = chunkend;
1843 		}
1844 
1845 		/* if we hit the end, fill the rest with silence */
1846 		if (chunknum == stream->chunks())
1847 		{
1848 			std::memset(output, 0, numsamples * 2);
1849 			break;
1850 		}
1851 
1852 		/* expand the tempbuffer to hold the data if necessary */
1853 		error avierr = expand_tempbuffer(stream->chunk(chunknum).length);
1854 		if (avierr != error::NONE)
1855 			return avierr;
1856 
1857 		/* read in the data */
1858 		auto const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(chunknum).offset, stream->chunk(chunknum).length, bytes_read);
1859 		if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(chunknum).length)
1860 			return error::READ_ERROR;
1861 
1862 		/* validate this is good data */
1863 		chunkid = fetch_32bits(&m_tempbuffer[0]);
1864 		if (chunkid != get_chunkid_for_stream(stream))
1865 			return error::INVALID_DATA;
1866 
1867 		/* determine how many samples to copy */
1868 		samples_this_chunk = chunkend - firstsample;
1869 		samples_this_chunk = (std::min)(samples_this_chunk, numsamples);
1870 
1871 		/* extract 16-bit samples from the chunk */
1872 		if (stream->samplebits() == 16)
1873 		{
1874 			const auto *base = reinterpret_cast<const std::int16_t *>(&m_tempbuffer[8]);
1875 			base += stream->channels() * (firstsample - chunkbase) + offset;
1876 			for (sampnum = 0; sampnum < samples_this_chunk; sampnum++)
1877 			{
1878 				*output++ = little_endianize_int16(*base);
1879 				base += stream->channels();
1880 			}
1881 		}
1882 
1883 		/* extract 8-bit samples from the chunk */
1884 		else if (stream->samplebits() == 8)
1885 		{
1886 			const std::uint8_t *base = &m_tempbuffer[8];
1887 			base += stream->channels() * (firstsample - chunkbase) + offset;
1888 			for (sampnum = 0; sampnum < samples_this_chunk; sampnum++)
1889 			{
1890 				*output++ = (*base << 8) - 0x8000;
1891 				base += stream->channels();
1892 			}
1893 		}
1894 
1895 		/* update our counters */
1896 		firstsample += samples_this_chunk;
1897 		numsamples -= samples_this_chunk;
1898 	}
1899 
1900 	return error::NONE;
1901 }
1902 
1903 
1904 /*-------------------------------------------------
1905     avi_append_video_frame_yuy16 - append a frame
1906     of video in YUY16 format
1907 -------------------------------------------------*/
1908 
1909 /**
1910  * @fn  avi_error avi_append_video_frame(avi_file *file, bitmap_yuy16 &bitmap)
1911  *
1912  * @brief   Avi append video frame.
1913  *
1914  * @param [in,out]  file    If non-null, the file.
1915  * @param [in,out]  bitmap  The bitmap.
1916  *
1917  * @return  An avi_error.
1918  */
1919 
append_video_frame(bitmap_yuy16 & bitmap)1920 avi_file::error avi_file_impl::append_video_frame(bitmap_yuy16 &bitmap)
1921 {
1922 	avi_stream *const stream = get_video_stream();
1923 	error avierr;
1924 	std::uint32_t maxlength;
1925 
1926 	/* validate our ability to handle the data */
1927 	if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU)
1928 		return error::UNSUPPORTED_VIDEO_FORMAT;
1929 
1930 	/* write out any sound data first */
1931 	avierr = soundbuf_write_chunk(stream->chunks());
1932 	if (avierr != error::NONE)
1933 		return avierr;
1934 
1935 	/* make sure we have enough room */
1936 	maxlength = 2 * stream->width() * stream->height();
1937 	avierr = expand_tempbuffer(maxlength);
1938 	if (avierr != error::NONE)
1939 		return avierr;
1940 
1941 	/* now compress the data */
1942 	avierr = stream->yuy16_compress_to_yuy(bitmap, &m_tempbuffer[0], maxlength);
1943 	if (avierr != error::NONE)
1944 		return avierr;
1945 
1946 	/* write the data */
1947 	avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength);
1948 	if (avierr != error::NONE)
1949 		return avierr;
1950 
1951 	/* set the info for this new chunk */
1952 	avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8);
1953 	if (avierr != error::NONE)
1954 		return avierr;
1955 
1956 	stream->set_samples(m_info.video_numsamples = stream->chunks());
1957 
1958 	return error::NONE;
1959 }
1960 
1961 
1962 /*-------------------------------------------------
1963     avi_append_video_frame_rgb32 - append a frame
1964     of video in RGB32 format
1965 -------------------------------------------------*/
1966 
1967 /**
1968  * @fn  avi_error avi_append_video_frame(avi_file *file, bitmap_rgb32 &bitmap)
1969  *
1970  * @brief   Avi append video frame.
1971  *
1972  * @param [in,out]  file    If non-null, the file.
1973  * @param [in,out]  bitmap  The bitmap.
1974  *
1975  * @return  An avi_error.
1976  */
1977 
append_video_frame(bitmap_rgb32 & bitmap)1978 avi_file::error avi_file_impl::append_video_frame(bitmap_rgb32 &bitmap)
1979 {
1980 	avi_stream *const stream = get_video_stream();
1981 	error avierr;
1982 	std::uint32_t maxlength;
1983 
1984 	/* validate our ability to handle the data */
1985 	if (stream->format() != 0)
1986 		return error::UNSUPPORTED_VIDEO_FORMAT;
1987 
1988 	/* depth must be 24 */
1989 	if (stream->depth() != 24)
1990 		return error::UNSUPPORTED_VIDEO_FORMAT;
1991 
1992 	/* write out any sound data first */
1993 	avierr = soundbuf_write_chunk(stream->chunks());
1994 	if (avierr != error::NONE)
1995 		return avierr;
1996 
1997 	/* make sure we have enough room */
1998 	maxlength = 3 * stream->width() * stream->height();
1999 	avierr = expand_tempbuffer(maxlength);
2000 	if (avierr != error::NONE)
2001 		return avierr;
2002 
2003 	/* copy the RGB data to the destination */
2004 	avierr = stream->rgb32_compress_to_rgb(bitmap, &m_tempbuffer[0], maxlength);
2005 	if (avierr != error::NONE)
2006 		return avierr;
2007 
2008 	/* write the data */
2009 	avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength);
2010 	if (avierr != error::NONE)
2011 		return avierr;
2012 
2013 	/* set the info for this new chunk */
2014 	avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8);
2015 	if (avierr != error::NONE)
2016 		return avierr;
2017 
2018 	stream->set_samples(m_info.video_numsamples = stream->chunks());
2019 
2020 	return error::NONE;
2021 }
2022 
2023 
2024 /*-------------------------------------------------
2025     avi_append_sound_samples - append sound
2026     samples
2027 -------------------------------------------------*/
2028 
2029 /**
2030  * @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)
2031  *
2032  * @brief   Avi append sound samples.
2033  *
2034  * @param [in,out]  file    If non-null, the file.
2035  * @param   channel         The channel.
2036  * @param   samples         The samples.
2037  * @param   numsamples      The numsamples.
2038  * @param   sampleskip      The sampleskip.
2039  *
2040  * @return  An avi_error.
2041  */
2042 
append_sound_samples(int channel,const std::int16_t * samples,std::uint32_t numsamples,std::uint32_t sampleskip)2043 avi_file::error avi_file_impl::append_sound_samples(int channel, const std::int16_t *samples, std::uint32_t numsamples, std::uint32_t sampleskip)
2044 {
2045 	std::uint32_t sampoffset = m_soundbuf_chansamples[channel];
2046 	std::uint32_t sampnum;
2047 
2048 	/* see if we have enough room in the buffer */
2049 	if (sampoffset + numsamples > m_soundbuf_samples)
2050 		return error::EXCEEDED_SOUND_BUFFER;
2051 
2052 	/* append samples to the buffer in little-endian format */
2053 	for (sampnum = 0; sampnum < numsamples; sampnum++)
2054 	{
2055 		std::int16_t data = *samples++;
2056 		samples += sampleskip;
2057 		data = little_endianize_int16(data);
2058 		m_soundbuf[sampoffset++ * m_info.audio_channels + channel] = data;
2059 	}
2060 	m_soundbuf_chansamples[channel] = sampoffset;
2061 
2062 	/* flush any full sound chunks to disk */
2063 	return soundbuf_flush(true);
2064 }
2065 
2066 
2067 /*-------------------------------------------------
2068     read_chunk_data - read a chunk's data into
2069     memory
2070 -------------------------------------------------*/
2071 
2072 /**
2073  * @fn  static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, std::uint8_t **buffer)
2074  *
2075  * @brief   Reads chunk data.
2076  *
2077  * @param [in,out]  file    If non-null, the file.
2078  * @param   chunk           The chunk.
2079  * @param [in,out]  buffer  If non-null, the buffer.
2080  *
2081  * @return  The chunk data.
2082  */
2083 
read_chunk_data(avi_chunk const & chunk,std::unique_ptr<std::uint8_t[]> & buffer)2084 avi_file::error avi_file_impl::read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer)
2085 {
2086 	/* allocate memory for the data */
2087 	try { buffer.reset(new std::uint8_t[chunk.size]); }
2088 	catch (...) { return error::NO_MEMORY; }
2089 
2090 	/* read from the file */
2091 	std::uint32_t bytes_read;
2092 	osd_file::error const filerr = m_file->read(&buffer[0], chunk.offset + 8, chunk.size, bytes_read);
2093 	if (filerr != osd_file::error::NONE || bytes_read != chunk.size)
2094 	{
2095 		buffer.reset();
2096 		return error::READ_ERROR;
2097 	}
2098 
2099 	return error::NONE;
2100 }
2101 
2102 
2103 /*-------------------------------------------------
2104     get_first_chunk - get information about the
2105     first chunk in a container
2106 -------------------------------------------------*/
2107 
2108 /**
2109  * @fn  static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk)
2110  *
2111  * @brief   Gets the first chunk.
2112  *
2113  * @param [in,out]  file        If non-null, the file.
2114  * @param   parent              The parent.
2115  * @param [in,out]  newchunk    If non-null, the newchunk.
2116  *
2117  * @return  The first chunk.
2118  */
2119 
get_first_chunk(avi_chunk const * parent,avi_chunk & newchunk)2120 avi_file::error avi_file_impl::get_first_chunk(avi_chunk const *parent, avi_chunk &newchunk)
2121 {
2122 	std::uint64_t startoffset = (parent != nullptr && parent->type != 0) ? parent->offset + 12 : 0;
2123 	if (parent != nullptr && parent->type != CHUNKTYPE_LIST && parent->type != CHUNKTYPE_RIFF && parent->type != 0)
2124 		return error::INVALID_DATA;
2125 	return get_next_chunk_internal(parent, newchunk, startoffset);
2126 }
2127 
2128 
2129 /*-------------------------------------------------
2130     get_next_chunk - get information about the
2131     next chunk in a container
2132 -------------------------------------------------*/
2133 
2134 /**
2135  * @fn  static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk)
2136  *
2137  * @brief   Gets the next chunk.
2138  *
2139  * @param [in,out]  file        If non-null, the file.
2140  * @param   parent              The parent.
2141  * @param [in,out]  newchunk    If non-null, the newchunk.
2142  *
2143  * @return  The next chunk.
2144  */
2145 
get_next_chunk(avi_chunk const * parent,avi_chunk & newchunk)2146 avi_file::error avi_file_impl::get_next_chunk(avi_chunk const *parent, avi_chunk &newchunk)
2147 {
2148 	std::uint64_t nextoffset = newchunk.offset + 8 + newchunk.size + (newchunk.size & 1);
2149 	return get_next_chunk_internal(parent, newchunk, nextoffset);
2150 }
2151 
2152 
2153 /*-------------------------------------------------
2154     find_first_chunk - get information about the
2155     first chunk of a particular type in a container
2156 -------------------------------------------------*/
2157 
2158 /**
2159  * @fn  static avi_error find_first_chunk(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result)
2160  *
2161  * @brief   Searches for the first chunk.
2162  *
2163  * @param [in,out]  file    If non-null, the file.
2164  * @param   findme          The findme.
2165  * @param   container       The container.
2166  * @param [out] result      If non-null, the result.
2167  *
2168  * @return  The found chunk.
2169  */
2170 
find_first_chunk(std::uint32_t findme,const avi_chunk * container,avi_chunk & result)2171 avi_file::error avi_file_impl::find_first_chunk(std::uint32_t findme, const avi_chunk *container, avi_chunk &result)
2172 {
2173 	error avierr;
2174 
2175 	for (avierr = get_first_chunk(container, result); avierr == error::NONE; avierr = get_next_chunk(container, result))
2176 		if (result.type == findme)
2177 			return error::NONE;
2178 
2179 	return avierr;
2180 }
2181 
2182 
2183 /*-------------------------------------------------
2184     find_next_chunk - get information about the
2185     next chunk of a particular type in a container
2186 -------------------------------------------------*/
2187 
2188 /**
2189  * @fn  static avi_error find_next_chunk(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result)
2190  *
2191  * @brief   Searches for the next chunk.
2192  *
2193  * @param [in,out]  file    If non-null, the file.
2194  * @param   findme          The findme.
2195  * @param   container       The container.
2196  * @param [out] result      If non-null, the result.
2197  *
2198  * @return  The found chunk.
2199  */
2200 
find_next_chunk(std::uint32_t findme,const avi_chunk * container,avi_chunk & result)2201 avi_file::error avi_file_impl::find_next_chunk(std::uint32_t findme, const avi_chunk *container, avi_chunk &result)
2202 {
2203 	error avierr;
2204 
2205 	for (avierr = get_next_chunk(container, result); avierr == error::NONE; avierr = get_next_chunk(container, result))
2206 		if (result.type == findme)
2207 			return error::NONE;
2208 
2209 	return avierr;
2210 }
2211 
2212 
2213 /*-------------------------------------------------
2214     find_first_list - get information about the
2215     first list of a particular type in a container
2216 -------------------------------------------------*/
2217 
2218 /**
2219  * @fn  static avi_error find_first_list(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result)
2220  *
2221  * @brief   Searches for the first list.
2222  *
2223  * @param [in,out]  file    If non-null, the file.
2224  * @param   findme          The findme.
2225  * @param   container       The container.
2226  * @param [out] result      If non-null, the result.
2227  *
2228  * @return  The found list.
2229  */
2230 
find_first_list(std::uint32_t findme,const avi_chunk * container,avi_chunk & result)2231 avi_file::error avi_file_impl::find_first_list(std::uint32_t findme, const avi_chunk *container, avi_chunk &result)
2232 {
2233 	error avierr;
2234 
2235 	for (avierr = find_first_chunk(CHUNKTYPE_LIST, container, result); avierr == error::NONE; avierr = find_next_chunk(CHUNKTYPE_LIST, container, result))
2236 		if (result.listtype == findme)
2237 			return error::NONE;
2238 
2239 	return avierr;
2240 }
2241 
2242 
2243 /*-------------------------------------------------
2244     find_next_list - get information about the
2245     next list of a particular type in a container
2246 -------------------------------------------------*/
2247 
2248 /**
2249  * @fn  static avi_error find_next_list(avi_file *file, std::uint32_t findme, const avi_chunk *container, avi_chunk *result)
2250  *
2251  * @brief   Searches for the next list.
2252  *
2253  * @param [in,out]  file    If non-null, the file.
2254  * @param   findme          The findme.
2255  * @param   container       The container.
2256  * @param [out] result      If non-null, the result.
2257  *
2258  * @return  The found list.
2259  */
2260 
find_next_list(std::uint32_t findme,const avi_chunk * container,avi_chunk & result)2261 avi_file::error avi_file_impl::find_next_list(std::uint32_t findme, const avi_chunk *container, avi_chunk &result)
2262 {
2263 	error avierr;
2264 
2265 	for (avierr = find_next_chunk(CHUNKTYPE_LIST, container, result); avierr == error::NONE; avierr = find_next_chunk(CHUNKTYPE_LIST, container, result))
2266 		if (result.listtype == findme)
2267 			return error::NONE;
2268 
2269 	return avierr;
2270 }
2271 
2272 
2273 /*-------------------------------------------------
2274     get_next_chunk_internal - fetch the next
2275     chunk relative to the current one
2276 -------------------------------------------------*/
2277 
2278 /**
2279  * @fn  static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, std::uint64_t offset)
2280  *
2281  * @brief   Gets the next chunk internal.
2282  *
2283  * @param [in,out]  file        If non-null, the file.
2284  * @param   parent              The parent.
2285  * @param [in,out]  newchunk    If non-null, the newchunk.
2286  * @param   offset              The offset.
2287  *
2288  * @return  The next chunk internal.
2289  */
2290 
get_next_chunk_internal(const avi_chunk * parent,avi_chunk & newchunk,std::uint64_t offset)2291 avi_file::error avi_file_impl::get_next_chunk_internal(const avi_chunk *parent, avi_chunk &newchunk, std::uint64_t offset)
2292 {
2293 	osd_file::error filerr;
2294 	std::uint8_t buffer[12];
2295 	std::uint32_t bytesread;
2296 
2297 	/* nullptr parent implies the root */
2298 	if (parent == nullptr)
2299 		parent = &m_rootchunk;
2300 
2301 	/* start at the current offset */
2302 	newchunk.offset = offset;
2303 
2304 	/* if we're past the bounds of the parent, bail */
2305 	if (newchunk.offset + 8 >= parent->offset + 8 + parent->size)
2306 		return error::END;
2307 
2308 	/* read the header */
2309 	filerr = m_file->read(buffer, newchunk.offset, 8, bytesread);
2310 	if (filerr != osd_file::error::NONE || bytesread != 8)
2311 		return error::INVALID_DATA;
2312 
2313 	/* fill in the new chunk */
2314 	newchunk.type = fetch_32bits(&buffer[0]);
2315 	newchunk.size = fetch_32bits(&buffer[4]);
2316 
2317 	/* if we are a list, fetch the list type */
2318 	if (newchunk.type == CHUNKTYPE_LIST || newchunk.type == CHUNKTYPE_RIFF)
2319 	{
2320 		filerr = m_file->read(&buffer[8], newchunk.offset + 8, 4, bytesread);
2321 		if (filerr != osd_file::error::NONE || bytesread != 4)
2322 			return error::INVALID_DATA;
2323 		newchunk.listtype = fetch_32bits(&buffer[8]);
2324 	}
2325 
2326 	return error::NONE;
2327 }
2328 
2329 
2330 /*-------------------------------------------------
2331     read_movie_data - get data about a movie
2332 -------------------------------------------------*/
2333 
2334 /**
2335  * @fn  static avi_error read_movie_data(avi_file *file)
2336  *
2337  * @brief   Reads movie data.
2338  *
2339  * @param [in,out]  file    If non-null, the file.
2340  *
2341  * @return  The movie data.
2342  */
2343 
read_movie_data()2344 avi_file::error avi_file_impl::read_movie_data()
2345 {
2346 	avi_chunk riff, hdrl, avih, strl, strh, strf, indx, movi, idx1;
2347 	error avierr;
2348 
2349 	/* find the RIFF chunk */
2350 	avierr = find_first_chunk(CHUNKTYPE_RIFF, nullptr, riff);
2351 	if (avierr != error::NONE)
2352 		return avierr;
2353 
2354 	/* verify that the RIFF type is AVI */
2355 	if (riff.listtype != LISTTYPE_AVI)
2356 	{
2357 		avierr = error::INVALID_DATA;
2358 		return avierr;
2359 	}
2360 
2361 	/* find the hdrl LIST chunk within the RIFF */
2362 	avierr = find_first_list(LISTTYPE_HDRL, &riff, hdrl);
2363 	if (avierr != error::NONE)
2364 		return avierr;
2365 
2366 	/* find the avih chunk */
2367 	avierr = find_first_chunk(CHUNKTYPE_AVIH, &hdrl, avih);
2368 	if (avierr != error::NONE)
2369 		return avierr;
2370 
2371 	/* parse the avih chunk */
2372 	avierr = parse_avih_chunk(avih);
2373 	if (avierr != error::NONE)
2374 		return avierr;
2375 
2376 	/* loop over strl LIST chunks */
2377 	int strindex = 0;
2378 	for (avierr = find_first_list(LISTTYPE_STRL, &hdrl, strl); avierr == error::NONE; avierr = find_next_list(LISTTYPE_STRL, &hdrl, strl))
2379 	{
2380 		/* if we have too many, it's a bad file */
2381 		if (strindex >= m_streams.size())
2382 			return avierr;
2383 
2384 		/* find the strh chunk */
2385 		avierr = find_first_chunk(CHUNKTYPE_STRH, &strl, strh);
2386 		if (avierr != error::NONE)
2387 			return avierr;
2388 
2389 		/* parse the data */
2390 		avierr = parse_strh_chunk(m_streams[strindex], strh);
2391 		if (avierr != error::NONE)
2392 			return avierr;
2393 
2394 		/* find the strf chunk */
2395 		avierr = find_first_chunk(CHUNKTYPE_STRF, &strl, strf);
2396 		if (avierr != error::NONE)
2397 			return avierr;
2398 
2399 		/* parse the data */
2400 		avierr = parse_strf_chunk(m_streams[strindex], strf);
2401 		if (avierr != error::NONE)
2402 			return avierr;
2403 
2404 		/* find the indx chunk, if present */
2405 		avierr = find_first_chunk(CHUNKTYPE_INDX, &strl, indx);
2406 		if (avierr == error::NONE)
2407 			avierr = parse_indx_chunk(m_streams[strindex], indx);
2408 
2409 		/* next stream */
2410 		strindex++;
2411 	}
2412 
2413 	/* normalize the error after parsing the stream headers */
2414 	if (avierr == error::END)
2415 		avierr = error::NONE;
2416 	if (avierr != error::NONE)
2417 		return avierr;
2418 
2419 	/* find the base of the movi data */
2420 	avierr = find_first_list(LISTTYPE_MOVI, &riff, movi);
2421 	if (avierr != error::NONE)
2422 		return avierr;
2423 
2424 	/* find and parse the idx1 chunk within the RIFF (if present) */
2425 	avierr = find_first_chunk(CHUNKTYPE_IDX1, &riff, idx1);
2426 	if (avierr == error::NONE)
2427 		avierr = parse_idx1_chunk(movi.offset + 8, idx1);
2428 	if (avierr != error::NONE)
2429 		return avierr;
2430 
2431 	/* now extract the movie info */
2432 	avierr = extract_movie_info();
2433 	return avierr;
2434 }
2435 
2436 
2437 /*-------------------------------------------------
2438     extract_movie_info - extract the movie info
2439     from the streams we've read
2440 -------------------------------------------------*/
2441 
2442 /**
2443  * @fn  static avi_error extract_movie_info(avi_file *file)
2444  *
2445  * @brief   Extracts the movie information described by file.
2446  *
2447  * @param [in,out]  file    If non-null, the file.
2448  *
2449  * @return  The extracted movie information.
2450  */
2451 
extract_movie_info()2452 avi_file::error avi_file_impl::extract_movie_info()
2453 {
2454 	avi_stream *stream;
2455 	int offset;
2456 
2457 	/* get the video stream */
2458 	stream = get_video_stream();
2459 	if (stream != nullptr)
2460 	{
2461 		/* fill in the info */
2462 		m_info.video_format = stream->format();
2463 		m_info.video_timescale = stream->rate();
2464 		m_info.video_sampletime = stream->scale();
2465 		m_info.video_numsamples = stream->samples();
2466 		m_info.video_width = stream->width();
2467 		m_info.video_height = stream->height();
2468 	}
2469 
2470 	/* get the first audio stream */
2471 	stream = get_audio_stream(0, offset);
2472 	if (stream != nullptr)
2473 	{
2474 		/* fill in the info */
2475 		m_info.audio_format = stream->format();
2476 		m_info.audio_timescale = stream->rate();
2477 		m_info.audio_sampletime = stream->scale();
2478 		m_info.audio_numsamples = stream->samples();
2479 		m_info.audio_channels = 1;
2480 		m_info.audio_samplebits = stream->samplebits();
2481 		m_info.audio_samplerate = stream->samplerate();
2482 	}
2483 
2484 	/* now make sure all other audio streams are valid */
2485 	while (nullptr != (stream = get_audio_stream(m_info.audio_channels, offset)))
2486 	{
2487 		/* get the stream info */
2488 		m_info.audio_channels++;
2489 
2490 		/* verify compatibility */
2491 		if (m_info.audio_format != stream->format() ||
2492 			m_info.audio_timescale != stream->rate() ||
2493 			m_info.audio_sampletime != stream->scale() ||
2494 			m_info.audio_numsamples != stream->samples() ||
2495 			m_info.audio_samplebits != stream->samplebits() ||
2496 			m_info.audio_samplerate != stream->samplerate())
2497 			return error::INCOMPATIBLE_AUDIO_STREAMS;
2498 	}
2499 
2500 	return error::NONE;
2501 }
2502 
2503 
2504 /*-------------------------------------------------
2505     parse_avih_chunk - parse an avih header
2506     chunk
2507 -------------------------------------------------*/
2508 
2509 /**
2510  * @fn  static avi_error parse_avih_chunk(avi_file *file, avi_chunk *avih)
2511  *
2512  * @brief   Parse avih chunk.
2513  *
2514  * @param [in,out]  file    If non-null, the file.
2515  * @param [in,out]  avih    If non-null, the avih.
2516  *
2517  * @return  An avi_error.
2518  */
2519 
parse_avih_chunk(avi_chunk const & avih)2520 avi_file::error avi_file_impl::parse_avih_chunk(avi_chunk const &avih)
2521 {
2522 	/* read the data */
2523 	std::unique_ptr<std::uint8_t []> chunkdata;
2524 	error avierr = read_chunk_data(avih, chunkdata);
2525 	if (avierr != error::NONE)
2526 		return avierr;
2527 
2528 	/* extract the data */
2529 	std::uint32_t const streams = fetch_32bits(&chunkdata[24]);
2530 
2531 	/* allocate memory for the streams */
2532 	m_streams.clear();
2533 	m_streams.resize(streams);
2534 
2535 	return avierr;
2536 }
2537 
2538 
2539 /*-------------------------------------------------
2540     parse_strh_chunk - parse a strh header
2541     chunk
2542 -------------------------------------------------*/
2543 
2544 /**
2545  * @fn  static avi_error parse_strh_chunk(avi_file *file, avi_stream *stream, avi_chunk *strh)
2546  *
2547  * @brief   Parse strh chunk.
2548  *
2549  * @param [in,out]  file    If non-null, the file.
2550  * @param [in,out]  stream  If non-null, the stream.
2551  * @param [in,out]  strh    If non-null, the strh.
2552  *
2553  * @return  An avi_error.
2554  */
2555 
parse_strh_chunk(avi_stream & stream,avi_chunk const & strh)2556 avi_file::error avi_file_impl::parse_strh_chunk(avi_stream &stream, avi_chunk const &strh)
2557 {
2558 	/* read the data */
2559 	std::unique_ptr<std::uint8_t []> chunkdata;
2560 	error const avierr = read_chunk_data(strh, chunkdata);
2561 	if (avierr != error::NONE)
2562 		return avierr;
2563 
2564 	/* extract the data */
2565 	return stream.set_strh_data(&chunkdata[0], strh.size);
2566 }
2567 
2568 
2569 /*-------------------------------------------------
2570     parse_strf_chunk - parse a strf header
2571     chunk
2572 -------------------------------------------------*/
2573 
2574 /**
2575  * @fn  static avi_error parse_strf_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf)
2576  *
2577  * @brief   Parse strf chunk.
2578  *
2579  * @param [in,out]  file    If non-null, the file.
2580  * @param [in,out]  stream  If non-null, the stream.
2581  * @param [in,out]  strf    If non-null, the strf.
2582  *
2583  * @return  An avi_error.
2584  */
2585 
parse_strf_chunk(avi_stream & stream,avi_chunk const & strf)2586 avi_file::error avi_file_impl::parse_strf_chunk(avi_stream &stream, avi_chunk const &strf)
2587 {
2588 	error avierr;
2589 
2590 	/* read the data */
2591 	std::unique_ptr<std::uint8_t []> chunkdata;
2592 	avierr = read_chunk_data(strf, chunkdata);
2593 	if (avierr != error::NONE)
2594 		return avierr;
2595 
2596 	return stream.set_strf_data(&chunkdata[0], strf.size);
2597 }
2598 
2599 
2600 /*-------------------------------------------------
2601     parse_indx_chunk - parse an indx chunk
2602 -------------------------------------------------*/
2603 
2604 /**
2605  * @fn  static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf)
2606  *
2607  * @brief   Parse indx chunk.
2608  *
2609  * @param [in,out]  file    If non-null, the file.
2610  * @param [in,out]  stream  If non-null, the stream.
2611  * @param [in,out]  strf    If non-null, the strf.
2612  *
2613  * @return  An avi_error.
2614  */
2615 
parse_indx_chunk(avi_stream & stream,avi_chunk const & strf)2616 avi_file::error avi_file_impl::parse_indx_chunk(avi_stream &stream, avi_chunk const &strf)
2617 {
2618 	error avierr;
2619 
2620 	/* read the data */
2621 	std::unique_ptr<std::uint8_t []> chunkdata;
2622 	avierr = read_chunk_data(strf, chunkdata);
2623 	if (avierr != error::NONE)
2624 		return avierr;
2625 
2626 	/* extract the data */
2627 	std::uint16_t const longs_per_entry = fetch_16bits(&chunkdata[0]);
2628 	//subtype = chunkdata[2];
2629 	std::uint8_t const type = chunkdata[3];
2630 	std::uint32_t const entries = fetch_32bits(&chunkdata[4]);
2631 	//id = fetch_32bits(&chunkdata[8]);
2632 	std::uint64_t const baseoffset = fetch_64bits(&chunkdata[12]);
2633 
2634 	/* if this is a superindex, loop over entries and call ourselves recursively */
2635 	if (type == AVI_INDEX_OF_INDEXES)
2636 	{
2637 		/* validate the size of each entry */
2638 		if (longs_per_entry != 4)
2639 			return error::INVALID_DATA;
2640 
2641 		/* loop over entries and create subchunks for each */
2642 		for (std::uint32_t entry = 0; entry < entries; entry++)
2643 		{
2644 			const std::uint8_t *base = &chunkdata[24 + entry * 16];
2645 			osd_file::error filerr;
2646 			avi_chunk subchunk;
2647 			std::uint32_t bytes_read;
2648 			std::uint8_t buffer[8];
2649 
2650 			/* go read the subchunk */
2651 			subchunk.offset = fetch_64bits(&base[0]);
2652 			filerr = m_file->read(buffer, subchunk.offset, sizeof(buffer), bytes_read);
2653 			if (filerr != osd_file::error::NONE || bytes_read != sizeof(buffer))
2654 			{
2655 				avierr = error::READ_ERROR;
2656 				break;
2657 			}
2658 
2659 			/* parse the data */
2660 			subchunk.type = fetch_32bits(&buffer[0]);
2661 			subchunk.size = fetch_32bits(&buffer[4]);
2662 
2663 			/* recursively parse each referenced chunk; stop if we hit an error */
2664 			avierr = parse_indx_chunk(stream, subchunk);
2665 			if (avierr != error::NONE)
2666 				break;
2667 		}
2668 	}
2669 
2670 	/* otherwise, this is a standard index */
2671 	else if (type == AVI_INDEX_OF_CHUNKS)
2672 	{
2673 		/* validate the size of each entry */
2674 		if (longs_per_entry != 2 && longs_per_entry != 3)
2675 			return error::INVALID_DATA;
2676 
2677 		/* loop over entries and parse out the data */
2678 		for (std::uint32_t entry = 0; entry < entries; entry++)
2679 		{
2680 			const std::uint8_t *base = &chunkdata[24 + entry * 4 * longs_per_entry];
2681 			std::uint32_t offset = fetch_32bits(&base[0]);
2682 			std::uint32_t size = fetch_32bits(&base[4]) & 0x7fffffff;  // bit 31 == NOT a keyframe
2683 
2684 			/* set the info for this chunk and advance */
2685 			avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset - 8, size + 8);
2686 			if (avierr != error::NONE)
2687 				break;
2688 		}
2689 	}
2690 
2691 	return avierr;
2692 }
2693 
2694 
2695 /*-------------------------------------------------
2696     parse_idx1_chunk - parse an idx1 chunk
2697 -------------------------------------------------*/
2698 
2699 /**
2700  * @fn  static avi_error parse_idx1_chunk(avi_file *file, std::uint64_t baseoffset, avi_chunk *idx1)
2701  *
2702  * @brief   Parse index 1 chunk.
2703  *
2704  * @param [in,out]  file    If non-null, the file.
2705  * @param   baseoffset      The baseoffset.
2706  * @param [in,out]  idx1    If non-null, the first index.
2707  *
2708  * @return  An avi_error.
2709  */
2710 
parse_idx1_chunk(std::uint64_t baseoffset,avi_chunk const & idx1)2711 avi_file::error avi_file_impl::parse_idx1_chunk(std::uint64_t baseoffset, avi_chunk const &idx1)
2712 {
2713 	error avierr;
2714 
2715 	/* read the data */
2716 	std::unique_ptr<std::uint8_t []> chunkdata;
2717 	avierr = read_chunk_data(idx1, chunkdata);
2718 	if (avierr != error::NONE)
2719 		return avierr;
2720 
2721 	/* loop over entries */
2722 	std::uint32_t const entries = idx1.size / 16;
2723 	for (std::uint32_t entry = 0; entry < entries; entry++)
2724 	{
2725 		std::uint8_t const *const base = &chunkdata[entry * 16];
2726 		std::uint32_t const chunkid = fetch_32bits(&base[0]);
2727 		std::uint32_t const offset = fetch_32bits(&base[8]);
2728 		std::uint32_t const size = fetch_32bits(&base[12]);
2729 		int streamnum;
2730 
2731 		/* determine the stream index */
2732 		streamnum = ((chunkid >> 8) & 0xff) - '0';
2733 		streamnum += 10 * ((chunkid & 0xff) - '0');
2734 		if (streamnum >= m_streams.size())
2735 			return error::INVALID_DATA;
2736 		avi_stream &stream = m_streams[streamnum];
2737 
2738 		/* set the appropriate entry */
2739 		avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset, size + 8);
2740 		if (avierr != error::NONE)
2741 			return avierr;
2742 	}
2743 
2744 	return avierr;
2745 }
2746 
2747 
2748 /*-------------------------------------------------
2749     chunk_open - open a new chunk for writing
2750 -------------------------------------------------*/
2751 
2752 /**
2753  * @fn  static avi_error chunk_open(avi_file *file, std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength)
2754  *
2755  * @brief   Queries if a given chunk open.
2756  *
2757  * @param [in,out]  file    If non-null, the file.
2758  * @param   type            The type.
2759  * @param   listtype        The listtype.
2760  * @param   estlength       The estlength.
2761  *
2762  * @return  An avi_error.
2763  */
2764 
chunk_open(std::uint32_t type,std::uint32_t listtype,std::uint32_t estlength)2765 avi_file::error avi_file_impl::chunk_open(std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength)
2766 {
2767 	/* if we're out of stack entries, bail */
2768 	if (m_chunksp >= m_chunkstack.size())
2769 		return error::STACK_TOO_DEEP;
2770 	avi_chunk &chunk = m_chunkstack[m_chunksp++];
2771 
2772 	/* set up the chunk information */
2773 	chunk.offset = m_writeoffs;
2774 	chunk.size = estlength;
2775 	chunk.type = type;
2776 	chunk.listtype = listtype;
2777 
2778 	/* non-list types */
2779 	if (type != CHUNKTYPE_RIFF && type != CHUNKTYPE_LIST)
2780 	{
2781 		std::uint8_t buffer[8];
2782 
2783 		/* populate the header */
2784 		put_32bits(&buffer[0], chunk.type);
2785 		put_32bits(&buffer[4], chunk.size);
2786 
2787 		/* write the header */
2788 		std::uint32_t written;
2789 		osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written);
2790 		if (filerr != osd_file::error::NONE || written != sizeof(buffer))
2791 			return error::WRITE_ERROR;
2792 		m_writeoffs += written;
2793 	}
2794 
2795 	/* list types */
2796 	else
2797 	{
2798 		std::uint8_t buffer[12];
2799 
2800 		/* populate the header */
2801 		put_32bits(&buffer[0], chunk.type);
2802 		put_32bits(&buffer[4], chunk.size);
2803 		put_32bits(&buffer[8], chunk.listtype);
2804 
2805 		/* write the header */
2806 		std::uint32_t written;
2807 		osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written);
2808 		if (filerr != osd_file::error::NONE || written != sizeof(buffer))
2809 			return error::WRITE_ERROR;
2810 		m_writeoffs += written;
2811 	}
2812 
2813 	return error::NONE;
2814 }
2815 
2816 
2817 /*-------------------------------------------------
2818     chunk_close - finish writing an chunk
2819 -------------------------------------------------*/
2820 
2821 /**
2822  * @fn  static avi_error chunk_close(avi_file *file)
2823  *
2824  * @brief   Chunk close.
2825  *
2826  * @param [in,out]  file    If non-null, the file.
2827  *
2828  * @return  An avi_error.
2829  */
2830 
chunk_close()2831 avi_file::error avi_file_impl::chunk_close()
2832 {
2833 	avi_chunk const &chunk = m_chunkstack[--m_chunksp];
2834 	std::uint64_t const chunksize = m_writeoffs - (chunk.offset + 8);
2835 
2836 	/* error if we don't fit into 32 bits */
2837 	if (chunksize != std::uint32_t(chunksize))
2838 		return error::INVALID_DATA;
2839 
2840 	/* write the final size if it is different from the guess */
2841 	if (chunk.size != chunksize)
2842 	{
2843 		std::uint8_t buffer[4];
2844 
2845 		put_32bits(&buffer[0], std::uint32_t(chunksize));
2846 		std::uint32_t written;
2847 		osd_file::error const filerr = m_file->write(buffer, chunk.offset + 4, sizeof(buffer), written);
2848 		if (filerr != osd_file::error::NONE || written != sizeof(buffer))
2849 			return error::WRITE_ERROR;
2850 	}
2851 
2852 	/* round up to the next word */
2853 	m_writeoffs += chunksize & 1;
2854 
2855 	return error::NONE;
2856 }
2857 
2858 
2859 /*-------------------------------------------------
2860     chunk_write - write an chunk and its data
2861 -------------------------------------------------*/
2862 
2863 /**
2864  * @fn  static avi_error chunk_write(avi_file *file, std::uint32_t type, const void *data, std::uint32_t length)
2865  *
2866  * @brief   Chunk write.
2867  *
2868  * @param [in,out]  file    If non-null, the file.
2869  * @param   type            The type.
2870  * @param   data            The data.
2871  * @param   length          The length.
2872  *
2873  * @return  An avi_error.
2874  */
2875 
chunk_write(std::uint32_t type,const void * data,std::uint32_t length)2876 avi_file::error avi_file_impl::chunk_write(std::uint32_t type, const void *data, std::uint32_t length)
2877 {
2878 	error avierr;
2879 
2880 	/* if we are the first RIFF, we must reserve enough space for the IDX chunk */
2881 	std::uint32_t const idxreserve = (m_riffbase == 0 && type != CHUNKTYPE_IDX1) ? compute_idx1_size() : 0;
2882 
2883 	/* if we are getting too big, split the RIFF */
2884 	/* note that we ignore writes before the current RIFF base, as those are assumed to be
2885 	   overwrites of a chunk from the previous RIFF */
2886 	if ((m_writeoffs >= m_riffbase) && ((m_writeoffs + length + idxreserve - m_riffbase) >= MAX_RIFF_SIZE))
2887 	{
2888 		/* close the movi list */
2889 		avierr = chunk_close();
2890 		if (avierr != error::NONE)
2891 			return avierr;
2892 
2893 		/* write the idx1 chunk if this is the first */
2894 		if (m_riffbase == 0)
2895 		{
2896 			avierr = write_idx1_chunk();
2897 			if (avierr != error::NONE)
2898 				return avierr;
2899 		}
2900 
2901 		/* close the RIFF */
2902 		avierr = chunk_close();
2903 		if (avierr != error::NONE)
2904 			return avierr;
2905 
2906 		/* open a new RIFF */
2907 		m_riffbase = m_writeoffs;
2908 		avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVIX, 0);
2909 		if (avierr != error::NONE)
2910 			return avierr;
2911 
2912 		/* open a nested movi list */
2913 		m_saved_movi_offset = m_writeoffs;
2914 		avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0);
2915 		if (avierr != error::NONE)
2916 			return avierr;
2917 	}
2918 
2919 	/* open the chunk */
2920 	avierr = chunk_open(type, 0, length);
2921 	if (avierr != error::NONE)
2922 		return avierr;
2923 
2924 	/* write the data */
2925 	std::uint32_t written;
2926 	osd_file::error const filerr = m_file->write(data, m_writeoffs, length, written);
2927 	if (filerr != osd_file::error::NONE || written != length)
2928 		return error::WRITE_ERROR;
2929 	m_writeoffs += written;
2930 
2931 	/* close the chunk */
2932 	return chunk_close();
2933 }
2934 
2935 
2936 /*-------------------------------------------------
2937     chunk_overwrite - write a chunk in two passes;
2938     first pass writes to the end of file and
2939     saves the offset; second pass overwrites the
2940     original
2941 -------------------------------------------------*/
2942 
2943 /**
2944  * @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)
2945  *
2946  * @brief   Chunk overwrite.
2947  *
2948  * @param [in,out]  file    If non-null, the file.
2949  * @param   type            The type.
2950  * @param   data            The data.
2951  * @param   length          The length.
2952  * @param [in,out]  offset  If non-null, the offset.
2953  * @param   initial_write   The initial write.
2954  *
2955  * @return  An avi_error.
2956  */
2957 
chunk_overwrite(std::uint32_t type,const void * data,std::uint32_t length,std::uint64_t & offset,bool initial_write)2958 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)
2959 {
2960 	std::uint64_t savedoffset = 0;
2961 
2962 	/* if this is our initial write, save the offset */
2963 	if (initial_write)
2964 		offset = m_writeoffs;
2965 
2966 	/* otherwise, remember the current write offset and replace it with the original */
2967 	else
2968 	{
2969 		savedoffset = m_writeoffs;
2970 		m_writeoffs = offset;
2971 	}
2972 
2973 	/* write the chunk */
2974 	error const avierr = chunk_write(type, data, length);
2975 
2976 	/* if this isn't the initial write, restore the previous offset */
2977 	if (!initial_write)
2978 		m_writeoffs = savedoffset;
2979 
2980 	return avierr;
2981 }
2982 
2983 
2984 /*-------------------------------------------------
2985     write_initial_headers - write out the initial
2986     set of AVI and stream headers
2987 -------------------------------------------------*/
2988 
2989 /**
2990  * @fn  static avi_error write_initial_headers(avi_file *file)
2991  *
2992  * @brief   Writes an initial headers.
2993  *
2994  * @param [in,out]  file    If non-null, the file.
2995  *
2996  * @return  An avi_error.
2997  */
2998 
write_initial_headers()2999 avi_file::error avi_file_impl::write_initial_headers()
3000 {
3001 	error avierr;
3002 
3003 	/* reset the write pointer */
3004 	m_writeoffs = 0;
3005 
3006 	/* open a RIFF chunk */
3007 	avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVI, 0);
3008 	if (avierr != error::NONE)
3009 		return avierr;
3010 
3011 	/* open a hdlr LIST */
3012 	avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_HDRL, 0);
3013 	if (avierr != error::NONE)
3014 		return avierr;
3015 
3016 	/* write an avih chunk */
3017 	avierr = write_avih_chunk(true);
3018 	if (avierr != error::NONE)
3019 		return avierr;
3020 
3021 	/* for each stream, write a strl LIST */
3022 	for (int strnum = 0; strnum < m_streams.size(); strnum++)
3023 	{
3024 		/* open a strl LIST */
3025 		avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_STRL, 0);
3026 		if (avierr != error::NONE)
3027 			return avierr;
3028 
3029 		/* write the strh chunk */
3030 		avierr = write_strh_chunk(m_streams[strnum], true);
3031 		if (avierr != error::NONE)
3032 			return avierr;
3033 
3034 		/* write the strf chunk */
3035 		avierr = write_strf_chunk(m_streams[strnum]);
3036 		if (avierr != error::NONE)
3037 			return avierr;
3038 
3039 		/* write the indx chunk */
3040 		avierr = write_indx_chunk(m_streams[strnum], true);
3041 		if (avierr != error::NONE)
3042 			return avierr;
3043 
3044 		/* close the strl LIST */
3045 		avierr = chunk_close();
3046 		if (avierr != error::NONE)
3047 			return avierr;
3048 	}
3049 
3050 	/* close the hdlr LIST */
3051 	avierr = chunk_close();
3052 	if (avierr != error::NONE)
3053 		return avierr;
3054 
3055 	/* open a movi LIST */
3056 	m_saved_movi_offset = m_writeoffs;
3057 	avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0);
3058 	if (avierr != error::NONE)
3059 		return avierr;
3060 
3061 	return avierr;
3062 }
3063 
3064 
3065 /*-------------------------------------------------
3066     write_avih_chunk - write the avih header
3067     chunk
3068 -------------------------------------------------*/
3069 
3070 /**
3071  * @fn  static avi_error write_avih_chunk(avi_file *file, int initial_write)
3072  *
3073  * @brief   Writes an avih chunk.
3074  *
3075  * @param [in,out]  file    If non-null, the file.
3076  * @param   initial_write   The initial write.
3077  *
3078  * @return  An avi_error.
3079  */
3080 
write_avih_chunk(bool initial_write)3081 avi_file::error avi_file_impl::write_avih_chunk(bool initial_write)
3082 {
3083 	avi_stream *video = get_video_stream();
3084 	std::uint8_t buffer[56];
3085 
3086 	/* reset the buffer */
3087 	std::memset(buffer, 0, sizeof(buffer));
3088 
3089 	put_32bits(&buffer[0], 1000000 * std::int64_t(video->scale()) / video->rate()); /* dwMicroSecPerFrame */
3090 	put_32bits(&buffer[12], AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* dwFlags */
3091 	put_32bits(&buffer[16], video->samples());          /* dwTotalFrames */
3092 	put_32bits(&buffer[24], m_streams.size());          /* dwStreams */
3093 	put_32bits(&buffer[32], video->width());            /* dwWidth */
3094 	put_32bits(&buffer[36], video->height());           /* dwHeight */
3095 
3096 	/* (over)write the chunk */
3097 	return chunk_overwrite(CHUNKTYPE_AVIH, buffer, sizeof(buffer), m_saved_avih_offset, initial_write);
3098 }
3099 
3100 
3101 /*-------------------------------------------------
3102     write_strh_chunk - write the strh header
3103     chunk
3104 -------------------------------------------------*/
3105 
3106 /**
3107  * @fn  static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initial_write)
3108  *
3109  * @brief   Writes a strh chunk.
3110  *
3111  * @param [in,out]  file    If non-null, the file.
3112  * @param [in,out]  stream  If non-null, the stream.
3113  * @param   initial_write   The initial write.
3114  *
3115  * @return  An avi_error.
3116  */
3117 
write_strh_chunk(avi_stream & stream,bool initial_write)3118 avi_file::error avi_file_impl::write_strh_chunk(avi_stream &stream, bool initial_write)
3119 {
3120 	std::uint8_t buffer[56];
3121 
3122 	/* reset the buffer */
3123 	std::memset(buffer, 0, sizeof(buffer));
3124 
3125 	put_32bits(&buffer[0], stream.type());              /* fccType */
3126 	put_32bits(&buffer[20], stream.scale());            /* dwScale */
3127 	put_32bits(&buffer[24], stream.rate());             /* dwRate */
3128 	put_32bits(&buffer[32], stream.samples());          /* dwLength */
3129 	put_32bits(&buffer[40], 10000);                     /* dwQuality */
3130 
3131 	/* video-stream specific data */
3132 	if (stream.type() == STREAMTYPE_VIDS)
3133 	{
3134 		put_32bits(&buffer[4],                          /* fccHandler */
3135 					(stream.format() == FORMAT_HFYU) ? HANDLER_HFYU : HANDLER_DIB);
3136 		put_32bits(&buffer[36],                         /* dwSuggestedBufferSize */
3137 					stream.width() * stream.height() * 4);
3138 		put_16bits(&buffer[52], stream.width());        /* rcFrame.right */
3139 		put_16bits(&buffer[54], stream.height());       /* rcFrame.bottom */
3140 	}
3141 
3142 	/* audio-stream specific data */
3143 	if (stream.type() == STREAMTYPE_AUDS)
3144 	{
3145 		put_32bits(&buffer[36],                         /* dwSuggestedBufferSize */
3146 					stream.samplerate() * stream.bytes_per_sample());
3147 		put_32bits(&buffer[44],                         /* dwSampleSize */
3148 					stream.bytes_per_sample());
3149 	}
3150 
3151 	/* write the chunk */
3152 	return chunk_overwrite(CHUNKTYPE_STRH, buffer, sizeof(buffer), stream.saved_strh_offset(), initial_write);
3153 }
3154 
3155 
3156 /*-------------------------------------------------
3157     write_strf_chunk - write the strf header
3158     chunk
3159 -------------------------------------------------*/
3160 
3161 /**
3162  * @fn  static avi_error write_strf_chunk(avi_file *file, avi_stream *stream)
3163  *
3164  * @brief   Writes a strf chunk.
3165  *
3166  * @param [in,out]  file    If non-null, the file.
3167  * @param [in,out]  stream  If non-null, the stream.
3168  *
3169  * @return  An avi_error.
3170  */
3171 
write_strf_chunk(avi_stream const & stream)3172 avi_file::error avi_file_impl::write_strf_chunk(avi_stream const &stream)
3173 {
3174 	/* video stream */
3175 	if (stream.type() == STREAMTYPE_VIDS)
3176 	{
3177 		std::uint8_t buffer[40];
3178 
3179 		/* reset the buffer */
3180 		std::memset(buffer, 0, sizeof(buffer));
3181 
3182 		put_32bits(&buffer[0], sizeof(buffer));         /* biSize */
3183 		put_32bits(&buffer[4], stream.width());         /* biWidth */
3184 		put_32bits(&buffer[8], stream.height());        /* biHeight */
3185 		put_16bits(&buffer[12], 1);                     /* biPlanes */
3186 		put_16bits(&buffer[14], stream.depth());        /* biBitCount */
3187 		put_32bits(&buffer[16], stream.format());       /* biCompression */
3188 		put_32bits(&buffer[20],                         /* biSizeImage */
3189 					stream.width() * stream.height() * (stream.depth() + 7) / 8);
3190 
3191 		/* write the chunk */
3192 		return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer));
3193 	}
3194 
3195 	/* audio stream */
3196 	if (stream.type() == STREAMTYPE_AUDS)
3197 	{
3198 		std::uint8_t buffer[16];
3199 
3200 		/* reset the buffer */
3201 		std::memset(buffer, 0, sizeof(buffer));
3202 
3203 		put_16bits(&buffer[0], 1);                      /* wFormatTag */
3204 		put_16bits(&buffer[2], stream.channels());      /* nChannels */
3205 		put_32bits(&buffer[4], stream.samplerate());    /* nSamplesPerSec */
3206 		put_32bits(&buffer[8],                          /* nAvgBytesPerSec */
3207 					stream.samplerate() * stream.bytes_per_sample());
3208 		put_16bits(&buffer[12],                         /* nBlockAlign */
3209 					stream.bytes_per_sample());
3210 		put_16bits(&buffer[14], stream.samplebits());   /* wBitsPerSample */
3211 
3212 		/* write the chunk */
3213 		return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer));
3214 	}
3215 
3216 	return error::INVALID_DATA;
3217 }
3218 
3219 
3220 /*-------------------------------------------------
3221     write_indx_chunk - write the indx header
3222     chunk
3223 -------------------------------------------------*/
3224 
3225 /**
3226  * @fn  static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initial_write)
3227  *
3228  * @brief   Writes an indx chunk.
3229  *
3230  * @param [in,out]  file    If non-null, the file.
3231  * @param [in,out]  stream  If non-null, the stream.
3232  * @param   initial_write   The initial write.
3233  *
3234  * @return  An avi_error.
3235  */
3236 
write_indx_chunk(avi_stream & stream,bool initial_write)3237 avi_file::error avi_file_impl::write_indx_chunk(avi_stream &stream, bool initial_write)
3238 {
3239 	std::uint8_t buffer[24 + 16 * MAX_AVI_SIZE_IN_GB / 4];
3240 	std::uint32_t master_entries = 0;
3241 
3242 	/* reset the buffer */
3243 	std::memset(buffer, 0, sizeof(buffer));
3244 
3245 	/* construct the chunk ID and index chunk ID */
3246 	std::uint32_t const chunkid = get_chunkid_for_stream(&stream);
3247 	std::uint32_t const indexchunkid = AVI_FOURCC('i', 'x', '0' + (&stream - &m_streams[0]) / 10, '0' + (&stream - &m_streams[0]) % 10);
3248 
3249 	/* loop over chunks of 4GB and write out indexes for them first */
3250 	if (!initial_write && m_riffbase != 0)
3251 	{
3252 		for (std::uint64_t currentbase = 0; currentbase < m_writeoffs; currentbase += FOUR_GB)
3253 		{
3254 			std::uint64_t const currentend = currentbase + FOUR_GB;
3255 			std::uint32_t chunks_this_index = 0;
3256 			std::uint32_t bytes_this_index = 0;
3257 
3258 			/* count chunks that are in this region */
3259 			for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++)
3260 				if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend)
3261 					chunks_this_index++;
3262 
3263 			/* if no chunks, skip */
3264 			if (chunks_this_index == 0)
3265 				continue;
3266 
3267 			if (master_entries >= MAX_AVI_SIZE_IN_GB / 4)
3268 				return error::WRITE_ERROR;
3269 
3270 			/* allocate memory */
3271 			std::unique_ptr<std::uint8_t []> tempbuf;
3272 			try { tempbuf.reset(new std::uint8_t[24 + 8 * chunks_this_index]); }
3273 			catch (...) { return error::NO_MEMORY; }
3274 			std::memset(&tempbuf[0], 0, 24 + 8 * chunks_this_index);
3275 
3276 			/* make a regular index */
3277 			put_16bits(&tempbuf[0], 2);                     /* wLongsPerEntry */
3278 			tempbuf[2] = 0;                                 /* bIndexSubType */
3279 			tempbuf[3] = AVI_INDEX_OF_CHUNKS;               /* bIndexType */
3280 			put_32bits(&tempbuf[4], chunks_this_index);     /* nEntriesInUse */
3281 			put_32bits(&tempbuf[8], chunkid);               /* dwChunkId */
3282 			put_64bits(&tempbuf[12], currentbase);          /* qwBaseOffset */
3283 
3284 			/* now fill in the indexes */
3285 			chunks_this_index = 0;
3286 			for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++)
3287 				if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend)
3288 				{
3289 					put_32bits(&tempbuf[24 + 8 * chunks_this_index + 0], stream.chunk(chunknum).offset + 8 - currentbase);
3290 					put_32bits(&tempbuf[24 + 8 * chunks_this_index + 4], stream.chunk(chunknum).length - 8);
3291 					bytes_this_index += stream.chunk(chunknum).length;
3292 					chunks_this_index++;
3293 				}
3294 
3295 			/* write the offset of this index to the master table */
3296 			put_64bits(&buffer[24 + 16 * master_entries + 0], m_writeoffs);
3297 			put_32bits(&buffer[24 + 16 * master_entries + 8], 24 + 8 * chunks_this_index + 8);
3298 			if (stream.type() == STREAMTYPE_VIDS)
3299 				put_32bits(&buffer[24 + 16 * master_entries + 12], chunks_this_index);
3300 			else if (stream.type() == STREAMTYPE_AUDS)
3301 				put_32bits(&buffer[24 + 16 * master_entries + 12], bytes_this_index / stream.bytes_per_sample());
3302 			master_entries++;
3303 
3304 			/* write the index */
3305 			error const avierr = chunk_write(indexchunkid, &tempbuf[0], 24 + 8 * chunks_this_index);
3306 			if (avierr != error::NONE)
3307 				return avierr;
3308 		}
3309 	}
3310 
3311 	/* build up the master index */
3312 	if (master_entries != 0)
3313 	{
3314 		put_16bits(&buffer[0], 4);                      /* wLongsPerEntry */
3315 		buffer[2] = 0;                                  /* bIndexSubType */
3316 		buffer[3] = AVI_INDEX_OF_INDEXES;               /* bIndexType */
3317 		put_32bits(&buffer[4], master_entries);         /* nEntriesInUse */
3318 		put_32bits(&buffer[8], chunkid);                /* dwChunkId */
3319 	}
3320 
3321 	/* (over)write the chunk */
3322 	return chunk_overwrite((master_entries == 0) ? CHUNKTYPE_JUNK : CHUNKTYPE_INDX, buffer, sizeof(buffer), stream.saved_indx_offset(), initial_write);
3323 }
3324 
3325 
3326 /*-------------------------------------------------
3327     write_idx1_chunk - write the idx1 chunk
3328 -------------------------------------------------*/
3329 
3330 /**
3331  * @fn  static avi_error write_idx1_chunk(avi_file *file)
3332  *
3333  * @brief   Writes an index 1 chunk.
3334  *
3335  * @param [in,out]  file    If non-null, the file.
3336  *
3337  * @return  An avi_error.
3338  */
3339 
write_idx1_chunk()3340 avi_file::error avi_file_impl::write_idx1_chunk()
3341 {
3342 	std::uint32_t const tempbuflength = compute_idx1_size() - 8;
3343 	std::uint32_t curchunk[2] = { 0 };
3344 
3345 	/* allocate a temporary buffer */
3346 	std::unique_ptr<std::uint8_t []> tempbuf;
3347 	try { tempbuf.reset(new std::uint8_t[tempbuflength]); }
3348 	catch (...) { return error::NO_MEMORY; }
3349 
3350 	/* fill it in */
3351 	for (std::uint32_t curoffset = 0; curoffset < tempbuflength; curoffset += 16)
3352 	{
3353 		std::uint64_t minoffset = ~std::uint64_t(0);
3354 		int minstr = 0;
3355 
3356 		/* determine which stream has the next chunk */
3357 		for (int strnum = 0; strnum < m_streams.size(); strnum++)
3358 			if (curchunk[strnum] < m_streams[strnum].chunks() && m_streams[strnum].chunk(curchunk[strnum]).offset < minoffset)
3359 			{
3360 				minoffset = m_streams[strnum].chunk(curchunk[strnum]).offset;
3361 				minstr = strnum;
3362 			}
3363 
3364 		/* make an entry for this index */
3365 		put_32bits(&tempbuf[curoffset + 0], get_chunkid_for_stream(&m_streams[minstr]));
3366 		put_32bits(&tempbuf[curoffset + 4], 0x0010 /* AVIIF_KEYFRAME */);
3367 		put_32bits(&tempbuf[curoffset + 8], minoffset - (m_saved_movi_offset + 8));
3368 		put_32bits(&tempbuf[curoffset + 12], m_streams[minstr].chunk(curchunk[minstr]).length - 8);
3369 
3370 		/* advance the chunk counter for this stream */
3371 		curchunk[minstr]++;
3372 	}
3373 
3374 	/* write the chunk */
3375 	return chunk_write(CHUNKTYPE_IDX1, &tempbuf[0], tempbuflength);
3376 }
3377 
3378 
3379 /*-------------------------------------------------
3380     soundbuf_initialize - initialize the sound
3381     buffering system
3382 -------------------------------------------------*/
3383 
3384 /**
3385  * @fn  static avi_error soundbuf_initialize(avi_file *file)
3386  *
3387  * @brief   Soundbuf initialize.
3388  *
3389  * @param [in,out]  file    If non-null, the file.
3390  *
3391  * @return  An avi_error.
3392  */
3393 
soundbuf_initialize()3394 avi_file::error avi_file_impl::soundbuf_initialize()
3395 {
3396 	int offset;
3397 	avi_stream *const audio = get_audio_stream(0, offset);
3398 	avi_stream *const video = get_video_stream();
3399 
3400 	/* we require a video stream */
3401 	if (video == nullptr)
3402 		return error::UNSUPPORTED_VIDEO_FORMAT;
3403 
3404 	/* skip if no audio stream */
3405 	if (audio == nullptr)
3406 		return error::NONE;
3407 
3408 	/* determine the number of samples we want in our buffer; 2 seconds should be enough */
3409 	m_soundbuf_samples = m_info.audio_samplerate * SOUND_BUFFER_MSEC / 1000;
3410 
3411 	/* allocate a buffer */
3412 	m_soundbuf.clear();
3413 	try { m_soundbuf.resize(m_soundbuf_samples * m_info.audio_channels, 0); }
3414 	catch (...) { return error::NO_MEMORY; }
3415 
3416 	/* determine the number of frames to be ahead (0.75secs) */
3417 	m_soundbuf_frames = (std::uint64_t(video->rate()) * 75) / (std::uint64_t(video->scale()) * 100) + 1;
3418 	return error::NONE;
3419 }
3420 
3421 
3422 /*-------------------------------------------------
3423     soundbuf_write_chunk - write the initial
3424     chunk data
3425 -------------------------------------------------*/
3426 
3427 /**
3428  * @fn  static avi_error soundbuf_write_chunk(avi_file *file, std::uint32_t framenum)
3429  *
3430  * @brief   Soundbuf write chunk.
3431  *
3432  * @param [in,out]  file    If non-null, the file.
3433  * @param   framenum        The framenum.
3434  *
3435  * @return  An avi_error.
3436  */
3437 
soundbuf_write_chunk(std::uint32_t framenum)3438 avi_file::error avi_file_impl::soundbuf_write_chunk(std::uint32_t framenum)
3439 {
3440 	int offset;
3441 	avi_stream *const stream = get_audio_stream(0, offset);
3442 	std::uint32_t length;
3443 
3444 	/* skip if no audio stream */
3445 	if (stream == nullptr)
3446 		return error::NONE;
3447 
3448 	/* determine the length of this chunk */
3449 	if (framenum == 0)
3450 		length = framenum_to_samplenum(m_soundbuf_frames);
3451 	else
3452 		length = framenum_to_samplenum(framenum + 1 + m_soundbuf_frames) - framenum_to_samplenum(framenum + m_soundbuf_frames);
3453 	length *= stream->channels() * sizeof(std::int16_t);
3454 
3455 	/* then do the initial write */
3456 	error const avierr = chunk_write(get_chunkid_for_stream(stream), &m_soundbuf[0], length);
3457 	if (avierr != error::NONE)
3458 		return avierr;
3459 
3460 	/* set the info for this new chunk */
3461 	return stream->set_chunk_info(stream->chunks(), m_writeoffs - length - 8, length + 8);
3462 }
3463 
3464 
3465 /*-------------------------------------------------
3466     soundbuf_flush - flush data from the sound
3467     buffers
3468 -------------------------------------------------*/
3469 
3470 /**
3471  * @fn  static avi_error soundbuf_flush(avi_file *file, int only_flush_full)
3472  *
3473  * @brief   Soundbuf flush.
3474  *
3475  * @param [in,out]  file    If non-null, the file.
3476  * @param   only_flush_full The only flush full.
3477  *
3478  * @return  An avi_error.
3479  */
3480 
soundbuf_flush(bool only_flush_full)3481 avi_file::error avi_file_impl::soundbuf_flush(bool only_flush_full)
3482 {
3483 	int offset;
3484 	avi_stream *const stream = get_audio_stream(0, offset);
3485 	std::int32_t channelsamples = m_soundbuf_samples;
3486 	std::int32_t processedsamples = 0;
3487 	std::uint32_t finalchunks;
3488 	error avierr;
3489 	std::uint32_t chunknum;
3490 	std::uint32_t chunkid;
3491 
3492 	/* skip if no stream */
3493 	if (stream == nullptr)
3494 		return error::NONE;
3495 
3496 	/* get the chunk ID for this stream */
3497 	chunkid = get_chunkid_for_stream(stream);
3498 	std::uint32_t const bytes_per_sample = stream->channels() * sizeof(std::int16_t);
3499 	finalchunks = stream->chunks();
3500 
3501 	/* find out how many samples we've accumulated */
3502 	for (int channel = 0; channel < stream->channels(); channel++)
3503 		channelsamples = (std::min<std::int32_t>)(channelsamples, m_soundbuf_chansamples[channel]);
3504 
3505 	/* loop over pending sound chunks */
3506 	for (chunknum = m_soundbuf_chunks; chunknum < stream->chunks(); chunknum++)
3507 	{
3508 		avi_chunk_list &chunk = stream->chunk(chunknum);
3509 		std::uint32_t const chunksamples = (chunk.length - 8) / bytes_per_sample;
3510 
3511 		/* stop if we don't have enough to satisfy this chunk */
3512 		if (only_flush_full && channelsamples < chunksamples)
3513 			break;
3514 
3515 		/* if we don't have all the samples we need, pad with 0's */
3516 		if (channelsamples > 0 && channelsamples < chunksamples)
3517 		{
3518 			if (processedsamples + chunksamples > m_soundbuf_samples)
3519 				return error::EXCEEDED_SOUND_BUFFER;
3520 			std::memset(&m_soundbuf[(processedsamples + channelsamples) * stream->channels()], 0, (chunksamples - channelsamples) * bytes_per_sample);
3521 		}
3522 
3523 		/* if we're completely out of samples, clear the buffer entirely and use the end */
3524 		else if (channelsamples <= 0)
3525 		{
3526 			processedsamples = m_soundbuf_samples - chunksamples;
3527 			std::memset(&m_soundbuf[processedsamples * stream->channels()], 0, chunksamples * bytes_per_sample);
3528 			chunkid = CHUNKTYPE_JUNK;
3529 			finalchunks--;
3530 		}
3531 
3532 		/* copy the sample data in */
3533 		avierr = chunk_overwrite(chunkid, &m_soundbuf[processedsamples * stream->channels()], chunk.length - 8, chunk.offset, false);
3534 		if (avierr != error::NONE)
3535 			return avierr;
3536 
3537 		/* add up the samples */
3538 		if (channelsamples > chunksamples)
3539 			m_info.audio_numsamples = stream->add_samples(chunksamples);
3540 		else if (channelsamples > 0)
3541 			m_info.audio_numsamples = stream->add_samples(channelsamples);
3542 
3543 		/* advance past those */
3544 		processedsamples += chunksamples;
3545 		channelsamples -= chunksamples;
3546 		channelsamples = (std::max)(0, channelsamples);
3547 	}
3548 
3549 	/* if we have a non-zero offset, shift everything down */
3550 	if (processedsamples > 0)
3551 	{
3552 		/* first account for the samples we processed */
3553 		if (m_soundbuf_samples > processedsamples)
3554 			std::memmove(&m_soundbuf[0], &m_soundbuf[processedsamples * stream->channels()], (m_soundbuf_samples - processedsamples) * bytes_per_sample);
3555 		for (int channel = 0; channel < stream->channels(); channel++)
3556 			m_soundbuf_chansamples[channel] -= processedsamples;
3557 	}
3558 
3559 	/* update the final chunk count */
3560 	if (!only_flush_full)
3561 		stream->set_chunks(finalchunks);
3562 
3563 	/* account for flushed chunks */
3564 	m_soundbuf_chunks = chunknum;
3565 	return error::NONE;
3566 }
3567 
3568 
3569 /*-------------------------------------------------
3570     printf_chunk_recursive - print information
3571     about a chunk recursively
3572 -------------------------------------------------*/
3573 
3574 /**
3575  * @fn  static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent)
3576  *
3577  * @brief   Printf chunk recursive.
3578  *
3579  * @param [in,out]  file        If non-null, the file.
3580  * @param [in,out]  container   If non-null, the container.
3581  * @param   indent              The indent.
3582  */
3583 
printf_chunk_recursive(avi_chunk const * container,int indent)3584 void avi_file_impl::printf_chunk_recursive(avi_chunk const *container, int indent)
3585 {
3586 	char size[20], offset[20];
3587 	avi_chunk curchunk;
3588 	error avierr;
3589 
3590 	/* iterate over chunks in this container */
3591 	for (avierr = get_first_chunk(container, curchunk); avierr == error::NONE; avierr = get_next_chunk(container, curchunk))
3592 	{
3593 		std::uint32_t chunksize = curchunk.size;
3594 		bool recurse = false;
3595 
3596 		u64toa(curchunk.size, size);
3597 		u64toa(curchunk.offset, offset);
3598 		printf("%*schunk = %c%c%c%c, size=%s (%s)\n", indent, "",
3599 				std::uint8_t(curchunk.type >> 0),
3600 				std::uint8_t(curchunk.type >> 8),
3601 				std::uint8_t(curchunk.type >> 16),
3602 				std::uint8_t(curchunk.type >> 24),
3603 				size, offset);
3604 
3605 		/* certain chunks are just containers; recurse into them */
3606 		switch (curchunk.type)
3607 		{
3608 			/* basic containers */
3609 			case CHUNKTYPE_RIFF:
3610 			case CHUNKTYPE_LIST:
3611 				printf("%*stype = %c%c%c%c\n", indent, "",
3612 						std::uint8_t(curchunk.listtype >> 0),
3613 						std::uint8_t(curchunk.listtype >> 8),
3614 						std::uint8_t(curchunk.listtype >> 16),
3615 						std::uint8_t(curchunk.listtype >> 24));
3616 				recurse = true;
3617 				chunksize = 0;
3618 				break;
3619 		}
3620 
3621 		/* print data within the chunk */
3622 		if (chunksize > 0 && curchunk.size < 1024 * 1024)
3623 		{
3624 			std::unique_ptr<std::uint8_t []> data;
3625 
3626 			/* read the data for a chunk */
3627 			avierr = read_chunk_data(curchunk, data);
3628 			if (avierr == error::NONE)
3629 			{
3630 				std::uint32_t const bytes = (std::min)(std::uint32_t(512), chunksize);
3631 				for (std::uint32_t i = 0; i < bytes; i++)
3632 				{
3633 					if (i % 16 == 0) printf("%*s   ", indent, "");
3634 					printf("%02X ", data[i]);
3635 					if (i % 16 == 15) printf("\n");
3636 				}
3637 				if (chunksize % 16 != 0) printf("\n");
3638 			}
3639 		}
3640 
3641 		/* if we're recursing, dive down */
3642 		if (recurse)
3643 			printf_chunk_recursive(&curchunk, indent + 4);
3644 	}
3645 
3646 	/* if we didn't get a legitimate error, indicate that */
3647 	if (avierr != error::END)
3648 		printf("[chunk error %d]\n", int(avierr));
3649 }
3650 
3651 } // anonymous namespace
3652 
3653 
3654 
3655 /***************************************************************************
3656     IMPLEMENTATION
3657 ***************************************************************************/
3658 
3659 /*-------------------------------------------------
3660     avi_open - open an AVI movie file for read
3661 -------------------------------------------------*/
3662 
3663 /**
3664  * @fn  avi_error avi_open(const char *filename, avi_file **file)
3665  *
3666  * @brief   Queries if a given avi open.
3667  *
3668  * @param   filename        Filename of the file.
3669  * @param [in,out]  file    If non-null, the file.
3670  *
3671  * @return  An avi_error.
3672  */
3673 
open(std::string const & filename,ptr & file)3674 avi_file::error avi_file::open(std::string const &filename, ptr &file)
3675 {
3676 	/* open the file */
3677 	osd_file::ptr f;
3678 	std::uint64_t length;
3679 	if (osd_file::open(filename, OPEN_FLAG_READ, f, length) != osd_file::error::NONE)
3680 		return error::CANT_OPEN_FILE;
3681 
3682 	/* allocate the file */
3683 	std::unique_ptr<avi_file_impl> newfile;
3684 	try { newfile = std::make_unique<avi_file_impl>(std::move(f), length); }
3685 	catch (...) { return error::NO_MEMORY; }
3686 
3687 	/* parse the data */
3688 	error const avierr = newfile->read_movie_data();
3689 	if (avierr != error::NONE)
3690 		return avierr;
3691 
3692 	file = std::move(newfile);
3693 	return error::NONE;
3694 }
3695 
3696 
3697 /*-------------------------------------------------
3698     avi_create - create a new AVI movie file
3699 -------------------------------------------------*/
3700 
3701 /**
3702  * @fn  avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file)
3703  *
3704  * @brief   Avi create.
3705  *
3706  * @param   filename        Filename of the file.
3707  * @param   info            The information.
3708  * @param [in,out]  file    If non-null, the file.
3709  *
3710  * @return  An avi_error.
3711  */
3712 
create(std::string const & filename,movie_info const & info,ptr & file)3713 avi_file::error avi_file::create(std::string const &filename, movie_info const &info, ptr &file)
3714 {
3715 	/* validate video info */
3716 	if ((info.video_format != 0 && info.video_format != FORMAT_UYVY && info.video_format != FORMAT_VYUY && info.video_format != FORMAT_YUY2) ||
3717 		(info.video_width == 0) ||
3718 		(info.video_height == 0) ||
3719 		(info.video_depth == 0) ||
3720 		(info.video_depth % 8 != 0))
3721 		return error::UNSUPPORTED_VIDEO_FORMAT;
3722 
3723 	/* validate audio info */
3724 	if (info.audio_format != 0 ||
3725 		info.audio_channels > MAX_SOUND_CHANNELS ||
3726 		info.audio_samplebits != 16)
3727 		return error::UNSUPPORTED_AUDIO_FORMAT;
3728 
3729 	/* open the file */
3730 	osd_file::ptr f;
3731 	std::uint64_t length;
3732 	osd_file::error const filerr = osd_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, f, length);
3733 	if (filerr != osd_file::error::NONE)
3734 		return error::CANT_OPEN_FILE;
3735 
3736 	/* allocate the file */
3737 	error avierr;
3738 	std::unique_ptr<avi_file_impl> newfile;
3739 	try { newfile = std::make_unique<avi_file_impl>(std::move(f), info); }
3740 	catch (...)
3741 	{
3742 		avierr = error::NO_MEMORY;
3743 		goto error;
3744 	}
3745 
3746 	/* initialize the sound buffering */
3747 	avierr = newfile->soundbuf_initialize();
3748 	if (avierr != error::NONE)
3749 		goto error;
3750 
3751 	/* write the initial headers */
3752 	avierr = newfile->write_initial_headers();
3753 	if (avierr != error::NONE)
3754 		goto error;
3755 
3756 	file = std::move(newfile);
3757 	return error::NONE;
3758 
3759 error:
3760 	f.reset();
3761 	newfile.reset();
3762 	osd_file::remove(filename);
3763 	return avierr;
3764 }
3765 
3766 
3767 /*-------------------------------------------------
3768     avi_error_string - get the error string for
3769     an avi_error
3770 -------------------------------------------------*/
3771 
3772 /**
3773  * @fn  const char *avi_error_string(avi_error err)
3774  *
3775  * @brief   Avi error string.
3776  *
3777  * @param   err The error.
3778  *
3779  * @return  null if it fails, else a char*.
3780  */
3781 
error_string(error err)3782 const char *avi_file::error_string(error err)
3783 {
3784 	switch (err)
3785 	{
3786 		case error::NONE:                       return "success";
3787 		case error::END:                        return "hit end of file";
3788 		case error::INVALID_DATA:               return "invalid data";
3789 		case error::NO_MEMORY:                  return "out of memory";
3790 		case error::READ_ERROR:                 return "read error";
3791 		case error::WRITE_ERROR:                return "write error";
3792 		case error::STACK_TOO_DEEP:             return "stack overflow";
3793 		case error::UNSUPPORTED_FEATURE:        return "unsupported feature";
3794 		case error::CANT_OPEN_FILE:             return "unable to open file";
3795 		case error::INCOMPATIBLE_AUDIO_STREAMS: return "found incompatible audio streams";
3796 		case error::INVALID_SAMPLERATE:         return "found invalid sample rate";
3797 		case error::INVALID_STREAM:             return "invalid stream";
3798 		case error::INVALID_FRAME:              return "invalid frame index";
3799 		case error::INVALID_BITMAP:             return "invalid bitmap";
3800 		case error::UNSUPPORTED_VIDEO_FORMAT:   return "unsupported video format";
3801 		case error::UNSUPPORTED_AUDIO_FORMAT:   return "unsupported audio format";
3802 		case error::EXCEEDED_SOUND_BUFFER:      return "sound buffer overflow";
3803 		default:                                return "undocumented error";
3804 	}
3805 }
3806 
3807 
avi_file()3808 avi_file::avi_file()
3809 {
3810 }
3811 
3812 
~avi_file()3813 avi_file::~avi_file()
3814 {
3815 }
3816