1/* libFLAC - Free Lossless Audio Codec
2 * Copyright (C) 2004,2005,2006,2007  Josh Coalson
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of the Xiph.org Foundation nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#if HAVE_CONFIG_H
33#  include <config.h>
34#endif
35
36#include <stdlib.h> /* for malloc() */
37#include <string.h> /* for memcmp(), memcpy() */
38#include "FLAC/assert.h"
39#include "share/alloc.h"
40#include "private/ogg_helper.h"
41#include "protected/stream_encoder.h"
42
43
44static FLAC__bool full_read_(FLAC__StreamEncoder *encoder, FLAC__byte *buffer, size_t bytes, FLAC__StreamEncoderReadCallback read_callback, void *client_data)
45{
46	while(bytes > 0) {
47		size_t bytes_read = bytes;
48		switch(read_callback(encoder, buffer, &bytes_read, client_data)) {
49			case FLAC__STREAM_ENCODER_READ_STATUS_CONTINUE:
50				bytes -= bytes_read;
51				buffer += bytes_read;
52				break;
53			case FLAC__STREAM_ENCODER_READ_STATUS_END_OF_STREAM:
54				if(bytes_read == 0) {
55					encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
56					return false;
57				}
58				bytes -= bytes_read;
59				buffer += bytes_read;
60				break;
61			case FLAC__STREAM_ENCODER_READ_STATUS_ABORT:
62				encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
63				return false;
64			case FLAC__STREAM_ENCODER_READ_STATUS_UNSUPPORTED:
65				return false;
66			default:
67				/* double protection: */
68				FLAC__ASSERT(0);
69				encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
70				return false;
71		}
72	}
73
74	return true;
75}
76
77void simple_ogg_page__init(ogg_page *page)
78{
79	page->header = 0;
80	page->header_len = 0;
81	page->body = 0;
82	page->body_len = 0;
83}
84
85void simple_ogg_page__clear(ogg_page *page)
86{
87	if(page->header)
88		free(page->header);
89	if(page->body)
90		free(page->body);
91	simple_ogg_page__init(page);
92}
93
94FLAC__bool simple_ogg_page__get_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderReadCallback read_callback, void *client_data)
95{
96	static const unsigned OGG_HEADER_FIXED_PORTION_LEN = 27;
97	static const unsigned OGG_MAX_HEADER_LEN = 27/*OGG_HEADER_FIXED_PORTION_LEN*/ + 255;
98	FLAC__byte crc[4];
99	FLAC__StreamEncoderSeekStatus seek_status;
100
101	FLAC__ASSERT(page->header == 0);
102	FLAC__ASSERT(page->header_len == 0);
103	FLAC__ASSERT(page->body == 0);
104	FLAC__ASSERT(page->body_len == 0);
105
106	/* move the stream pointer to the supposed beginning of the page */
107	if(0 == seek_callback)
108		return false;
109	if((seek_status = seek_callback((FLAC__StreamEncoder*)encoder, position, client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) {
110		if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR)
111			encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
112		return false;
113	}
114
115	/* allocate space for the page header */
116	if(0 == (page->header = (unsigned char *)safe_malloc_(OGG_MAX_HEADER_LEN))) {
117		encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
118		return false;
119	}
120
121	/* read in the fixed part of the page header (up to but not including
122	 * the segment table */
123	if(!full_read_(encoder, page->header, OGG_HEADER_FIXED_PORTION_LEN, read_callback, client_data))
124		return false;
125
126	page->header_len = OGG_HEADER_FIXED_PORTION_LEN + page->header[26];
127
128	/* check to see if it's a correct, "simple" page (one packet only) */
129	if(
130		memcmp(page->header, "OggS", 4) ||               /* doesn't start with OggS */
131		(page->header[5] & 0x01) ||                      /* continued packet */
132		memcmp(page->header+6, "\0\0\0\0\0\0\0\0", 8) || /* granulepos is non-zero */
133		page->header[26] == 0                            /* packet is 0-size */
134	) {
135		encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
136		return false;
137	}
138
139	/* read in the segment table */
140	if(!full_read_(encoder, page->header + OGG_HEADER_FIXED_PORTION_LEN, page->header[26], read_callback, client_data))
141		return false;
142
143	{
144		unsigned i;
145
146		/* check to see that it specifies a single packet */
147		for(i = 0; i < (unsigned)page->header[26] - 1; i++) {
148			if(page->header[i + OGG_HEADER_FIXED_PORTION_LEN] != 255) {
149				encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
150				return false;
151			}
152		}
153
154		page->body_len = 255 * i + page->header[i + OGG_HEADER_FIXED_PORTION_LEN];
155	}
156
157	/* allocate space for the page body */
158	if(0 == (page->body = (unsigned char *)safe_malloc_(page->body_len))) {
159		encoder->protected_->state = FLAC__STREAM_ENCODER_MEMORY_ALLOCATION_ERROR;
160		return false;
161	}
162
163	/* read in the page body */
164	if(!full_read_(encoder, page->body, page->body_len, read_callback, client_data))
165		return false;
166
167	/* check the CRC */
168	memcpy(crc, page->header+22, 4);
169	ogg_page_checksum_set(page);
170	if(memcmp(crc, page->header+22, 4)) {
171		encoder->protected_->state = FLAC__STREAM_ENCODER_OGG_ERROR;
172		return false;
173	}
174
175	return true;
176}
177
178FLAC__bool simple_ogg_page__set_at(FLAC__StreamEncoder *encoder, FLAC__uint64 position, ogg_page *page, FLAC__StreamEncoderSeekCallback seek_callback, FLAC__StreamEncoderWriteCallback write_callback, void *client_data)
179{
180	FLAC__StreamEncoderSeekStatus seek_status;
181
182	FLAC__ASSERT(page->header != 0);
183	FLAC__ASSERT(page->header_len != 0);
184	FLAC__ASSERT(page->body != 0);
185	FLAC__ASSERT(page->body_len != 0);
186
187	/* move the stream pointer to the supposed beginning of the page */
188	if(0 == seek_callback)
189		return false;
190	if((seek_status = seek_callback((FLAC__StreamEncoder*)encoder, position, client_data)) != FLAC__STREAM_ENCODER_SEEK_STATUS_OK) {
191		if(seek_status == FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR)
192			encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
193		return false;
194	}
195
196	ogg_page_checksum_set(page);
197
198	/* re-write the page */
199	if(write_callback((FLAC__StreamEncoder*)encoder, page->header, page->header_len, 0, 0, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
200		encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
201		return false;
202	}
203	if(write_callback((FLAC__StreamEncoder*)encoder, page->body, page->body_len, 0, 0, client_data) != FLAC__STREAM_ENCODER_WRITE_STATUS_OK) {
204		encoder->protected_->state = FLAC__STREAM_ENCODER_CLIENT_ERROR;
205		return false;
206	}
207
208	return true;
209}
210