Blame axfer/container-voc.c

Packit Service a9274b
// SPDX-License-Identifier: GPL-2.0
Packit Service a9274b
//
Packit Service a9274b
// container-voc.c - a parser/builder for a container of Creative Voice File.
Packit Service a9274b
//
Packit Service a9274b
// Copyright (c) 2018 Takashi Sakamoto <o-takashi@sakamocchi.jp>
Packit Service a9274b
//
Packit Service a9274b
// Licensed under the terms of the GNU General Public License, version 2.
Packit Service a9274b
Packit Service a9274b
#include "container.h"
Packit Service a9274b
#include "misc.h"
Packit Service a9274b
Packit Service a9274b
// Not portable to all of UNIX platforms.
Packit Service a9274b
#include <endian.h>
Packit Service a9274b
Packit Service a9274b
// References:
Packit Service a9274b
//  - http://sox.sourceforge.net/
Packit Service a9274b
Packit Service a9274b
#define VOC_MAGIC		"Creative Voice File\x1a"
Packit Service a9274b
#define VOC_VERSION_1_10	0x010a
Packit Service a9274b
#define VOC_VERSION_1_20	0x0114
Packit Service a9274b
Packit Service a9274b
enum block_type {
Packit Service a9274b
	BLOCK_TYPE_TERMINATOR		= 0x00,
Packit Service a9274b
	BLOCK_TYPE_V110_DATA		= 0x01,
Packit Service a9274b
	BLOCK_TYPE_CONTINUOUS_DATA	= 0x02,
Packit Service a9274b
	BLOCK_TYPE_SILENCE		= 0x03,
Packit Service a9274b
	BLOCK_TYPE_MARKER		= 0x04,
Packit Service a9274b
	BLOCK_TYPE_STRING		= 0x05,
Packit Service a9274b
	BLOCK_TYPE_REPEAT_START		= 0x06,
Packit Service a9274b
	BLOCK_TYPE_REPEAT_END		= 0x07,
Packit Service a9274b
	BLOCK_TYPE_EXTENDED_V110_FORMAT	= 0x08,
Packit Service a9274b
	BLOCK_TYPE_V120_DATA		= 0x09,
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
enum code_id {
Packit Service a9274b
	// Version 1.10.
Packit Service a9274b
	CODE_ID_GENERIC_MBLA_U8			= 0x00,
Packit Service a9274b
	CODE_ID_CREATIVE_ADPCM_8BIT_TO_4BIT_LE	= 0x01,
Packit Service a9274b
	CODE_ID_CREATIVE_ADPCM_8BIT_TO_3BIT_LE	= 0x02,
Packit Service a9274b
	CODE_ID_CREATIVE_ADPCM_8BIT_TO_2BIT_LE	= 0x03,
Packit Service a9274b
	// Version 1.20.
Packit Service a9274b
	CODE_ID_GENERIC_MBLA_S16_LE		= 0x04,
Packit Service a9274b
	CODE_ID_CCIT_A_LAW_LE			= 0x06,
Packit Service a9274b
	CODE_ID_CCIT_MU_LAW_LE			= 0x07,
Packit Service a9274b
	CODE_ID_CREATIVE_ADPCM_16BIT_TO_4BIT_LE	= 0x2000,
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct format_map {
Packit Service a9274b
	unsigned int minimal_version;
Packit Service a9274b
	enum code_id code_id;
Packit Service a9274b
	snd_pcm_format_t format;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static const struct format_map format_maps[] = {
Packit Service a9274b
	{VOC_VERSION_1_10, CODE_ID_GENERIC_MBLA_U8,	SND_PCM_FORMAT_U8},
Packit Service a9274b
	{VOC_VERSION_1_20, CODE_ID_GENERIC_MBLA_S16_LE,	SND_PCM_FORMAT_S16_LE},
Packit Service a9274b
	{VOC_VERSION_1_20, CODE_ID_CCIT_A_LAW_LE,	SND_PCM_FORMAT_A_LAW},
Packit Service a9274b
	{VOC_VERSION_1_20, CODE_ID_CCIT_MU_LAW_LE,	SND_PCM_FORMAT_MU_LAW},
Packit Service a9274b
	// The other formats are not supported by ALSA.
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct container_header {
Packit Service a9274b
	uint8_t magic[20];
Packit Service a9274b
	uint16_t hdr_size;
Packit Service a9274b
	uint16_t version;
Packit Service a9274b
	uint16_t version_compr;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// A format for data blocks except for terminator type.
Packit Service a9274b
struct block_header {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];
Packit Service a9274b
Packit Service a9274b
	uint8_t data[0];
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// Data block for terminator type has an exceptional format.
Packit Service a9274b
struct block_terminator {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct time_const {
Packit Service a9274b
	unsigned int frames_per_second;
Packit Service a9274b
	uint16_t code;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static const struct time_const v110_time_consts[] = {
Packit Service a9274b
	{5512, 74},
Packit Service a9274b
	{8000, 130},
Packit Service a9274b
	{11025, 165},
Packit Service a9274b
	{16000, 193},
Packit Service a9274b
	{22050, 210},
Packit Service a9274b
	{32000, 224},
Packit Service a9274b
	{44100, 233},
Packit Service a9274b
	{48000, 235},
Packit Service a9274b
	{64000, 240},
Packit Service a9274b
	// Time constant for the upper sampling rate is not identical.
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static const struct time_const ex_v110_time_consts[] = {
Packit Service a9274b
	{5512, 19092},
Packit Service a9274b
	{8000, 33536},
Packit Service a9274b
	{11025, 42317},
Packit Service a9274b
	{16000, 49536},
Packit Service a9274b
	{22050, 53927},
Packit Service a9274b
	{32000, 57536},
Packit Service a9274b
	{44100, 59732},
Packit Service a9274b
	{48000, 60203},
Packit Service a9274b
	{64000, 61536},
Packit Service a9274b
	{88200, 62634},
Packit Service a9274b
	{96000, 62870},
Packit Service a9274b
	{176400, 64085},
Packit Service a9274b
	{192000, 64203},
Packit Service a9274b
	// This support up to 192.0 kHz. The rest is for cases with 2ch.
Packit Service a9274b
	{352800, 64811},
Packit Service a9274b
	{384000, 64870},
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// v1.10 format:
Packit Service a9274b
// - monaural.
Packit Service a9274b
// - frames_per_second = 1,000,000 / (256 - time_const)
Packit Service a9274b
struct block_v110_data {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to (2 + the size of frames).
Packit Service a9274b
Packit Service a9274b
	uint8_t time_const;
Packit Service a9274b
	uint8_t code_id;
Packit Service a9274b
	uint8_t frames[0];	// Aligned to little-endian.
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct block_continuous_data {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to the size of frames.
Packit Service a9274b
Packit Service a9274b
	uint8_t frames[0];	// Aligned to little-endian.
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// v1.10 format:
Packit Service a9274b
// - monaural.
Packit Service a9274b
// - frames_per_second = 1,000,000 / (256 - time_const).
Packit Service a9274b
struct block_silence {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to 3.
Packit Service a9274b
Packit Service a9274b
	uint16_t frame_count;
Packit Service a9274b
	uint8_t time_const;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct block_marker {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to 2.
Packit Service a9274b
Packit Service a9274b
	uint16_t mark;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct block_string {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to the length of string with 0x00.
Packit Service a9274b
Packit Service a9274b
	uint8_t chars[0];
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct block_repeat_start {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to 2.
Packit Service a9274b
Packit Service a9274b
	uint16_t count;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
struct block_repeat_end {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to 0.
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// Extended v1.10 format:
Packit Service a9274b
// - manaural/stereo.
Packit Service a9274b
// - frames_per_second =
Packit Service a9274b
//		256,000,000 / (samples_per_frame * (65536 - time_const)).
Packit Service a9274b
// - Appear just before v110_data block.
Packit Service a9274b
struct block_extended_v110_format {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to 4.
Packit Service a9274b
Packit Service a9274b
	uint16_t time_const;
Packit Service a9274b
	uint8_t code_id;
Packit Service a9274b
	uint8_t ch_mode;	// 0 is monaural, 1 is stereo.
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// v1.20 format:
Packit Service a9274b
// - monaural/stereo.
Packit Service a9274b
// - 8/16 bits_per_sample.
Packit Service a9274b
// - time_const is not used.
Packit Service a9274b
// - code_id is extended.
Packit Service a9274b
struct block_v120_format {
Packit Service a9274b
	uint8_t type;
Packit Service a9274b
	uint8_t size[3];	// Equals to (12 + ).
Packit Service a9274b
Packit Service a9274b
	uint32_t frames_per_second;
Packit Service a9274b
	uint8_t bits_per_sample;
Packit Service a9274b
	uint8_t samples_per_frame;
Packit Service a9274b
	uint16_t code_id;
Packit Service a9274b
	uint8_t reserved[4];
Packit Service a9274b
Packit Service a9274b
	uint8_t frames[0];	// Aligned to little-endian.
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
// Aligned to little endian order but 24 bits field.
Packit Service a9274b
static uint32_t parse_block_data_size(uint8_t fields[3])
Packit Service a9274b
{
Packit Service a9274b
	return (fields[2] << 16) | (fields[1] << 8) | fields[0];
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void build_block_data_size(uint8_t fields[3], unsigned int size)
Packit Service a9274b
{
Packit Service a9274b
	fields[0] = (size & 0x0000ff);
Packit Service a9274b
	fields[1] = (size & 0x00ff00) >> 8;
Packit Service a9274b
	fields[2] = (size & 0xff0000) >> 16;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int build_time_constant(unsigned int frames_per_second,
Packit Service a9274b
			       unsigned int samples_per_frame, uint16_t *code,
Packit Service a9274b
			       bool extended)
Packit Service a9274b
{
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	// 16 bits are available for this purpose.
Packit Service a9274b
	if (extended) {
Packit Service a9274b
		if (samples_per_frame > 2)
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
		frames_per_second *= samples_per_frame;
Packit Service a9274b
Packit Service a9274b
		for (i = 0; i < ARRAY_SIZE(ex_v110_time_consts); ++i) {
Packit Service a9274b
			if (ex_v110_time_consts[i].frames_per_second ==
Packit Service a9274b
					frames_per_second)
Packit Service a9274b
				break;
Packit Service a9274b
		}
Packit Service a9274b
		if (i < ARRAY_SIZE(ex_v110_time_consts) &&
Packit Service a9274b
		    frames_per_second <= 192000) {
Packit Service a9274b
			*code = ex_v110_time_consts[i].code;
Packit Service a9274b
		} else {
Packit Service a9274b
			*code = 65536 - 256000000 / frames_per_second;
Packit Service a9274b
		}
Packit Service a9274b
	} else {
Packit Service a9274b
		if (samples_per_frame != 1)
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
Packit Service a9274b
		for (i = 0; i < ARRAY_SIZE(v110_time_consts); ++i) {
Packit Service a9274b
			if (v110_time_consts[i].frames_per_second ==
Packit Service a9274b
					frames_per_second)
Packit Service a9274b
			break;
Packit Service a9274b
		}
Packit Service a9274b
		// Should be within 8 bit.
Packit Service a9274b
		if (i < ARRAY_SIZE(v110_time_consts))
Packit Service a9274b
			*code = (uint8_t)v110_time_consts[i].code;
Packit Service a9274b
		else
Packit Service a9274b
			*code = 256 - 1000000 / frames_per_second;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static unsigned int parse_time_constant(uint16_t code,
Packit Service a9274b
					unsigned int samples_per_frame,
Packit Service a9274b
					unsigned int *frames_per_second,
Packit Service a9274b
					bool extended)
Packit Service a9274b
{
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	if (extended) {
Packit Service a9274b
		if (samples_per_frame > 2)
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
Packit Service a9274b
		for (i = 0; i < ARRAY_SIZE(ex_v110_time_consts); ++i) {
Packit Service a9274b
			if (ex_v110_time_consts[i].code == code ||
Packit Service a9274b
			    ex_v110_time_consts[i].code - 1 == code)
Packit Service a9274b
				break;
Packit Service a9274b
		}
Packit Service a9274b
		if (i < ARRAY_SIZE(ex_v110_time_consts)) {
Packit Service a9274b
			*frames_per_second =
Packit Service a9274b
				ex_v110_time_consts[i].frames_per_second /
Packit Service a9274b
				samples_per_frame;
Packit Service a9274b
		} else {
Packit Service a9274b
			*frames_per_second = 256000000 / samples_per_frame /
Packit Service a9274b
					     (65536 - code);
Packit Service a9274b
		}
Packit Service a9274b
	} else {
Packit Service a9274b
		if (samples_per_frame != 1)
Packit Service a9274b
			return -EINVAL;
Packit Service a9274b
Packit Service a9274b
		for (i = 0; i < ARRAY_SIZE(v110_time_consts); ++i) {
Packit Service a9274b
			if (v110_time_consts[i].code == code ||
Packit Service a9274b
			    v110_time_consts[i].code - 1 == code)
Packit Service a9274b
				break;
Packit Service a9274b
		}
Packit Service a9274b
		if (i < ARRAY_SIZE(v110_time_consts)) {
Packit Service a9274b
			*frames_per_second =
Packit Service a9274b
					v110_time_consts[i].frames_per_second;
Packit Service a9274b
		} else {
Packit Service a9274b
			*frames_per_second = 1000000 / (256 - code);
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
struct parser_state {
Packit Service a9274b
	unsigned int version;
Packit Service a9274b
	bool extended;
Packit Service a9274b
Packit Service a9274b
	unsigned int frames_per_second;
Packit Service a9274b
	unsigned int samples_per_frame;
Packit Service a9274b
	unsigned int bytes_per_sample;
Packit Service a9274b
	enum code_id code_id;
Packit Service a9274b
	uint32_t byte_count;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static int parse_container_header(struct parser_state *state,
Packit Service a9274b
				  struct container_header *header)
Packit Service a9274b
{
Packit Service a9274b
	uint16_t hdr_size;
Packit Service a9274b
	uint16_t version;
Packit Service a9274b
	uint16_t version_compr;
Packit Service a9274b
Packit Service a9274b
	hdr_size = le16toh(header->hdr_size);
Packit Service a9274b
	version = le16toh(header->version);
Packit Service a9274b
	version_compr = le16toh(header->version_compr);
Packit Service a9274b
Packit Service a9274b
	if (memcmp(header->magic, VOC_MAGIC, sizeof(header->magic)))
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	if (hdr_size != sizeof(*header))
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	if (version_compr != 0x1234 + ~version)
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	if (version != VOC_VERSION_1_10 && version != VOC_VERSION_1_20)
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	state->version = version;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static bool check_code_id(uint8_t code_id, unsigned int version)
Packit Service a9274b
{
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
Packit Service a9274b
		if (code_id != format_maps[i].code_id)
Packit Service a9274b
			continue;
Packit Service a9274b
		if (version >= format_maps[i].minimal_version)
Packit Service a9274b
			return true;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return false;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int parse_v120_format_block(struct parser_state *state,
Packit Service a9274b
				   struct block_v120_format *block)
Packit Service a9274b
{
Packit Service a9274b
	state->frames_per_second = le32toh(block->frames_per_second);
Packit Service a9274b
	state->bytes_per_sample = block->bits_per_sample / 8;
Packit Service a9274b
	state->samples_per_frame = block->samples_per_frame;
Packit Service a9274b
	state->code_id = le16toh(block->code_id);
Packit Service a9274b
	state->byte_count = parse_block_data_size(block->size) - 12;
Packit Service a9274b
Packit Service a9274b
	if (!check_code_id(state->code_id, VOC_VERSION_1_20))
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int parse_extended_v110_format(struct parser_state *state,
Packit Service a9274b
				      struct block_extended_v110_format *block)
Packit Service a9274b
{
Packit Service a9274b
	unsigned int time_const;
Packit Service a9274b
	unsigned int frames_per_second;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	state->code_id = block->code_id;
Packit Service a9274b
	if (!check_code_id(state->code_id, VOC_VERSION_1_10))
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	if (block->ch_mode == 0)
Packit Service a9274b
		state->samples_per_frame = 1;
Packit Service a9274b
	else if (block->ch_mode == 1)
Packit Service a9274b
		state->samples_per_frame = 2;
Packit Service a9274b
	else
Packit Service a9274b
		return -EIO;
Packit Service a9274b
Packit Service a9274b
	time_const = le16toh(block->time_const);
Packit Service a9274b
	err = parse_time_constant(time_const, state->samples_per_frame,
Packit Service a9274b
				  &frames_per_second, true);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	state->frames_per_second = frames_per_second;
Packit Service a9274b
Packit Service a9274b
	state->extended = true;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int parse_v110_data(struct parser_state *state,
Packit Service a9274b
			   struct block_v110_data *block)
Packit Service a9274b
{
Packit Service a9274b
	unsigned int time_const;
Packit Service a9274b
	unsigned int frames_per_second;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	if (!state->extended) {
Packit Service a9274b
		state->code_id = block->code_id;
Packit Service a9274b
		if (!check_code_id(state->code_id, VOC_VERSION_1_10))
Packit Service a9274b
			return -EIO;
Packit Service a9274b
Packit Service a9274b
		time_const = block->time_const;
Packit Service a9274b
		err = parse_time_constant(time_const, 1, &frames_per_second,
Packit Service a9274b
					  false);
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
		state->frames_per_second = frames_per_second;
Packit Service a9274b
		state->samples_per_frame = 1;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	state->bytes_per_sample = 1;
Packit Service a9274b
	state->byte_count = parse_block_data_size(block->size) - 2;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int detect_container_version(struct container_context *cntr)
Packit Service a9274b
{
Packit Service a9274b
	struct parser_state *state = cntr->private_data;
Packit Service a9274b
	struct container_header header = {0};
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	// 4 bytes were alread read to detect container type.
Packit Service a9274b
	memcpy(&header.magic, cntr->magic, sizeof(cntr->magic));
Packit Service a9274b
	err = container_recursive_read(cntr,
Packit Service a9274b
				       (char *)&header + sizeof(cntr->magic),
Packit Service a9274b
				       sizeof(header) - sizeof(cntr->magic));
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	if (cntr->eof)
Packit Service a9274b
		return 0;
Packit Service a9274b
Packit Service a9274b
	return parse_container_header(state, &header);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int allocate_for_block_cache(struct container_context *cntr,
Packit Service a9274b
				    struct block_header *header, void **buf)
Packit Service a9274b
{
Packit Service a9274b
	uint32_t block_size;
Packit Service a9274b
	char *cache;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	if (header->type == BLOCK_TYPE_V110_DATA)
Packit Service a9274b
		block_size = sizeof(struct block_v110_data);
Packit Service a9274b
	else if (header->type == BLOCK_TYPE_CONTINUOUS_DATA)
Packit Service a9274b
		block_size = sizeof(struct block_continuous_data);
Packit Service a9274b
	else if (header->type == BLOCK_TYPE_EXTENDED_V110_FORMAT)
Packit Service a9274b
		block_size = sizeof(struct block_extended_v110_format);
Packit Service a9274b
	else if (header->type == BLOCK_TYPE_V120_DATA)
Packit Service a9274b
		block_size = sizeof(struct block_v120_format);
Packit Service a9274b
	else
Packit Service a9274b
		block_size = parse_block_data_size(header->size);
Packit Service a9274b
Packit Service a9274b
	cache = malloc(block_size);
Packit Service a9274b
	if (cache == NULL)
Packit Service a9274b
		return -ENOMEM;
Packit Service a9274b
	memset(cache, 0, block_size);
Packit Service a9274b
Packit Service a9274b
	memcpy(cache, header, sizeof(*header));
Packit Service a9274b
	err = container_recursive_read(cntr, cache + sizeof(*header),
Packit Service a9274b
				       block_size - sizeof(*header));
Packit Service a9274b
	if (err < 0) {
Packit Service a9274b
		free(cache);
Packit Service a9274b
		return err;
Packit Service a9274b
	}
Packit Service a9274b
	if (cntr->eof) {
Packit Service a9274b
		free(cache);
Packit Service a9274b
		return 0;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	*buf = cache;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int cache_data_block(struct container_context *cntr,
Packit Service a9274b
			    struct block_header *header, void **buf)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	// Check type of this block.
Packit Service a9274b
	err = container_recursive_read(cntr, &header->type,
Packit Service a9274b
				       sizeof(header->type));
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	if (cntr->eof)
Packit Service a9274b
		return 0;
Packit Service a9274b
Packit Service a9274b
	if (header->type > BLOCK_TYPE_V120_DATA)
Packit Service a9274b
		return -EIO;
Packit Service a9274b
	if (header->type == BLOCK_TYPE_TERMINATOR)
Packit Service a9274b
		return 0;
Packit Service a9274b
Packit Service a9274b
	// Check size of this block. If the block includes a batch of data,
Packit Service a9274b
	err = container_recursive_read(cntr, &header->size,
Packit Service a9274b
				       sizeof(header->size));
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	if (cntr->eof)
Packit Service a9274b
		return 0;
Packit Service a9274b
Packit Service a9274b
	return allocate_for_block_cache(cntr, header, buf);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int detect_format_block(struct container_context *cntr)
Packit Service a9274b
{
Packit Service a9274b
	struct parser_state *state = cntr->private_data;
Packit Service a9274b
	struct block_header header;
Packit Service a9274b
	void *buf;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
again:
Packit Service a9274b
	buf = NULL;
Packit Service a9274b
	err = cache_data_block(cntr, &header, &buf;;
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	if (buf) {
Packit Service a9274b
		if (header.type == BLOCK_TYPE_EXTENDED_V110_FORMAT) {
Packit Service a9274b
			err = parse_extended_v110_format(state, buf);
Packit Service a9274b
		} else if (header.type == BLOCK_TYPE_V120_DATA) {
Packit Service a9274b
			err = parse_v120_format_block(state, buf);
Packit Service a9274b
		} else if (header.type == BLOCK_TYPE_V110_DATA) {
Packit Service a9274b
			err = parse_v110_data(state, buf);
Packit Service a9274b
		} else {
Packit Service a9274b
			free(buf);
Packit Service a9274b
			goto again;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		free(buf);
Packit Service a9274b
Packit Service a9274b
		if (err < 0)
Packit Service a9274b
			return err;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	// Expect to detect block_v110_data.
Packit Service a9274b
	if (header.type == BLOCK_TYPE_EXTENDED_V110_FORMAT)
Packit Service a9274b
		goto again;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int voc_parser_pre_process(struct container_context *cntr,
Packit Service a9274b
				  snd_pcm_format_t *format,
Packit Service a9274b
				  unsigned int *samples_per_frame,
Packit Service a9274b
				  unsigned int *frames_per_second,
Packit Service a9274b
				  uint64_t *byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct parser_state *state = cntr->private_data;
Packit Service a9274b
	int i;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	err = detect_container_version(cntr);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	err = detect_format_block(cntr);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
Packit Service a9274b
		if (format_maps[i].code_id == state->code_id)
Packit Service a9274b
			break;
Packit Service a9274b
	}
Packit Service a9274b
	if (i == ARRAY_SIZE(format_maps))
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
Packit Service a9274b
	*format = format_maps[i].format;
Packit Service a9274b
	*samples_per_frame = state->samples_per_frame;
Packit Service a9274b
	*frames_per_second = state->frames_per_second;
Packit Service a9274b
Packit Service a9274b
	// This program handles PCM frames in this data block only.
Packit Service a9274b
	*byte_count = state->byte_count;
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
struct builder_state {
Packit Service a9274b
	unsigned int version;
Packit Service a9274b
	bool extended;
Packit Service a9274b
	enum code_id code_id;
Packit Service a9274b
Packit Service a9274b
	unsigned int samples_per_frame;
Packit Service a9274b
	unsigned int bytes_per_sample;
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
static int write_container_header(struct container_context *cntr,
Packit Service a9274b
				  struct container_header *header)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
Packit Service a9274b
	// Process container header.
Packit Service a9274b
	memcpy(header->magic, VOC_MAGIC, sizeof(header->magic));
Packit Service a9274b
	header->hdr_size = htole16(sizeof(*header));
Packit Service a9274b
	header->version = htole16(state->version);
Packit Service a9274b
	header->version_compr = htole16(0x1234 + ~state->version);
Packit Service a9274b
Packit Service a9274b
	return container_recursive_write(cntr, header, sizeof(*header));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int write_v120_format_block(struct container_context *cntr,
Packit Service a9274b
				   struct block_v120_format *block,
Packit Service a9274b
				   unsigned int frames_per_second,
Packit Service a9274b
				   uint64_t byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
Packit Service a9274b
	block->type = BLOCK_TYPE_V120_DATA;
Packit Service a9274b
	build_block_data_size(block->size, 12 + byte_count);
Packit Service a9274b
Packit Service a9274b
	block->frames_per_second = htole32(frames_per_second);
Packit Service a9274b
	block->bits_per_sample = state->bytes_per_sample * 8;
Packit Service a9274b
	block->samples_per_frame = state->samples_per_frame;
Packit Service a9274b
	block->code_id = htole16(state->code_id);
Packit Service a9274b
Packit Service a9274b
	return container_recursive_write(cntr, block, sizeof(*block));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int write_extended_v110_format_block(struct container_context *cntr,
Packit Service a9274b
				unsigned int frames_per_second,
Packit Service a9274b
				struct block_extended_v110_format *block)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
	uint16_t time_const;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	block->type = BLOCK_TYPE_EXTENDED_V110_FORMAT;
Packit Service a9274b
	build_block_data_size(block->size, 4);
Packit Service a9274b
Packit Service a9274b
	// 16 bits are available for this purpose.
Packit Service a9274b
	err = build_time_constant(frames_per_second, state->samples_per_frame,
Packit Service a9274b
				  &time_const, true);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	block->time_const = htole16(time_const);
Packit Service a9274b
	block->code_id = htole16(state->code_id);
Packit Service a9274b
Packit Service a9274b
	if (state->samples_per_frame == 1)
Packit Service a9274b
		block->ch_mode = 0;
Packit Service a9274b
	else
Packit Service a9274b
		block->ch_mode = 1;
Packit Service a9274b
Packit Service a9274b
	return container_recursive_write(cntr, block, sizeof(*block));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int write_v110_format_block(struct container_context *cntr,
Packit Service a9274b
				   struct block_v110_data *block,
Packit Service a9274b
				   unsigned int frames_per_second,
Packit Service a9274b
				   uint64_t byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
	uint16_t time_const;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	block->type = BLOCK_TYPE_V110_DATA;
Packit Service a9274b
	build_block_data_size(block->size, 2 + byte_count);
Packit Service a9274b
Packit Service a9274b
	// These fields were obsoleted by extension.
Packit Service a9274b
	err = build_time_constant(frames_per_second, 1, &time_const, false);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
	block->time_const = (uint8_t)time_const;
Packit Service a9274b
	block->code_id = state->code_id;
Packit Service a9274b
	return container_recursive_write(cntr, block, sizeof(*block));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int write_data_blocks(struct container_context *cntr,
Packit Service a9274b
			     unsigned int frames_per_second,
Packit Service a9274b
			     uint64_t byte_count)
Packit Service a9274b
{
Packit Service a9274b
	union {
Packit Service a9274b
		struct container_header header;
Packit Service a9274b
		struct block_v110_data v110_data;
Packit Service a9274b
		struct block_extended_v110_format extended_v110_format;
Packit Service a9274b
		struct block_v120_format v120_format;
Packit Service a9274b
	} buf = {0};
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	err = write_container_header(cntr, &buf.header);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	if (state->version == VOC_VERSION_1_20) {
Packit Service a9274b
		err = write_v120_format_block(cntr, &buf.v120_format,
Packit Service a9274b
					      frames_per_second, byte_count);
Packit Service a9274b
	} else {
Packit Service a9274b
		if (state->extended) {
Packit Service a9274b
			err = write_extended_v110_format_block(cntr,
Packit Service a9274b
					frames_per_second,
Packit Service a9274b
					&buf.extended_v110_format);
Packit Service a9274b
			if (err < 0)
Packit Service a9274b
				return err;
Packit Service a9274b
		}
Packit Service a9274b
		err = write_v110_format_block(cntr, &buf.v110_data,
Packit Service a9274b
					      frames_per_second, byte_count);
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int voc_builder_pre_process(struct container_context *cntr,
Packit Service a9274b
				   snd_pcm_format_t *format,
Packit Service a9274b
				   unsigned int *samples_per_frame,
Packit Service a9274b
				   unsigned int *frames_per_second,
Packit Service a9274b
				   uint64_t *byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
	int i;
Packit Service a9274b
Packit Service a9274b
	// Validate parameters.
Packit Service a9274b
	for (i = 0; i < ARRAY_SIZE(format_maps); ++i) {
Packit Service a9274b
		if (format_maps[i].format == *format)
Packit Service a9274b
			break;
Packit Service a9274b
	}
Packit Service a9274b
	if (i == ARRAY_SIZE(format_maps))
Packit Service a9274b
		return -EINVAL;
Packit Service a9274b
	state->code_id = format_maps[i].code_id;
Packit Service a9274b
Packit Service a9274b
	// Decide container version.
Packit Service a9274b
	if (*samples_per_frame > 2)
Packit Service a9274b
		state->version = VOC_VERSION_1_20;
Packit Service a9274b
	else
Packit Service a9274b
		state->version = format_maps[i].minimal_version;
Packit Service a9274b
	if (state->version == VOC_VERSION_1_10) {
Packit Service a9274b
		if (*samples_per_frame == 2) {
Packit Service a9274b
			for (i = 0;
Packit Service a9274b
			     i < ARRAY_SIZE(ex_v110_time_consts); ++i) {
Packit Service a9274b
				if (ex_v110_time_consts[i].frames_per_second ==
Packit Service a9274b
						*frames_per_second)
Packit Service a9274b
					break;
Packit Service a9274b
			}
Packit Service a9274b
			if (i == ARRAY_SIZE(ex_v110_time_consts))
Packit Service a9274b
				state->version = VOC_VERSION_1_20;
Packit Service a9274b
			else
Packit Service a9274b
				state->extended = true;
Packit Service a9274b
		} else {
Packit Service a9274b
			for (i = 0; i < ARRAY_SIZE(v110_time_consts); ++i) {
Packit Service a9274b
				if (v110_time_consts[i].frames_per_second ==
Packit Service a9274b
							*frames_per_second)
Packit Service a9274b
					break;
Packit Service a9274b
			}
Packit Service a9274b
			if (i == ARRAY_SIZE(v110_time_consts))
Packit Service a9274b
				state->version = VOC_VERSION_1_20;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	state->bytes_per_sample = snd_pcm_format_physical_width(*format) / 8;
Packit Service a9274b
	state->samples_per_frame = *samples_per_frame;
Packit Service a9274b
Packit Service a9274b
	return write_data_blocks(cntr, *frames_per_second, *byte_count);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int write_block_terminator(struct container_context *cntr)
Packit Service a9274b
{
Packit Service a9274b
	struct block_terminator block = {0};
Packit Service a9274b
Packit Service a9274b
	block.type = BLOCK_TYPE_TERMINATOR;
Packit Service a9274b
	return container_recursive_write(cntr, &block, sizeof(block));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int write_data_size(struct container_context *cntr, uint64_t byte_count)
Packit Service a9274b
{
Packit Service a9274b
	struct builder_state *state = cntr->private_data;
Packit Service a9274b
	off64_t offset;
Packit Service a9274b
	uint8_t size_field[3];
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	offset = sizeof(struct container_header) + sizeof(uint8_t);
Packit Service a9274b
	if (state->version == VOC_VERSION_1_10 && state->extended)
Packit Service a9274b
		offset += sizeof(struct block_extended_v110_format);
Packit Service a9274b
	err = container_seek_offset(cntr, offset);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	if (state->version == VOC_VERSION_1_10)
Packit Service a9274b
		offset = 2;
Packit Service a9274b
	else
Packit Service a9274b
		offset = 12;
Packit Service a9274b
Packit Service a9274b
	if (byte_count > cntr->max_size - offset)
Packit Service a9274b
		byte_count = cntr->max_size;
Packit Service a9274b
	else
Packit Service a9274b
		byte_count += offset;
Packit Service a9274b
	build_block_data_size(size_field, byte_count);
Packit Service a9274b
Packit Service a9274b
	return container_recursive_write(cntr, &size_field, sizeof(size_field));
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static int voc_builder_post_process(struct container_context *cntr,
Packit Service a9274b
				    uint64_t handled_byte_count)
Packit Service a9274b
{
Packit Service a9274b
	int err;
Packit Service a9274b
Packit Service a9274b
	err = write_block_terminator(cntr);
Packit Service a9274b
	if (err < 0)
Packit Service a9274b
		return err;
Packit Service a9274b
Packit Service a9274b
	return write_data_size(cntr, handled_byte_count);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
const struct container_parser container_parser_voc = {
Packit Service a9274b
	.format = CONTAINER_FORMAT_VOC,
Packit Service a9274b
	.magic = VOC_MAGIC,
Packit Service a9274b
	.max_size = 0xffffff -	// = UINT24_MAX.
Packit Service a9274b
		    sizeof(struct block_terminator),
Packit Service a9274b
	.ops = {
Packit Service a9274b
		.pre_process	= voc_parser_pre_process,
Packit Service a9274b
	},
Packit Service a9274b
	.private_size = sizeof(struct parser_state),
Packit Service a9274b
};
Packit Service a9274b
Packit Service a9274b
const struct container_builder container_builder_voc = {
Packit Service a9274b
	.format = CONTAINER_FORMAT_VOC,
Packit Service a9274b
	.max_size = 0xffffff -	// = UINT24_MAX.
Packit Service a9274b
		    sizeof(struct block_terminator),
Packit Service a9274b
	.ops = {
Packit Service a9274b
		.pre_process	= voc_builder_pre_process,
Packit Service a9274b
		.post_process	= voc_builder_post_process,
Packit Service a9274b
	},
Packit Service a9274b
	.private_size = sizeof(struct builder_state),
Packit Service a9274b
};