1// SPDX-License-Identifier: GPL-2.0
2//
3// container-voc.c - a parser/builder for a container of Creative Voice File.
4//
5// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
6//
7// Licensed under the terms of the GNU General Public License, version 2.
8
9#include "container.h"
10#include "misc.h"
11
12// Not portable to all of UNIX platforms.
13#include <endian.h>
14
15// References:
16//  - http://sox.sourceforge.net/
17
18#define VOC_MAGIC		"Creative Voice File\x1a"
19#define VOC_VERSION_1_10	0x010a
20#define VOC_VERSION_1_20	0x0114
21
22enum block_type {
23	BLOCK_TYPE_TERMINATOR		= 0x00,
24	BLOCK_TYPE_V110_DATA		= 0x01,
25	BLOCK_TYPE_CONTINUOUS_DATA	= 0x02,
26	BLOCK_TYPE_SILENCE		= 0x03,
27	BLOCK_TYPE_MARKER		= 0x04,
28	BLOCK_TYPE_STRING		= 0x05,
29	BLOCK_TYPE_REPEAT_START		= 0x06,
30	BLOCK_TYPE_REPEAT_END		= 0x07,
31	BLOCK_TYPE_EXTENDED_V110_FORMAT	= 0x08,
32	BLOCK_TYPE_V120_DATA		= 0x09,
33};
34
35enum code_id {
36	// Version 1.10.
37	CODE_ID_GENERIC_MBLA_U8			= 0x00,
38	CODE_ID_CREATIVE_ADPCM_8BIT_TO_4BIT_LE	= 0x01,
39	CODE_ID_CREATIVE_ADPCM_8BIT_TO_3BIT_LE	= 0x02,
40	CODE_ID_CREATIVE_ADPCM_8BIT_TO_2BIT_LE	= 0x03,
41	// Version 1.20.
42	CODE_ID_GENERIC_MBLA_S16_LE		= 0x04,
43	CODE_ID_CCIT_A_LAW_LE			= 0x06,
44	CODE_ID_CCIT_MU_LAW_LE			= 0x07,
45	CODE_ID_CREATIVE_ADPCM_16BIT_TO_4BIT_LE	= 0x2000,
46};
47
48struct format_map {
49	unsigned int minimal_version;
50	enum code_id code_id;
51	snd_pcm_format_t format;
52};
53
54static const struct format_map format_maps[] = {
55	{VOC_VERSION_1_10, CODE_ID_GENERIC_MBLA_U8,	SND_PCM_FORMAT_U8},
56	{VOC_VERSION_1_20, CODE_ID_GENERIC_MBLA_S16_LE,	SND_PCM_FORMAT_S16_LE},
57	{VOC_VERSION_1_20, CODE_ID_CCIT_A_LAW_LE,	SND_PCM_FORMAT_A_LAW},
58	{VOC_VERSION_1_20, CODE_ID_CCIT_MU_LAW_LE,	SND_PCM_FORMAT_MU_LAW},
59	// The other formats are not supported by ALSA.
60};
61
62struct container_header {
63	uint8_t magic[20];
64	uint16_t hdr_size;
65	uint16_t version;
66	uint16_t version_compr;
67};
68
69// A format for data blocks except for terminator type.
70struct block_header {
71	uint8_t type;
72	uint8_t size[3];
73
74	uint8_t data[0];
75};
76
77// Data block for terminator type has an exceptional format.
78struct block_terminator {
79	uint8_t type;
80};
81
82struct time_const {
83	unsigned int frames_per_second;
84	uint16_t code;
85};
86
87static const struct time_const v110_time_consts[] = {
88	{5512, 74},
89	{8000, 130},
90	{11025, 165},
91	{16000, 193},
92	{22050, 210},
93	{32000, 224},
94	{44100, 233},
95	{48000, 235},
96	{64000, 240},
97	// Time constant for the upper sampling rate is not identical.
98};
99
100static const struct time_const ex_v110_time_consts[] = {
101	{5512, 19092},
102	{8000, 33536},
103	{11025, 42317},
104	{16000, 49536},
105	{22050, 53927},
106	{32000, 57536},
107	{44100, 59732},
108	{48000, 60203},
109	{64000, 61536},
110	{88200, 62634},
111	{96000, 62870},
112	{176400, 64085},
113	{192000, 64203},
114	// This support up to 192.0 kHz. The rest is for cases with 2ch.
115	{352800, 64811},
116	{384000, 64870},
117};
118
119// v1.10 format:
120// - monaural.
121// - frames_per_second = 1,000,000 / (256 - time_const)
122struct block_v110_data {
123	uint8_t type;
124	uint8_t size[3];	// Equals to (2 + the size of frames).
125
126	uint8_t time_const;
127	uint8_t code_id;
128	uint8_t frames[0];	// Aligned to little-endian.
129};
130
131struct block_continuous_data {
132	uint8_t type;
133	uint8_t size[3];	// Equals to the size of frames.
134
135	uint8_t frames[0];	// Aligned to little-endian.
136};
137
138// v1.10 format:
139// - monaural.
140// - frames_per_second = 1,000,000 / (256 - time_const).
141struct block_silence {
142	uint8_t type;
143	uint8_t size[3];	// Equals to 3.
144
145	uint16_t frame_count;
146	uint8_t time_const;
147};
148
149struct block_marker {
150	uint8_t type;
151	uint8_t size[3];	// Equals to 2.
152
153	uint16_t mark;
154};
155
156struct block_string {
157	uint8_t type;
158	uint8_t size[3];	// Equals to the length of string with 0x00.
159
160	uint8_t chars[0];
161};
162
163struct block_repeat_start {
164	uint8_t type;
165	uint8_t size[3];	// Equals to 2.
166
167	uint16_t count;
168};
169
170struct block_repeat_end {
171	uint8_t type;
172	uint8_t size[3];	// Equals to 0.
173};
174
175// Extended v1.10 format:
176// - manaural/stereo.
177// - frames_per_second =
178//		256,000,000 / (samples_per_frame * (65536 - time_const)).
179// - Appear just before v110_data block.
180struct block_extended_v110_format {
181	uint8_t type;
182	uint8_t size[3];	// Equals to 4.
183
184	uint16_t time_const;
185	uint8_t code_id;
186	uint8_t ch_mode;	// 0 is monaural, 1 is stereo.
187};
188
189// v1.20 format:
190// - monaural/stereo.
191// - 8/16 bits_per_sample.
192// - time_const is not used.
193// - code_id is extended.
194struct block_v120_format {
195	uint8_t type;
196	uint8_t size[3];	// Equals to (12 + ).
197
198	uint32_t frames_per_second;
199	uint8_t bits_per_sample;
200	uint8_t samples_per_frame;
201	uint16_t code_id;
202	uint8_t reserved[4];
203
204	uint8_t frames[0];	// Aligned to little-endian.
205};
206
207// Aligned to little endian order but 24 bits field.
208static uint32_t parse_block_data_size(uint8_t fields[3])
209{
210	return (fields[2] << 16) | (fields[1] << 8) | fields[0];
211}
212
213static void build_block_data_size(uint8_t fields[3], unsigned int size)
214{
215	fields[0] = (size & 0x0000ff);
216	fields[1] = (size & 0x00ff00) >> 8;
217	fields[2] = (size & 0xff0000) >> 16;
218}
219
220static int build_time_constant(unsigned int frames_per_second,
221			       unsigned int samples_per_frame, uint16_t *code,
222			       bool extended)
223{
224	unsigned int i;
225
226	// 16 bits are available for this purpose.
227	if (extended) {
228		if (samples_per_frame > 2)
229			return -EINVAL;
230		frames_per_second *= samples_per_frame;
231
232		for (i = 0; i < ARRAY_SIZE(ex_v110_time_consts); ++i) {
233			if (ex_v110_time_consts[i].frames_per_second ==
234					frames_per_second)
235				break;
236		}
237		if (i < ARRAY_SIZE(ex_v110_time_consts) &&
238		    frames_per_second <= 192000) {
239			*code = ex_v110_time_consts[i].code;
240		} else {
241			*code = 65536 - 256000000 / frames_per_second;
242		}
243	} else {
244		if (samples_per_frame != 1)
245			return -EINVAL;
246
247		for (i = 0; i < ARRAY_SIZE(v110_time_consts); ++i) {
248			if (v110_time_consts[i].frames_per_second ==
249					frames_per_second)
250			break;
251		}
252		// Should be within 8 bit.
253		if (i < ARRAY_SIZE(v110_time_consts))
254			*code = (uint8_t)v110_time_consts[i].code;
255		else
256			*code = 256 - 1000000 / frames_per_second;
257	}
258
259	return 0;
260}
261
262static unsigned int parse_time_constant(uint16_t code,
263					unsigned int samples_per_frame,
264					unsigned int *frames_per_second,
265					bool extended)
266{
267	unsigned int i;
268
269	if (extended) {
270		if (samples_per_frame > 2)
271			return -EINVAL;
272
273		for (i = 0; i < ARRAY_SIZE(ex_v110_time_consts); ++i) {
274			if (ex_v110_time_consts[i].code == code ||
275			    ex_v110_time_consts[i].code - 1 == code)
276				break;
277		}
278		if (i < ARRAY_SIZE(ex_v110_time_consts)) {
279			*frames_per_second =
280				ex_v110_time_consts[i].frames_per_second /
281				samples_per_frame;
282		} else {
283			*frames_per_second = 256000000 / samples_per_frame /
284					     (65536 - code);
285		}
286	} else {
287		if (samples_per_frame != 1)
288			return -EINVAL;
289
290		for (i = 0; i < ARRAY_SIZE(v110_time_consts); ++i) {
291			if (v110_time_consts[i].code == code ||
292			    v110_time_consts[i].code - 1 == code)
293				break;
294		}
295		if (i < ARRAY_SIZE(v110_time_consts)) {
296			*frames_per_second =
297					v110_time_consts[i].frames_per_second;
298		} else {
299			*frames_per_second = 1000000 / (256 - code);
300		}
301	}
302
303	return 0;
304}
305
306struct parser_state {
307	unsigned int version;
308	bool extended;
309
310	unsigned int frames_per_second;
311	unsigned int samples_per_frame;
312	unsigned int bytes_per_sample;
313	enum code_id code_id;
314	uint32_t byte_count;
315};
316
317static int parse_container_header(struct parser_state *state,
318				  struct container_header *header)
319{
320	uint16_t hdr_size;
321	uint16_t version;
322	uint16_t version_compr;
323
324	hdr_size = le16toh(header->hdr_size);
325	version = le16toh(header->version);
326	version_compr = le16toh(header->version_compr);
327
328	if (memcmp(header->magic, VOC_MAGIC, sizeof(header->magic)))
329		return -EIO;
330
331	if (hdr_size != sizeof(*header))
332		return -EIO;
333
334	if (version_compr != 0x1234 + ~version)
335		return -EIO;
336
337	if (version != VOC_VERSION_1_10 && version != VOC_VERSION_1_20)
338		return -EIO;
339
340	state->version = version;
341
342	return 0;
343}
344
345static bool check_code_id(uint8_t code_id, unsigned int version)
346{
347	unsigned int i;
348
349	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
350		if (code_id != format_maps[i].code_id)
351			continue;
352		if (version >= format_maps[i].minimal_version)
353			return true;
354	}
355
356	return false;
357}
358
359static int parse_v120_format_block(struct parser_state *state,
360				   struct block_v120_format *block)
361{
362	state->frames_per_second = le32toh(block->frames_per_second);
363	state->bytes_per_sample = block->bits_per_sample / 8;
364	state->samples_per_frame = block->samples_per_frame;
365	state->code_id = le16toh(block->code_id);
366	state->byte_count = parse_block_data_size(block->size) - 12;
367
368	if (!check_code_id(state->code_id, VOC_VERSION_1_20))
369		return -EIO;
370
371	return 0;
372}
373
374static int parse_extended_v110_format(struct parser_state *state,
375				      struct block_extended_v110_format *block)
376{
377	unsigned int time_const;
378	unsigned int frames_per_second;
379	int err;
380
381	state->code_id = block->code_id;
382	if (!check_code_id(state->code_id, VOC_VERSION_1_10))
383		return -EIO;
384
385	if (block->ch_mode == 0)
386		state->samples_per_frame = 1;
387	else if (block->ch_mode == 1)
388		state->samples_per_frame = 2;
389	else
390		return -EIO;
391
392	time_const = le16toh(block->time_const);
393	err = parse_time_constant(time_const, state->samples_per_frame,
394				  &frames_per_second, true);
395	if (err < 0)
396		return err;
397	state->frames_per_second = frames_per_second;
398
399	state->extended = true;
400
401	return 0;
402}
403
404static int parse_v110_data(struct parser_state *state,
405			   struct block_v110_data *block)
406{
407	unsigned int time_const;
408	unsigned int frames_per_second;
409	int err;
410
411	if (!state->extended) {
412		state->code_id = block->code_id;
413		if (!check_code_id(state->code_id, VOC_VERSION_1_10))
414			return -EIO;
415
416		time_const = block->time_const;
417		err = parse_time_constant(time_const, 1, &frames_per_second,
418					  false);
419		if (err < 0)
420			return err;
421		state->frames_per_second = frames_per_second;
422		state->samples_per_frame = 1;
423	}
424
425	state->bytes_per_sample = 1;
426	state->byte_count = parse_block_data_size(block->size) - 2;
427
428	return 0;
429}
430
431static int detect_container_version(struct container_context *cntr)
432{
433	struct parser_state *state = cntr->private_data;
434	struct container_header header = {0};
435	int err;
436
437	// 4 bytes were alread read to detect container type.
438	memcpy(&header.magic, cntr->magic, sizeof(cntr->magic));
439	err = container_recursive_read(cntr,
440				       (char *)&header + sizeof(cntr->magic),
441				       sizeof(header) - sizeof(cntr->magic));
442	if (err < 0)
443		return err;
444	if (cntr->eof)
445		return 0;
446
447	return parse_container_header(state, &header);
448}
449
450static int allocate_for_block_cache(struct container_context *cntr,
451				    struct block_header *header, void **buf)
452{
453	uint32_t block_size;
454	char *cache;
455	int err;
456
457	if (header->type == BLOCK_TYPE_V110_DATA)
458		block_size = sizeof(struct block_v110_data);
459	else if (header->type == BLOCK_TYPE_CONTINUOUS_DATA)
460		block_size = sizeof(struct block_continuous_data);
461	else if (header->type == BLOCK_TYPE_EXTENDED_V110_FORMAT)
462		block_size = sizeof(struct block_extended_v110_format);
463	else if (header->type == BLOCK_TYPE_V120_DATA)
464		block_size = sizeof(struct block_v120_format);
465	else
466		block_size = parse_block_data_size(header->size);
467
468	cache = malloc(block_size);
469	if (cache == NULL)
470		return -ENOMEM;
471	memset(cache, 0, block_size);
472
473	memcpy(cache, header, sizeof(*header));
474	err = container_recursive_read(cntr, cache + sizeof(*header),
475				       block_size - sizeof(*header));
476	if (err < 0) {
477		free(cache);
478		return err;
479	}
480	if (cntr->eof) {
481		free(cache);
482		return 0;
483	}
484
485	*buf = cache;
486
487	return 0;
488}
489
490static int cache_data_block(struct container_context *cntr,
491			    struct block_header *header, void **buf)
492{
493	int err;
494
495	// Check type of this block.
496	err = container_recursive_read(cntr, &header->type,
497				       sizeof(header->type));
498	if (err < 0)
499		return err;
500	if (cntr->eof)
501		return 0;
502
503	if (header->type > BLOCK_TYPE_V120_DATA)
504		return -EIO;
505	if (header->type == BLOCK_TYPE_TERMINATOR)
506		return 0;
507
508	// Check size of this block. If the block includes a batch of data,
509	err = container_recursive_read(cntr, &header->size,
510				       sizeof(header->size));
511	if (err < 0)
512		return err;
513	if (cntr->eof)
514		return 0;
515
516	return allocate_for_block_cache(cntr, header, buf);
517}
518
519static int detect_format_block(struct container_context *cntr)
520{
521	struct parser_state *state = cntr->private_data;
522	struct block_header header;
523	void *buf;
524	int err;
525
526again:
527	buf = NULL;
528	err = cache_data_block(cntr, &header, &buf);
529	if (err < 0)
530		return err;
531	if (buf) {
532		if (header.type == BLOCK_TYPE_EXTENDED_V110_FORMAT) {
533			err = parse_extended_v110_format(state, buf);
534		} else if (header.type == BLOCK_TYPE_V120_DATA) {
535			err = parse_v120_format_block(state, buf);
536		} else if (header.type == BLOCK_TYPE_V110_DATA) {
537			err = parse_v110_data(state, buf);
538		} else {
539			free(buf);
540			goto again;
541		}
542
543		free(buf);
544
545		if (err < 0)
546			return err;
547	}
548
549	// Expect to detect block_v110_data.
550	if (header.type == BLOCK_TYPE_EXTENDED_V110_FORMAT)
551		goto again;
552
553	return 0;
554}
555
556static int voc_parser_pre_process(struct container_context *cntr,
557				  snd_pcm_format_t *format,
558				  unsigned int *samples_per_frame,
559				  unsigned int *frames_per_second,
560				  uint64_t *byte_count)
561{
562	struct parser_state *state = cntr->private_data;
563	unsigned int i;
564	int err;
565
566	err = detect_container_version(cntr);
567	if (err < 0)
568		return err;
569
570	err = detect_format_block(cntr);
571	if (err < 0)
572		return err;
573
574	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
575		if (format_maps[i].code_id == state->code_id)
576			break;
577	}
578	if (i == ARRAY_SIZE(format_maps))
579		return -EINVAL;
580
581	*format = format_maps[i].format;
582	*samples_per_frame = state->samples_per_frame;
583	*frames_per_second = state->frames_per_second;
584
585	// This program handles PCM frames in this data block only.
586	*byte_count = state->byte_count;
587
588	return 0;
589}
590
591struct builder_state {
592	unsigned int version;
593	bool extended;
594	enum code_id code_id;
595
596	unsigned int samples_per_frame;
597	unsigned int bytes_per_sample;
598};
599
600static int write_container_header(struct container_context *cntr,
601				  struct container_header *header)
602{
603	struct builder_state *state = cntr->private_data;
604
605	// Process container header.
606	memcpy(header->magic, VOC_MAGIC, sizeof(header->magic));
607	header->hdr_size = htole16(sizeof(*header));
608	header->version = htole16(state->version);
609	header->version_compr = htole16(0x1234 + ~state->version);
610
611	return container_recursive_write(cntr, header, sizeof(*header));
612}
613
614static int write_v120_format_block(struct container_context *cntr,
615				   struct block_v120_format *block,
616				   unsigned int frames_per_second,
617				   uint64_t byte_count)
618{
619	struct builder_state *state = cntr->private_data;
620
621	block->type = BLOCK_TYPE_V120_DATA;
622	build_block_data_size(block->size, 12 + byte_count);
623
624	block->frames_per_second = htole32(frames_per_second);
625	block->bits_per_sample = state->bytes_per_sample * 8;
626	block->samples_per_frame = state->samples_per_frame;
627	block->code_id = htole16(state->code_id);
628
629	return container_recursive_write(cntr, block, sizeof(*block));
630}
631
632static int write_extended_v110_format_block(struct container_context *cntr,
633				unsigned int frames_per_second,
634				struct block_extended_v110_format *block)
635{
636	struct builder_state *state = cntr->private_data;
637	uint16_t time_const;
638	int err;
639
640	block->type = BLOCK_TYPE_EXTENDED_V110_FORMAT;
641	build_block_data_size(block->size, 4);
642
643	// 16 bits are available for this purpose.
644	err = build_time_constant(frames_per_second, state->samples_per_frame,
645				  &time_const, true);
646	if (err < 0)
647		return err;
648	block->time_const = htole16(time_const);
649	block->code_id = htole16(state->code_id);
650
651	if (state->samples_per_frame == 1)
652		block->ch_mode = 0;
653	else
654		block->ch_mode = 1;
655
656	return container_recursive_write(cntr, block, sizeof(*block));
657}
658
659static int write_v110_format_block(struct container_context *cntr,
660				   struct block_v110_data *block,
661				   unsigned int frames_per_second,
662				   uint64_t byte_count)
663{
664	struct builder_state *state = cntr->private_data;
665	uint16_t time_const;
666	int err;
667
668	block->type = BLOCK_TYPE_V110_DATA;
669	build_block_data_size(block->size, 2 + byte_count);
670
671	// These fields were obsoleted by extension.
672	err = build_time_constant(frames_per_second, 1, &time_const, false);
673	if (err < 0)
674		return err;
675	block->time_const = (uint8_t)time_const;
676	block->code_id = state->code_id;
677	return container_recursive_write(cntr, block, sizeof(*block));
678}
679
680static int write_data_blocks(struct container_context *cntr,
681			     unsigned int frames_per_second,
682			     uint64_t byte_count)
683{
684	union {
685		struct container_header header;
686		struct block_v110_data v110_data;
687		struct block_extended_v110_format extended_v110_format;
688		struct block_v120_format v120_format;
689	} buf = {0};
690	struct builder_state *state = cntr->private_data;
691	int err;
692
693	err = write_container_header(cntr, &buf.header);
694	if (err < 0)
695		return err;
696
697	if (state->version == VOC_VERSION_1_20) {
698		err = write_v120_format_block(cntr, &buf.v120_format,
699					      frames_per_second, byte_count);
700	} else {
701		if (state->extended) {
702			err = write_extended_v110_format_block(cntr,
703					frames_per_second,
704					&buf.extended_v110_format);
705			if (err < 0)
706				return err;
707		}
708		err = write_v110_format_block(cntr, &buf.v110_data,
709					      frames_per_second, byte_count);
710	}
711
712	return err;
713}
714
715static int voc_builder_pre_process(struct container_context *cntr,
716				   snd_pcm_format_t *format,
717				   unsigned int *samples_per_frame,
718				   unsigned int *frames_per_second,
719				   uint64_t *byte_count)
720{
721	struct builder_state *state = cntr->private_data;
722	unsigned int i;
723
724	// Validate parameters.
725	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
726		if (format_maps[i].format == *format)
727			break;
728	}
729	if (i == ARRAY_SIZE(format_maps))
730		return -EINVAL;
731	state->code_id = format_maps[i].code_id;
732
733	// Decide container version.
734	if (*samples_per_frame > 2)
735		state->version = VOC_VERSION_1_20;
736	else
737		state->version = format_maps[i].minimal_version;
738	if (state->version == VOC_VERSION_1_10) {
739		if (*samples_per_frame == 2) {
740			for (i = 0;
741			     i < ARRAY_SIZE(ex_v110_time_consts); ++i) {
742				if (ex_v110_time_consts[i].frames_per_second ==
743						*frames_per_second)
744					break;
745			}
746			if (i == ARRAY_SIZE(ex_v110_time_consts))
747				state->version = VOC_VERSION_1_20;
748			else
749				state->extended = true;
750		} else {
751			for (i = 0; i < ARRAY_SIZE(v110_time_consts); ++i) {
752				if (v110_time_consts[i].frames_per_second ==
753							*frames_per_second)
754					break;
755			}
756			if (i == ARRAY_SIZE(v110_time_consts))
757				state->version = VOC_VERSION_1_20;
758		}
759	}
760
761	state->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
762	state->samples_per_frame = *samples_per_frame;
763
764	return write_data_blocks(cntr, *frames_per_second, *byte_count);
765}
766
767static int write_block_terminator(struct container_context *cntr)
768{
769	struct block_terminator block = {0};
770
771	block.type = BLOCK_TYPE_TERMINATOR;
772	return container_recursive_write(cntr, &block, sizeof(block));
773}
774
775static int write_data_size(struct container_context *cntr, uint64_t byte_count)
776{
777	struct builder_state *state = cntr->private_data;
778	off_t offset;
779	uint8_t size_field[3];
780	int err;
781
782	offset = sizeof(struct container_header) + sizeof(uint8_t);
783	if (state->version == VOC_VERSION_1_10 && state->extended)
784		offset += sizeof(struct block_extended_v110_format);
785	err = container_seek_offset(cntr, offset);
786	if (err < 0)
787		return err;
788
789	if (state->version == VOC_VERSION_1_10)
790		offset = 2;
791	else
792		offset = 12;
793
794	if (byte_count > cntr->max_size - offset)
795		byte_count = cntr->max_size;
796	else
797		byte_count += offset;
798	build_block_data_size(size_field, byte_count);
799
800	return container_recursive_write(cntr, &size_field, sizeof(size_field));
801}
802
803static int voc_builder_post_process(struct container_context *cntr,
804				    uint64_t handled_byte_count)
805{
806	int err;
807
808	err = write_block_terminator(cntr);
809	if (err < 0)
810		return err;
811
812	return write_data_size(cntr, handled_byte_count);
813}
814
815const struct container_parser container_parser_voc = {
816	.format = CONTAINER_FORMAT_VOC,
817	.magic = VOC_MAGIC,
818	.max_size = 0xffffff -	// = UINT24_MAX.
819		    sizeof(struct block_terminator),
820	.ops = {
821		.pre_process	= voc_parser_pre_process,
822	},
823	.private_size = sizeof(struct parser_state),
824};
825
826const struct container_builder container_builder_voc = {
827	.format = CONTAINER_FORMAT_VOC,
828	.max_size = 0xffffff -	// = UINT24_MAX.
829		    sizeof(struct block_terminator),
830	.ops = {
831		.pre_process	= voc_builder_pre_process,
832		.post_process	= voc_builder_post_process,
833	},
834	.private_size = sizeof(struct builder_state),
835};
836