format.c 20.6 KB
Newer Older
1
/* libFLAC - Free Lossless Audio Codec library
2
 * Copyright (C) 2000-2009  Josh Coalson
3
 * Copyright (C) 2011-2016  Xiph.Org Foundation
Josh Coalson's avatar
Josh Coalson committed
4
 *
5 6 7
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
Josh Coalson's avatar
Josh Coalson committed
8
 *
9 10
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
Josh Coalson's avatar
Josh Coalson committed
11
 *
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * - Neither the name of the Xiph.org Foundation nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Josh Coalson's avatar
Josh Coalson committed
31 32
 */

33
#ifdef HAVE_CONFIG_H
Josh Coalson's avatar
Josh Coalson committed
34 35 36
#  include <config.h>
#endif

Josh Coalson's avatar
Josh Coalson committed
37
#include <stdio.h>
38
#include <stdlib.h> /* for qsort() */
39
#include <string.h> /* for memset() */
40
#include "FLAC/assert.h"
Josh Coalson's avatar
Josh Coalson committed
41
#include "FLAC/format.h"
42
#include "share/alloc.h"
43
#include "share/compat.h"
44
#include "private/format.h"
45
#include "private/macros.h"
Josh Coalson's avatar
Josh Coalson committed
46

Erik de Castro Lopo's avatar
Erik de Castro Lopo committed
47 48
/* PACKAGE_VERSION should come from configure */
FLAC_API const char *FLAC__VERSION_STRING = PACKAGE_VERSION;
49

Erik de Castro Lopo's avatar
Erik de Castro Lopo committed
50
FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " PACKAGE_VERSION " 20170101";
Josh Coalson's avatar
Josh Coalson committed
51

52 53
FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' };
FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143;
Josh Coalson's avatar
Josh Coalson committed
54
FLAC_API const unsigned FLAC__STREAM_SYNC_LEN = 32; /* bits */
Josh Coalson's avatar
Josh Coalson committed
55

56 57 58 59 60 61 62 63 64
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */
65

66
FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */
Josh Coalson's avatar
Josh Coalson committed
67

68 69 70
FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */
Josh Coalson's avatar
Josh Coalson committed
71

72
FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff);
Josh Coalson's avatar
Josh Coalson committed
73

74 75
FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */
76

77 78
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */
Josh Coalson's avatar
Josh Coalson committed
79
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */
80 81 82 83 84 85

FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */
Josh Coalson's avatar
Josh Coalson committed
86
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */
87 88 89 90
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */

FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */
91
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */
Josh Coalson's avatar
Josh Coalson committed
92
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */
93 94
FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */

95 96 97 98 99 100
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */
101
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */
102 103
FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */

104 105 106
FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */
FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */
Josh Coalson's avatar
Josh Coalson committed
107

108 109
FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC = 0x3ffe;
FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */
110 111
FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */
FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */
112 113 114 115 116 117
FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */
FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */
FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */
FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */
FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */
FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */
118

119
FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */
Josh Coalson's avatar
Josh Coalson committed
120

121 122 123
FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */
FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */
FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */
124
FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */
125
FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */
126

127
FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)-1 */
128
FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER = 31; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN)-1 */
Josh Coalson's avatar
Josh Coalson committed
129

130
FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[] = {
131 132
	"PARTITIONED_RICE",
	"PARTITIONED_RICE2"
133 134
};

135 136
FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN = 4; /* bits */
FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN = 5; /* bits */
Josh Coalson's avatar
Josh Coalson committed
137

138 139 140
FLAC_API const unsigned FLAC__SUBFRAME_ZERO_PAD_LEN = 1; /* bits */
FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LEN = 6; /* bits */
FLAC_API const unsigned FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN = 1; /* bits */
141

142 143 144 145
FLAC_API const unsigned FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK = 0x00;
FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK = 0x02;
FLAC_API const unsigned FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK = 0x10;
FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK = 0x40;
146

147
FLAC_API const char * const FLAC__SubframeTypeString[] = {
148 149 150 151 152 153
	"CONSTANT",
	"VERBATIM",
	"FIXED",
	"LPC"
};

154
FLAC_API const char * const FLAC__ChannelAssignmentString[] = {
155 156 157 158 159 160
	"INDEPENDENT",
	"LEFT_SIDE",
	"RIGHT_SIDE",
	"MID_SIDE"
};

161
FLAC_API const char * const FLAC__FrameNumberTypeString[] = {
Josh Coalson's avatar
Josh Coalson committed
162 163 164 165
	"FRAME_NUMBER_TYPE_FRAME_NUMBER",
	"FRAME_NUMBER_TYPE_SAMPLE_NUMBER"
};

166
FLAC_API const char * const FLAC__MetadataTypeString[] = {
167 168
	"STREAMINFO",
	"PADDING",
169
	"APPLICATION",
170
	"SEEKTABLE",
171
	"VORBIS_COMMENT",
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
	"CUESHEET",
	"PICTURE"
};

FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[] = {
	"Other",
	"32x32 pixels 'file icon' (PNG only)",
	"Other file icon",
	"Cover (front)",
	"Cover (back)",
	"Leaflet page",
	"Media (e.g. label side of CD)",
	"Lead artist/lead performer/soloist",
	"Artist/performer",
	"Conductor",
	"Band/Orchestra",
	"Composer",
	"Lyricist/text writer",
	"Recording Location",
	"During recording",
	"During performance",
	"Movie/video screen capture",
	"A bright coloured fish",
	"Illustration",
	"Band/artist logotype",
	"Publisher/Studio logotype"
198
};
199

200
FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate)
201 202 203 204 205 206 207 208
{
	if(sample_rate == 0 || sample_rate > FLAC__MAX_SAMPLE_RATE) {
		return false;
	}
	else
		return true;
}

209 210 211 212 213 214 215 216 217 218
FLAC_API FLAC__bool FLAC__format_blocksize_is_subset(unsigned blocksize, unsigned sample_rate)
{
	if(blocksize > 16384)
		return false;
	else if(sample_rate <= 48000 && blocksize > 4608)
		return false;
	else
		return true;
}

219
FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate)
220 221
{
	if(
222
		!FLAC__format_sample_rate_is_valid(sample_rate) ||
223
		(
Josh Coalson's avatar
Josh Coalson committed
224
			sample_rate >= (1u << 16) &&
225 226 227 228 229 230 231 232
			!(sample_rate % 1000 == 0 || sample_rate % 10 == 0)
		)
	) {
		return false;
	}
	else
		return true;
}
233

Josh Coalson's avatar
Josh Coalson committed
234
/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
235
FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table)
236 237 238 239 240 241 242 243 244
{
	unsigned i;
	FLAC__uint64 prev_sample_number = 0;
	FLAC__bool got_prev = false;

	FLAC__ASSERT(0 != seek_table);

	for(i = 0; i < seek_table->num_points; i++) {
		if(got_prev) {
245 246 247 248
			if(
				seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER &&
				seek_table->points[i].sample_number <= prev_sample_number
			)
249 250 251 252 253 254 255 256
				return false;
		}
		prev_sample_number = seek_table->points[i].sample_number;
		got_prev = true;
	}

	return true;
}
257 258 259 260 261 262 263 264 265 266 267 268 269

/* used as the sort predicate for qsort() */
static int seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r)
{
	/* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */
	if(l->sample_number == r->sample_number)
		return 0;
	else if(l->sample_number < r->sample_number)
		return -1;
	else
		return 1;
}

Josh Coalson's avatar
Josh Coalson committed
270
/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
271
FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table)
272 273 274 275
{
	unsigned i, j;
	FLAC__bool first;

Josh Coalson's avatar
Josh Coalson committed
276 277
	FLAC__ASSERT(0 != seek_table);

278 279 280
	if (seek_table->num_points == 0)
		return 0;

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	/* sort the seekpoints */
	qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (*)(const void *, const void *))seekpoint_compare_);

	/* uniquify the seekpoints */
	first = true;
	for(i = j = 0; i < seek_table->num_points; i++) {
		if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) {
			if(!first) {
				if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number)
					continue;
			}
		}
		first = false;
		seek_table->points[j++] = seek_table->points[i];
	}

297 298 299 300
	for(i = j; i < seek_table->num_points; i++) {
		seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER;
		seek_table->points[i].stream_offset = 0;
		seek_table->points[i].frame_samples = 0;
301 302 303 304
	}

	return j;
}
305

306 307 308 309 310 311
/*
 * also disallows non-shortest-form encodings, c.f.
 *   http://www.unicode.org/versions/corrigendum1.html
 * and a more clear explanation at the end of this section:
 *   http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
 */
Josh Coalson's avatar
Josh Coalson committed
312
static unsigned utf8len_(const FLAC__byte *utf8)
313 314
{
	FLAC__ASSERT(0 != utf8);
315
	if ((utf8[0] & 0x80) == 0) {
316
		return 1;
317 318
	}
	else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) {
Josh Coalson's avatar
Josh Coalson committed
319
		if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */
320
			return 0;
321
		return 2;
322 323 324 325 326 327 328 329 330
	}
	else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) {
		if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */
			return 0;
		/* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */
		if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */
			return 0;
		if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */
			return 0;
331
		return 3;
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
	}
	else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) {
		if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */
			return 0;
		return 4;
	}
	else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) {
		if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */
			return 0;
		return 5;
	}
	else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) {
		if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */
			return 0;
		return 6;
	}
	else {
349
		return 0;
350
	}
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
}

FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name)
{
	char c;
	for(c = *name; c; c = *(++name))
		if(c < 0x20 || c == 0x3d || c > 0x7d)
			return false;
	return true;
}

FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length)
{
	if(length == (unsigned)(-1)) {
		while(*value) {
			unsigned n = utf8len_(value);
			if(n == 0)
				return false;
			value += n;
		}
	}
	else {
		const FLAC__byte *end = value + length;
		while(value < end) {
			unsigned n = utf8len_(value);
			if(n == 0)
				return false;
			value += n;
		}
		if(value != end)
			return false;
	}
	return true;
}

FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length)
{
	const FLAC__byte *s, *end;

	for(s = entry, end = s + length; s < end && *s != '='; s++) {
		if(*s < 0x20 || *s > 0x7D)
			return false;
	}
	if(s == end)
		return false;

	s++; /* skip '=' */

	while(s < end) {
		unsigned n = utf8len_(s);
		if(n == 0)
			return false;
		s += n;
	}
	if(s != end)
		return false;

	return true;
}

Josh Coalson's avatar
Josh Coalson committed
411
/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation)
{
	unsigned i, j;

	if(check_cd_da_subset) {
		if(cue_sheet->lead_in < 2 * 44100) {
			if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds";
			return false;
		}
		if(cue_sheet->lead_in % 588 != 0) {
			if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples";
			return false;
		}
	}

	if(cue_sheet->num_tracks == 0) {
		if(violation) *violation = "cue sheet must have at least one track (the lead-out)";
		return false;
	}

	if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) {
		if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)";
		return false;
	}

	for(i = 0; i < cue_sheet->num_tracks; i++) {
		if(cue_sheet->tracks[i].number == 0) {
			if(violation) *violation = "cue sheet may not have a track number 0";
			return false;
		}

		if(check_cd_da_subset) {
			if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) {
				if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170";
				return false;
			}
		}

		if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) {
451
			if(violation) {
452 453 454 455
				if(i == cue_sheet->num_tracks-1) /* the lead-out track... */
					*violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples";
				else
					*violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples";
456
			}
457 458 459
			return false;
		}

460 461 462 463 464
		if(i < cue_sheet->num_tracks - 1) {
			if(cue_sheet->tracks[i].num_indices == 0) {
				if(violation) *violation = "cue sheet track must have at least one index point";
				return false;
			}
465

466 467 468 469
			if(cue_sheet->tracks[i].indices[0].number > 1) {
				if(violation) *violation = "cue sheet track's first index number must be 0 or 1";
				return false;
			}
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
		}

		for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) {
			if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) {
				if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples";
				return false;
			}

			if(j > 0) {
				if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) {
					if(violation) *violation = "cue sheet track index numbers must increase by 1";
					return false;
				}
			}
		}
	}

	return true;
}

Josh Coalson's avatar
Josh Coalson committed
490
/* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation)
{
	char *p;
	FLAC__byte *b;

	for(p = picture->mime_type; *p; p++) {
		if(*p < 0x20 || *p > 0x7e) {
			if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)";
			return false;
		}
	}

	for(b = picture->description; *b; ) {
		unsigned n = utf8len_(b);
		if(n == 0) {
			if(violation) *violation = "description string must be valid UTF-8";
			return false;
		}
		b += n;
	}

	return true;
}

515 516 517
/*
 * These routines are private to libFLAC
 */
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534
unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order)
{
	return
		FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(
			FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize),
			blocksize,
			predictor_order
		);
}

unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize)
{
	unsigned max_rice_partition_order = 0;
	while(!(blocksize & 1)) {
		max_rice_partition_order++;
		blocksize >>= 1;
	}
535
	return flac_min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order);
536 537 538 539 540 541 542 543 544
}

unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order)
{
	unsigned max_rice_partition_order = limit;

	while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order)
		max_rice_partition_order--;

Josh Coalson's avatar
Josh Coalson committed
545 546 547 548
	FLAC__ASSERT(
		(max_rice_partition_order == 0 && blocksize >= predictor_order) ||
		(max_rice_partition_order > 0 && blocksize >> max_rice_partition_order > predictor_order)
	);
549 550 551 552

	return max_rice_partition_order;
}

553
void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object)
554 555 556 557 558 559 560 561
{
	FLAC__ASSERT(0 != object);

	object->parameters = 0;
	object->raw_bits = 0;
	object->capacity_by_order = 0;
}

562
void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object)
563 564 565 566 567 568 569
{
	FLAC__ASSERT(0 != object);

	if(0 != object->parameters)
		free(object->parameters);
	if(0 != object->raw_bits)
		free(object->raw_bits);
570
	FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object);
571 572
}

573
FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order)
574 575 576 577 578 579
{
	FLAC__ASSERT(0 != object);

	FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits));

	if(object->capacity_by_order < max_partition_order) {
580
		if(0 == (object->parameters = safe_realloc_(object->parameters, sizeof(unsigned)*(1 << max_partition_order))))
581
			return false;
582
		if(0 == (object->raw_bits = safe_realloc_(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order))))
583
			return false;
584
		memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order));
585 586 587 588 589
		object->capacity_by_order = max_partition_order;
	}

	return true;
}