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