1 ////////////////////////////////////////////////////////////////////////////
2 //                           **** WAVPACK ****                            //
3 //                  Hybrid Lossless Wavefile Compressor                   //
4 //              Copyright (c) 1998 - 2013 Conifer Software.               //
5 //                          All Rights Reserved.                          //
6 //      Distributed under the BSD Software License (see license.txt)      //
7 ////////////////////////////////////////////////////////////////////////////
8 
9 // unpack3_open.c
10 
11 // This module provides an extension to the open_utils.c module for handling
12 // WavPack files prior to version 4.0, not including "raw" files. As these
13 // modes are all obsolete and are no longer written, this code will not be
14 // fully documented other than the global functions. However, full documentation
15 // is provided in the version 3.97 source code. Note that this module only
16 // provides the functionality of opening the files and obtaining information
17 // from them; the actual audio decoding is located in the unpack3.c module.
18 
19 #ifdef ENABLE_LEGACY
20 
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "wavpack_local.h"
25 #include "unpack3.h"
26 
27 #define ATTEMPT_ERROR_MUTING
28 
29 // This provides an extension to the WavpackOpenFileRead () function contained
30 // in the wputils.c module. It is assumed that an 'R' had been read as the
31 // first character of the file/stream (indicating a non-raw pre version 4.0
32 // WavPack file) and had been pushed back onto the stream (or simply seeked
33 // back to).
34 
open_file3(WavpackContext * wpc,char * error)35 WavpackContext *open_file3 (WavpackContext *wpc, char *error)
36 {
37     RiffChunkHeader RiffChunkHeader;
38     ChunkHeader ChunkHeader;
39     WavpackHeader3 wphdr;
40     WavpackStream3 *wps;
41     WaveHeader3 wavhdr;
42 
43     CLEAR (wavhdr);
44     wpc->stream3 = wps = (WavpackStream3 *) malloc (sizeof (WavpackStream3));
45     CLEAR (*wps);
46 
47     if (wpc->reader->read_bytes (wpc->wv_in, &RiffChunkHeader, sizeof (RiffChunkHeader)) !=
48         sizeof (RiffChunkHeader)) {
49             if (error) strcpy (error, "not a valid WavPack file!");
50             return WavpackCloseFile (wpc);
51     }
52 
53     if (!strncmp (RiffChunkHeader.ckID, "RIFF", 4) && !strncmp (RiffChunkHeader.formType, "WAVE", 4)) {
54 
55         if (wpc->open_flags & OPEN_WRAPPER) {
56             wpc->wrapper_data = (unsigned char *)malloc (wpc->wrapper_bytes = sizeof (RiffChunkHeader));
57             memcpy (wpc->wrapper_data, &RiffChunkHeader, sizeof (RiffChunkHeader));
58         }
59 
60     // If the first chunk is a wave RIFF header, then read the various chunks
61     // until we get to the "data" chunk (and WavPack header should follow). If
62     // the first chunk is not a RIFF, then we assume a "raw" WavPack file and
63     // the WavPack header must be first.
64 
65         while (1) {
66 
67             if (wpc->reader->read_bytes (wpc->wv_in, &ChunkHeader, sizeof (ChunkHeader)) !=
68                 sizeof (ChunkHeader)) {
69                     if (error) strcpy (error, "not a valid WavPack file!");
70                     return WavpackCloseFile (wpc);
71             }
72             else {
73                 if (wpc->open_flags & OPEN_WRAPPER) {
74                     wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + sizeof (ChunkHeader));
75                     memcpy (wpc->wrapper_data + wpc->wrapper_bytes, &ChunkHeader, sizeof (ChunkHeader));
76                     wpc->wrapper_bytes += sizeof (ChunkHeader);
77                 }
78 
79                 WavpackLittleEndianToNative (&ChunkHeader, ChunkHeaderFormat);
80 
81                 if (!strncmp (ChunkHeader.ckID, "fmt ", 4)) {
82 
83                     if (ChunkHeader.ckSize < sizeof (wavhdr) ||
84                         wpc->reader->read_bytes (wpc->wv_in, &wavhdr, sizeof (wavhdr)) != sizeof (wavhdr)) {
85                             if (error) strcpy (error, "not a valid WavPack file!");
86                             return WavpackCloseFile (wpc);
87                     }
88                     else if (wpc->open_flags & OPEN_WRAPPER) {
89                         wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + sizeof (wavhdr));
90                         memcpy (wpc->wrapper_data + wpc->wrapper_bytes, &wavhdr, sizeof (wavhdr));
91                         wpc->wrapper_bytes += sizeof (wavhdr);
92                     }
93 
94                     WavpackLittleEndianToNative (&wavhdr, WaveHeader3Format);
95 
96                     if (ChunkHeader.ckSize > sizeof (wavhdr)) {
97                         uint32_t bytes_to_skip = (ChunkHeader.ckSize + 1 - sizeof (wavhdr)) & ~1L;
98 
99                         if (bytes_to_skip > 1024 * 1024) {
100                             if (error) strcpy (error, "not a valid WavPack file!");
101                             return WavpackCloseFile (wpc);
102                         }
103 
104                         if (wpc->open_flags & OPEN_WRAPPER) {
105                             wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + bytes_to_skip);
106                             wpc->reader->read_bytes (wpc->wv_in, wpc->wrapper_data + wpc->wrapper_bytes, bytes_to_skip);
107                             wpc->wrapper_bytes += bytes_to_skip;
108                         }
109                         else {
110                             unsigned char *temp = (unsigned char *)malloc (bytes_to_skip);
111                             wpc->reader->read_bytes (wpc->wv_in, temp, bytes_to_skip);
112                             free (temp);
113                         }
114                     }
115                 }
116                 else if (!strncmp (ChunkHeader.ckID, "data", 4))
117                     break;
118                 else if ((ChunkHeader.ckSize + 1) & ~1L) {
119                     uint32_t bytes_to_skip = (ChunkHeader.ckSize + 1) & ~1L;
120 
121                     if (bytes_to_skip > 1024 * 1024) {
122                         if (error) strcpy (error, "not a valid WavPack file!");
123                         return WavpackCloseFile (wpc);
124                     }
125 
126                     if (wpc->open_flags & OPEN_WRAPPER) {
127                         wpc->wrapper_data = (unsigned char *)realloc (wpc->wrapper_data, wpc->wrapper_bytes + bytes_to_skip);
128                         wpc->reader->read_bytes (wpc->wv_in, wpc->wrapper_data + wpc->wrapper_bytes, bytes_to_skip);
129                         wpc->wrapper_bytes += bytes_to_skip;
130                     }
131                     else {
132                         unsigned char *temp = (unsigned char *)malloc (bytes_to_skip);
133                         wpc->reader->read_bytes (wpc->wv_in, temp, bytes_to_skip);
134                         free (temp);
135                     }
136                 }
137             }
138         }
139     }
140     else {
141         if (error) strcpy (error, "not a valid WavPack file!");
142         return WavpackCloseFile (wpc);
143     }
144 
145     if (wavhdr.FormatTag != 1 || !wavhdr.NumChannels || wavhdr.NumChannels > 2 ||
146         !wavhdr.SampleRate || wavhdr.BitsPerSample < 16 || wavhdr.BitsPerSample > 24 ||
147         wavhdr.BlockAlign / wavhdr.NumChannels > 3 || wavhdr.BlockAlign % wavhdr.NumChannels ||
148         wavhdr.BlockAlign / wavhdr.NumChannels < (wavhdr.BitsPerSample + 7) / 8) {
149             if (error) strcpy (error, "not a valid WavPack file!");
150             return WavpackCloseFile (wpc);
151     }
152 
153     wpc->total_samples = ChunkHeader.ckSize / wavhdr.NumChannels /
154         ((wavhdr.BitsPerSample > 16) ? 3 : 2);
155 
156     if (wpc->reader->read_bytes (wpc->wv_in, &wphdr, 10) != 10) {
157         if (error) strcpy (error, "not a valid WavPack file!");
158         return WavpackCloseFile (wpc);
159     }
160 
161     if (((char *) &wphdr) [8] == 2 && (wpc->reader->read_bytes (wpc->wv_in, ((char *) &wphdr) + 10, 2) != 2)) {
162         if (error) strcpy (error, "not a valid WavPack file!");
163         return WavpackCloseFile (wpc);
164     }
165     else if (((char *) &wphdr) [8] == 3 && (wpc->reader->read_bytes (wpc->wv_in, ((char *) &wphdr) + 10,
166         sizeof (wphdr) - 10) != sizeof (wphdr) - 10)) {
167             if (error) strcpy (error, "not a valid WavPack file!");
168             return WavpackCloseFile (wpc);
169     }
170 
171     WavpackLittleEndianToNative (&wphdr, WavpackHeader3Format);
172 
173     // make sure this is a version we know about
174 
175     if (strncmp (wphdr.ckID, "wvpk", 4) || wphdr.version < 1 || wphdr.version > 3) {
176         if (error) strcpy (error, "not a valid WavPack file!");
177         return WavpackCloseFile (wpc);
178     }
179 
180     // Because I ran out of flag bits in the WavPack header, an amazingly ugly
181     // kludge was forced upon me! This code takes care of preparing the flags
182     // field for internal use and checking for unknown formats we can't decode
183 
184     if (wphdr.version == 3) {
185 
186         if (wphdr.flags & EXTREME_DECORR) {
187 
188             if ((wphdr.flags & NOT_STORED_FLAGS) ||
189                 ((wphdr.bits) &&
190                 (((wphdr.flags & NEW_HIGH_FLAG) &&
191                 (wphdr.flags & (FAST_FLAG | HIGH_FLAG))) ||
192                 (wphdr.flags & CROSS_DECORR)))) {
193                     if (error) strcpy (error, "not a valid WavPack file!");
194                     return WavpackCloseFile (wpc);
195             }
196 
197             if (wphdr.flags & CANCEL_EXTREME)
198                 wphdr.flags &= ~(EXTREME_DECORR | CANCEL_EXTREME);
199         }
200         else
201             wphdr.flags &= ~CROSS_DECORR;
202     }
203 
204     // check to see if we should look for a "correction" file, and if so try
205     // to open it for reading, then set WVC_FLAG accordingly
206 
207     if (wpc->wvc_in && wphdr.version == 3 && wphdr.bits && (wphdr.flags & NEW_HIGH_FLAG)) {
208         wpc->file2len = wpc->reader->get_length (wpc->wvc_in);
209         wphdr.flags |= WVC_FLAG;
210         wpc->wvc_flag = TRUE;
211     }
212     else
213         wphdr.flags &= ~WVC_FLAG;
214 
215     // check WavPack version to handle special requirements of versions
216     // before 3.0 that had smaller headers
217 
218     if (wphdr.version < 3) {
219         wphdr.total_samples = (int32_t) wpc->total_samples;
220         wphdr.flags = wavhdr.NumChannels == 1 ? MONO_FLAG : 0;
221         wphdr.shift = 16 - wavhdr.BitsPerSample;
222 
223         if (wphdr.version == 1)
224             wphdr.bits = 0;
225     }
226 
227     wpc->config.sample_rate = wavhdr.SampleRate;
228     wpc->config.num_channels = wavhdr.NumChannels;
229     wpc->config.channel_mask = 5 - wavhdr.NumChannels;
230 
231     if (wphdr.flags & MONO_FLAG)
232         wpc->config.flags |= CONFIG_MONO_FLAG;
233 
234     if (wphdr.flags & EXTREME_DECORR)
235         wpc->config.flags |= CONFIG_HIGH_FLAG;
236 
237     if (wphdr.bits) {
238         if (wphdr.flags & NEW_HIGH_FLAG)
239             wpc->config.flags |= CONFIG_HYBRID_FLAG;
240         else
241             wpc->config.flags |= CONFIG_LOSSY_MODE;
242     }
243     else if (!(wphdr.flags & HIGH_FLAG))
244         wpc->config.flags |= CONFIG_FAST_FLAG;
245 
246     wpc->config.bytes_per_sample = (wphdr.flags & BYTES_3) ? 3 : 2;
247     wpc->config.bits_per_sample = wavhdr.BitsPerSample;
248 
249     memcpy (&wps->wphdr, &wphdr, sizeof (wphdr));
250     wps->wvbits.bufsiz = wps->wvcbits.bufsiz = 1024 * 1024;
251     return wpc;
252 }
253 
254 // return currently decoded sample index
255 
get_sample_index3(WavpackContext * wpc)256 uint32_t get_sample_index3 (WavpackContext *wpc)
257 {
258     WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3;
259 
260     return (wps) ? wps->sample_index : (uint32_t) -1;
261 }
262 
get_version3(WavpackContext * wpc)263 int get_version3 (WavpackContext *wpc)
264 {
265     WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3;
266 
267     return (wps) ? wps->wphdr.version : 0;
268 }
269 
free_stream3(WavpackContext * wpc)270 void free_stream3 (WavpackContext *wpc)
271 {
272     WavpackStream3 *wps = (WavpackStream3 *) wpc->stream3;
273 
274     if (wps) {
275 #ifndef NO_SEEKING
276         if (wps->unpack_data)
277             free (wps->unpack_data);
278 #endif
279         if ((wps->wphdr.flags & WVC_FLAG) && wps->wvcbits.buf)
280             free (wps->wvcbits.buf);
281 
282         if (wps->wvbits.buf)
283             free (wps->wvbits.buf);
284 
285         free (wps);
286     }
287 }
288 
289 #endif  // ENABLE_LEGACY
290