1b815c7f3Sopenharmony_ci/*
2b815c7f3Sopenharmony_ci** Copyright (C) 2010-2014 Erik de Castro Lopo <erikd@mega-nerd.com>
3b815c7f3Sopenharmony_ci**
4b815c7f3Sopenharmony_ci** All rights reserved.
5b815c7f3Sopenharmony_ci**
6b815c7f3Sopenharmony_ci** Redistribution and use in source and binary forms, with or without
7b815c7f3Sopenharmony_ci** modification, are permitted provided that the following conditions are
8b815c7f3Sopenharmony_ci** met:
9b815c7f3Sopenharmony_ci**
10b815c7f3Sopenharmony_ci**     * Redistributions of source code must retain the above copyright
11b815c7f3Sopenharmony_ci**       notice, this list of conditions and the following disclaimer.
12b815c7f3Sopenharmony_ci**     * Redistributions in binary form must reproduce the above copyright
13b815c7f3Sopenharmony_ci**       notice, this list of conditions and the following disclaimer in
14b815c7f3Sopenharmony_ci**       the documentation and/or other materials provided with the
15b815c7f3Sopenharmony_ci**       distribution.
16b815c7f3Sopenharmony_ci**     * Neither the author nor the names of any contributors may be used
17b815c7f3Sopenharmony_ci**       to endorse or promote products derived from this software without
18b815c7f3Sopenharmony_ci**       specific prior written permission.
19b815c7f3Sopenharmony_ci**
20b815c7f3Sopenharmony_ci** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21b815c7f3Sopenharmony_ci** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22b815c7f3Sopenharmony_ci** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23b815c7f3Sopenharmony_ci** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24b815c7f3Sopenharmony_ci** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25b815c7f3Sopenharmony_ci** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26b815c7f3Sopenharmony_ci** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27b815c7f3Sopenharmony_ci** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28b815c7f3Sopenharmony_ci** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29b815c7f3Sopenharmony_ci** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30b815c7f3Sopenharmony_ci** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31b815c7f3Sopenharmony_ci*/
32b815c7f3Sopenharmony_ci
33b815c7f3Sopenharmony_ci#include	"sfconfig.h"
34b815c7f3Sopenharmony_ci
35b815c7f3Sopenharmony_ci#include	<stdio.h>
36b815c7f3Sopenharmony_ci#include	<stdlib.h>
37b815c7f3Sopenharmony_ci#include	<string.h>
38b815c7f3Sopenharmony_ci#include	<inttypes.h>
39b815c7f3Sopenharmony_ci#include	<ctype.h>
40b815c7f3Sopenharmony_ci#include	<math.h>
41b815c7f3Sopenharmony_ci#include	<errno.h>
42b815c7f3Sopenharmony_ci#if HAVE_UNISTD_H
43b815c7f3Sopenharmony_ci#include	<unistd.h>
44b815c7f3Sopenharmony_ci#else
45b815c7f3Sopenharmony_ci#include	"sf_unistd.h"
46b815c7f3Sopenharmony_ci#endif
47b815c7f3Sopenharmony_ci#include	<fcntl.h>
48b815c7f3Sopenharmony_ci#include	<sys/stat.h>
49b815c7f3Sopenharmony_ci#include	<sys/types.h>
50b815c7f3Sopenharmony_ci
51b815c7f3Sopenharmony_ci#include	<sndfile.h>
52b815c7f3Sopenharmony_ci
53b815c7f3Sopenharmony_ci#include	"common.h"
54b815c7f3Sopenharmony_ci
55b815c7f3Sopenharmony_ci#define	BUFFER_LEN		(1 << 16)
56b815c7f3Sopenharmony_ci
57b815c7f3Sopenharmony_ci#define	NOT(x)			(! (x))
58b815c7f3Sopenharmony_ci
59b815c7f3Sopenharmony_ci#ifndef _WIN32
60b815c7f3Sopenharmony_citypedef off_t sf_off_t ;
61b815c7f3Sopenharmony_ci#else
62b815c7f3Sopenharmony_citypedef long long sf_off_t ;
63b815c7f3Sopenharmony_ci#endif
64b815c7f3Sopenharmony_ci
65b815c7f3Sopenharmony_ci
66b815c7f3Sopenharmony_cistatic void usage_exit (const char *progname) ;
67b815c7f3Sopenharmony_cistatic void salvage_file (const char * broken_wav, const char * fixed_w64) ;
68b815c7f3Sopenharmony_ci
69b815c7f3Sopenharmony_ciint
70b815c7f3Sopenharmony_cimain (int argc, char *argv [])
71b815c7f3Sopenharmony_ci{
72b815c7f3Sopenharmony_ci	if (argc != 3)
73b815c7f3Sopenharmony_ci		usage_exit (program_name (argv [0])) ;
74b815c7f3Sopenharmony_ci
75b815c7f3Sopenharmony_ci	salvage_file (argv [1], argv [2]) ;
76b815c7f3Sopenharmony_ci
77b815c7f3Sopenharmony_ci	return 0 ;
78b815c7f3Sopenharmony_ci} /* main */
79b815c7f3Sopenharmony_ci
80b815c7f3Sopenharmony_ci/*==============================================================================
81b815c7f3Sopenharmony_ci*/
82b815c7f3Sopenharmony_ci
83b815c7f3Sopenharmony_cistatic void lseek_or_die (int fd, sf_off_t offset, int whence) ;
84b815c7f3Sopenharmony_cistatic sf_off_t get_file_length (int fd, const char * name) ;
85b815c7f3Sopenharmony_cistatic sf_count_t find_data_offset (int fd, int format) ;
86b815c7f3Sopenharmony_cistatic void copy_data (int fd, SNDFILE * sndfile, int readsize) ;
87b815c7f3Sopenharmony_ci
88b815c7f3Sopenharmony_ci
89b815c7f3Sopenharmony_cistatic void
90b815c7f3Sopenharmony_ciusage_exit (const char *progname)
91b815c7f3Sopenharmony_ci{	printf ("Usage :\n\n  %s <broken wav file> <fixed w64 file>\n\n", progname) ;
92b815c7f3Sopenharmony_ci	puts ("Salvages the audio data from WAV files which are more than 4G in length.\n") ;
93b815c7f3Sopenharmony_ci	printf ("Using %s.\n\n", sf_version_string ()) ;
94b815c7f3Sopenharmony_ci	exit (1) ;
95b815c7f3Sopenharmony_ci} /* usage_exit */
96b815c7f3Sopenharmony_ci
97b815c7f3Sopenharmony_cistatic void
98b815c7f3Sopenharmony_cisalvage_file (const char * broken_wav, const char * fixed_w64)
99b815c7f3Sopenharmony_ci{	SNDFILE * sndfile ;
100b815c7f3Sopenharmony_ci	SF_INFO sfinfo ;
101b815c7f3Sopenharmony_ci	sf_count_t broken_len, data_offset ;
102b815c7f3Sopenharmony_ci	int fd, read_size ;
103b815c7f3Sopenharmony_ci
104b815c7f3Sopenharmony_ci	if (strcmp (broken_wav, fixed_w64) == 0)
105b815c7f3Sopenharmony_ci	{	printf ("Error : Input and output files must be different.\n\n") ;
106b815c7f3Sopenharmony_ci		exit (1) ;
107b815c7f3Sopenharmony_ci		} ;
108b815c7f3Sopenharmony_ci
109b815c7f3Sopenharmony_ci	if ((fd = open (broken_wav, O_RDONLY)) < 0)
110b815c7f3Sopenharmony_ci	{	printf ("Error : Not able to open file '%s' : %s\n", broken_wav, strerror (errno)) ;
111b815c7f3Sopenharmony_ci		exit (1) ;
112b815c7f3Sopenharmony_ci		} ;
113b815c7f3Sopenharmony_ci
114b815c7f3Sopenharmony_ci	broken_len = get_file_length (fd, broken_wav) ;
115b815c7f3Sopenharmony_ci	if (broken_len <= 0xffffffff)
116b815c7f3Sopenharmony_ci		printf ("File is not greater than 4Gig but salvaging anyway.\n") ;
117b815c7f3Sopenharmony_ci
118b815c7f3Sopenharmony_ci	/* Grab the format info from the broken file. */
119b815c7f3Sopenharmony_ci	memset (&sfinfo, 0, sizeof (sfinfo)) ;
120b815c7f3Sopenharmony_ci	if ((sndfile = sf_open (broken_wav, SFM_READ, &sfinfo)) == NULL)
121b815c7f3Sopenharmony_ci	{	printf ("sf_open ('%s') failed : %s\n", broken_wav, sf_strerror (NULL)) ;
122b815c7f3Sopenharmony_ci		exit (1) ;
123b815c7f3Sopenharmony_ci		} ;
124b815c7f3Sopenharmony_ci	sf_close (sndfile) ;
125b815c7f3Sopenharmony_ci
126b815c7f3Sopenharmony_ci	data_offset = find_data_offset (fd, sfinfo.format & SF_FORMAT_TYPEMASK) ;
127b815c7f3Sopenharmony_ci
128b815c7f3Sopenharmony_ci	printf ("Offset to audio data : %" PRId64 "\n", data_offset) ;
129b815c7f3Sopenharmony_ci
130b815c7f3Sopenharmony_ci	switch (sfinfo.format & SF_FORMAT_TYPEMASK)
131b815c7f3Sopenharmony_ci	{	case SF_FORMAT_WAV :
132b815c7f3Sopenharmony_ci		case SF_FORMAT_WAVEX :
133b815c7f3Sopenharmony_ci			sfinfo.format = SF_FORMAT_W64 | (sfinfo.format & SF_FORMAT_SUBMASK) ;
134b815c7f3Sopenharmony_ci			break ;
135b815c7f3Sopenharmony_ci
136b815c7f3Sopenharmony_ci		default :
137b815c7f3Sopenharmony_ci			printf ("Don't currently support this file type.\n") ;
138b815c7f3Sopenharmony_ci			exit (1) ;
139b815c7f3Sopenharmony_ci		} ;
140b815c7f3Sopenharmony_ci
141b815c7f3Sopenharmony_ci	switch (sfinfo.format & SF_FORMAT_SUBMASK)
142b815c7f3Sopenharmony_ci	{	case SF_FORMAT_PCM_U8 :
143b815c7f3Sopenharmony_ci		case SF_FORMAT_PCM_S8 :
144b815c7f3Sopenharmony_ci				read_size = 1 ;
145b815c7f3Sopenharmony_ci				break ;
146b815c7f3Sopenharmony_ci
147b815c7f3Sopenharmony_ci		case SF_FORMAT_PCM_16 :
148b815c7f3Sopenharmony_ci				read_size = 2 ;
149b815c7f3Sopenharmony_ci				break ;
150b815c7f3Sopenharmony_ci
151b815c7f3Sopenharmony_ci		case SF_FORMAT_PCM_24 :
152b815c7f3Sopenharmony_ci				read_size = 3 ;
153b815c7f3Sopenharmony_ci				break ;
154b815c7f3Sopenharmony_ci
155b815c7f3Sopenharmony_ci		case SF_FORMAT_PCM_32 :
156b815c7f3Sopenharmony_ci		case SF_FORMAT_FLOAT :
157b815c7f3Sopenharmony_ci				read_size = 4 ;
158b815c7f3Sopenharmony_ci				break ;
159b815c7f3Sopenharmony_ci
160b815c7f3Sopenharmony_ci		case SF_FORMAT_DOUBLE :
161b815c7f3Sopenharmony_ci				read_size = 8 ;
162b815c7f3Sopenharmony_ci				break ;
163b815c7f3Sopenharmony_ci
164b815c7f3Sopenharmony_ci		default :
165b815c7f3Sopenharmony_ci			printf ("Sorry, don't currently support this file encoding type.\n") ;
166b815c7f3Sopenharmony_ci			exit (1) ;
167b815c7f3Sopenharmony_ci		} ;
168b815c7f3Sopenharmony_ci
169b815c7f3Sopenharmony_ci	read_size *= sfinfo.channels ;
170b815c7f3Sopenharmony_ci
171b815c7f3Sopenharmony_ci	if ((sndfile = sf_open (fixed_w64, SFM_WRITE, &sfinfo)) == NULL)
172b815c7f3Sopenharmony_ci	{	printf ("sf_open ('%s') failed : %s\n", fixed_w64, sf_strerror (NULL)) ;
173b815c7f3Sopenharmony_ci		exit (1) ;
174b815c7f3Sopenharmony_ci		} ;
175b815c7f3Sopenharmony_ci
176b815c7f3Sopenharmony_ci	lseek_or_die (fd, data_offset, SEEK_SET) ;
177b815c7f3Sopenharmony_ci
178b815c7f3Sopenharmony_ci	copy_data (fd, sndfile, read_size) ;
179b815c7f3Sopenharmony_ci
180b815c7f3Sopenharmony_ci	sf_close (sndfile) ;
181b815c7f3Sopenharmony_ci
182b815c7f3Sopenharmony_ci	puts ("Done!") ;
183b815c7f3Sopenharmony_ci} /* salvage_file */
184b815c7f3Sopenharmony_ci
185b815c7f3Sopenharmony_ci/*------------------------------------------------------------------------------
186b815c7f3Sopenharmony_ci*/
187b815c7f3Sopenharmony_ci
188b815c7f3Sopenharmony_cistatic void
189b815c7f3Sopenharmony_cilseek_or_die (int fd, sf_off_t offset, int whence)
190b815c7f3Sopenharmony_ci{
191b815c7f3Sopenharmony_ci#ifndef _WIN32
192b815c7f3Sopenharmony_ci	if (lseek (fd, offset, whence) < 0)
193b815c7f3Sopenharmony_ci#else
194b815c7f3Sopenharmony_ci	if (_lseeki64 (fd, offset, whence) < 0)
195b815c7f3Sopenharmony_ci#endif
196b815c7f3Sopenharmony_ci	{	printf ("lseek failed : %s\n", strerror (errno)) ;
197b815c7f3Sopenharmony_ci		exit (1) ;
198b815c7f3Sopenharmony_ci		} ;
199b815c7f3Sopenharmony_ci
200b815c7f3Sopenharmony_ci	return ;
201b815c7f3Sopenharmony_ci} /* lseek_or_die */
202b815c7f3Sopenharmony_ci
203b815c7f3Sopenharmony_ci
204b815c7f3Sopenharmony_cistatic sf_off_t
205b815c7f3Sopenharmony_ciget_file_length (int fd, const char * name)
206b815c7f3Sopenharmony_ci{
207b815c7f3Sopenharmony_ci#ifndef _WIN32
208b815c7f3Sopenharmony_ci	struct stat sbuf ;
209b815c7f3Sopenharmony_ci#else
210b815c7f3Sopenharmony_ci	struct _stat64 sbuf ;
211b815c7f3Sopenharmony_ci#endif
212b815c7f3Sopenharmony_ci
213b815c7f3Sopenharmony_ci	if (sizeof (sbuf.st_size) != 8)
214b815c7f3Sopenharmony_ci	{	puts ("Error : sizeof (sbuf.st_size) != 8. Was program compiled with\n"
215b815c7f3Sopenharmony_ci				"        64 bit file offsets?\n") ;
216b815c7f3Sopenharmony_ci		exit (1) ;
217b815c7f3Sopenharmony_ci		} ;
218b815c7f3Sopenharmony_ci
219b815c7f3Sopenharmony_ci#ifndef _WIN32
220b815c7f3Sopenharmony_ci	if (fstat (fd, &sbuf) != 0)
221b815c7f3Sopenharmony_ci#else
222b815c7f3Sopenharmony_ci	if (_fstat64 (fd, &sbuf) != 0)
223b815c7f3Sopenharmony_ci#endif
224b815c7f3Sopenharmony_ci	{	printf ("Error : fstat ('%s') failed : %s\n", name, strerror (errno)) ;
225b815c7f3Sopenharmony_ci		exit (1) ;
226b815c7f3Sopenharmony_ci		} ;
227b815c7f3Sopenharmony_ci
228b815c7f3Sopenharmony_ci	return sbuf.st_size ;
229b815c7f3Sopenharmony_ci} /* get_file_length */
230b815c7f3Sopenharmony_ci
231b815c7f3Sopenharmony_cistatic sf_count_t
232b815c7f3Sopenharmony_cifind_data_offset (int fd, int format)
233b815c7f3Sopenharmony_ci{	char buffer [8192], *cptr ;
234b815c7f3Sopenharmony_ci	const char * target = "XXXX" ;
235b815c7f3Sopenharmony_ci	sf_count_t offset = -1, extra ;
236b815c7f3Sopenharmony_ci	int rlen, slen ;
237b815c7f3Sopenharmony_ci
238b815c7f3Sopenharmony_ci	switch (format)
239b815c7f3Sopenharmony_ci	{	case SF_FORMAT_WAV :
240b815c7f3Sopenharmony_ci		case SF_FORMAT_WAVEX :
241b815c7f3Sopenharmony_ci			target = "data" ;
242b815c7f3Sopenharmony_ci			extra = 8 ;
243b815c7f3Sopenharmony_ci			break ;
244b815c7f3Sopenharmony_ci
245b815c7f3Sopenharmony_ci		case SF_FORMAT_AIFF :
246b815c7f3Sopenharmony_ci			target = "SSND" ;
247b815c7f3Sopenharmony_ci			extra = 16 ;
248b815c7f3Sopenharmony_ci			break ;
249b815c7f3Sopenharmony_ci
250b815c7f3Sopenharmony_ci		default :
251b815c7f3Sopenharmony_ci			puts ("Error : Sorry, don't handle this input file format.\n") ;
252b815c7f3Sopenharmony_ci			exit (1) ;
253b815c7f3Sopenharmony_ci		} ;
254b815c7f3Sopenharmony_ci
255b815c7f3Sopenharmony_ci	slen = (int) strlen (target) ;
256b815c7f3Sopenharmony_ci
257b815c7f3Sopenharmony_ci	lseek_or_die (fd, 0, SEEK_SET) ;
258b815c7f3Sopenharmony_ci
259b815c7f3Sopenharmony_ci	printf ("Searching for '%s' maker.\n", target) ;
260b815c7f3Sopenharmony_ci
261b815c7f3Sopenharmony_ci	if ((rlen = read (fd, buffer, sizeof (buffer))) < 0)
262b815c7f3Sopenharmony_ci	{	printf ("Error : failed read : %s\n", strerror (errno)) ;
263b815c7f3Sopenharmony_ci		exit (1) ;
264b815c7f3Sopenharmony_ci		} ;
265b815c7f3Sopenharmony_ci
266b815c7f3Sopenharmony_ci	cptr = memchr (buffer, target [0], rlen - slen) ;
267b815c7f3Sopenharmony_ci	if (cptr && memcmp (cptr, target, slen) == 0)
268b815c7f3Sopenharmony_ci		offset = cptr - buffer ;
269b815c7f3Sopenharmony_ci	else
270b815c7f3Sopenharmony_ci	{	printf ("Error : Could not find data offset.\n") ;
271b815c7f3Sopenharmony_ci		exit (1) ;
272b815c7f3Sopenharmony_ci		} ;
273b815c7f3Sopenharmony_ci
274b815c7f3Sopenharmony_ci	return offset + extra ;
275b815c7f3Sopenharmony_ci} /* find_data_offset */
276b815c7f3Sopenharmony_ci
277b815c7f3Sopenharmony_cistatic void
278b815c7f3Sopenharmony_cicopy_data (int fd, SNDFILE * sndfile, int readsize)
279b815c7f3Sopenharmony_ci{	static char * buffer ;
280b815c7f3Sopenharmony_ci	sf_count_t readlen, count ;
281b815c7f3Sopenharmony_ci	int bufferlen, done = 0 ;
282b815c7f3Sopenharmony_ci
283b815c7f3Sopenharmony_ci	bufferlen = readsize * 1024 ;
284b815c7f3Sopenharmony_ci	buffer = malloc (bufferlen) ;
285b815c7f3Sopenharmony_ci
286b815c7f3Sopenharmony_ci	while (NOT (done) && (readlen = read (fd, buffer, bufferlen)) >= 0)
287b815c7f3Sopenharmony_ci	{	if (readlen < bufferlen)
288b815c7f3Sopenharmony_ci		{	readlen -= readlen % readsize ;
289b815c7f3Sopenharmony_ci			done = 1 ;
290b815c7f3Sopenharmony_ci			} ;
291b815c7f3Sopenharmony_ci
292b815c7f3Sopenharmony_ci		if ((count = sf_write_raw (sndfile, buffer, readlen)) != readlen)
293b815c7f3Sopenharmony_ci		{	printf ("Error : sf_write_raw returned %" PRId64 " : %s\n", count, sf_strerror (sndfile)) ;
294b815c7f3Sopenharmony_ci			return ;
295b815c7f3Sopenharmony_ci			} ;
296b815c7f3Sopenharmony_ci		} ;
297b815c7f3Sopenharmony_ci
298b815c7f3Sopenharmony_ci	free (buffer) ;
299b815c7f3Sopenharmony_ci
300b815c7f3Sopenharmony_ci	return ;
301b815c7f3Sopenharmony_ci} /* copy_data */
302b815c7f3Sopenharmony_ci
303