1 /*
2   This file is Copyright © 1994-1995 Olivier Montanuy,
3                Copyright © 1999-2005 André Majorel,
4                Copyright © 2006-2019 contributors to the DeuTex project.
5 
6   DeuTex incorporates code derived from DEU 5.21 that was put in the
7   public domain in 1994 by Raphaël Quinet and Brendon Wyber.
8 
9   SPDX-License-Identifier: GPL-2.0-or-later
10 */
11 
12 #include "deutex.h"
13 #include <errno.h>
14 #include "tools.h"
15 #include "endianm.h"
16 #include "mkwad.h"
17 #include "sound.h"
18 #include "text.h"
19 
20 static struct RIFFHEAD {
21     char riff[4];
22     int32_t length;
23     char wave[4];
24 } headr;
25 static struct CHUNK {
26     char name[4];
27     int32_t size;
28 } headc;
29 static struct WAVEFMT {         /*format */
30     char fmt[4];                /* "fmt " */
31     int32_t fmtsize;            /*0x10 */
32     int16_t tag;                /*format tag. 1=PCM */
33     int16_t channel;            /*1 */
34     int32_t smplrate;
35     int32_t bytescnd;           /*average bytes per second */
36     int16_t align;              /*block alignment, in bytes */
37     int16_t nbits;              /*specific to PCM format */
38 } headf;
39 static struct WAVEDATA {        /*data */
40     char data[4];               /* "data" */
41     int32_t datasize;
42 } headw;
43 
SNDsaveWave(char * file,char * buffer,int32_t size,int32_t speed)44 static void SNDsaveWave(char *file, char *buffer, int32_t size,
45                         int32_t speed)
46 {
47     FILE *fp;
48     int32_t wsize, sz = 0;
49     fp = fopen(file, FOPEN_WB);
50     if (fp == NULL) {
51         ProgError("RW10", "%s: %s", fname(file), strerror(errno));
52     }
53     /*header */
54     strncpy(headr.riff, "RIFF", 4);
55     write_i32_le(&headr.length,
56                  4 + sizeof(struct WAVEFMT) + sizeof(struct WAVEDATA) +
57                  size);
58     strncpy(headr.wave, "WAVE", 4);
59     fwrite(&headr, sizeof(struct RIFFHEAD), 1, fp);
60     strncpy(headf.fmt, "fmt ", 4);
61     write_i32_le(&headf.fmtsize, sizeof(struct WAVEFMT) - 8);
62     write_i16_le(&headf.tag, 1);
63     write_i16_le(&headf.channel, 1);
64     write_i32_le(&headf.smplrate, speed);
65     write_i32_le(&headf.bytescnd, speed);
66     write_i16_le(&headf.align, 1);
67     write_i16_le(&headf.nbits, 8);
68     fwrite(&headf, sizeof(struct WAVEFMT), 1, fp);
69     strncpy(headw.data, "data", 4);
70     write_i32_le(&headw.datasize, size);
71     fwrite(&headw, sizeof(struct WAVEDATA), 1, fp);
72     for (wsize = 0; wsize < size; wsize += sz) {
73         sz = (size - wsize > MEMORYCACHE) ? MEMORYCACHE : (size - wsize);
74         if (fwrite((buffer + (wsize)), (size_t) sz, 1, fp) != 1)
75             ProgError("RW11", "%s: write error", fname(file));
76     }
77     fclose(fp);
78 }
79 
SNDloadWaveFile(char * file,int32_t * psize,int32_t * pspeed)80 char *SNDloadWaveFile(char *file, int32_t * psize, int32_t * pspeed)
81 {
82     FILE *fp;
83     int32_t wsize, sz = 0, smplrate, datasize;
84     int32_t chunk;
85     char *data;
86     fp = fopen(file, FOPEN_RB);
87     if (fp == NULL)
88         ProgError("RR10", "%s: %s", fname(file), strerror(errno));
89     /*read RIFF HEADER */
90     if (fread(&headr, sizeof(struct RIFFHEAD), 1, fp) != 1)
91         ProgError("RR11", "%s: read error in header", fname(file));
92     /*check RIFF header */
93     if (strncmp(headr.riff, "RIFF", 4) != 0)
94         ProgError("RR12", "%s: bad RIFF magic in header (%s)", fname(file),
95                   short_dump(headr.riff, 4));
96     if (strncmp(headr.wave, "WAVE", 4) != 0)
97         ProgError("RR13", "%s: bad WAVE magic in header (%s)", fname(file),
98                   short_dump(headr.wave, 4));
99     chunk = sizeof(struct RIFFHEAD);
100     for (sz = 0;; sz++) {
101         if (sz > 256)
102             ProgError("RR14", "%s: no fmt", fname(file));
103         fseek(fp, chunk, SEEK_SET);
104         if (fread(&headc, sizeof(struct CHUNK), 1, fp) != 1)
105             ProgError("RR15", "%s: no fmt", fname(file));
106         if (strncmp(headc.name, "fmt ", 4) == 0)
107             break;
108         /* There used to be a bug here; sizeof (struct CHUNK) had
109            its bytes swapped too. Reading .wav files on big endian
110            machines must have been broken. -- AYM 1999-07-04 */
111         chunk += sizeof(struct CHUNK) + peek_i32_le(&headc.size);
112     }
113     fseek(fp, chunk, SEEK_SET);
114     if (fread(&headf, sizeof(struct WAVEFMT), 1, fp) == 1) {
115         Detail("RR01", "%s is a WAVE file", fname(file));
116     } else {
117         ProgError("RR02", "%s is not a WAVE file", fname(file));
118     }
119     if (peek_i16_le(&headf.tag) != 1)
120         ProgError("RR16", "%s: not raw data", fname(file));
121     if (peek_i16_le(&headf.channel) != 1)
122         ProgError("RR17", "%s: not one channel", fname(file));
123     smplrate = peek_i32_le(&headf.smplrate);
124 
125     for (sz = 0;; sz++) {
126         if (sz > 256)
127             ProgError("RR18", "%s: no data", fname(file));
128         fseek(fp, chunk, SEEK_SET);
129         if (fread(&headc, sizeof(struct CHUNK), 1, fp) != 1)
130             ProgError("RR19", "%s: no data", fname(file));
131         if (strncmp(headc.name, "data", 4) == 0)
132             break;
133         /* Same endianness bug as above. */
134         chunk += sizeof(struct CHUNK) + peek_i32_le(&headc.size);
135     }
136     fseek(fp, chunk, SEEK_SET);
137     if (fread(&headw, sizeof(struct WAVEDATA), 1, fp) != 1)
138         ProgError("RR20", "%s: no data", fname(file));
139     datasize = peek_i32_le(&headw.datasize);
140     /*check WAVE header */
141     if (datasize > 0x1000000L)  /* AYM 2000-04-22 */
142         ProgError("RR21", "%s: sample too long (%ld)", fname(file),
143                   (long) datasize);
144     /*read data */
145     data = (char *) Malloc(datasize);
146     for (wsize = 0; wsize < datasize; wsize += sz) {
147         sz = (datasize - wsize >
148               MEMORYCACHE) ? MEMORYCACHE : (datasize - wsize);
149         if (fread((data + (wsize)), (size_t) sz, 1, fp) != 1)
150             ProgError("RR22", "%s: read error in data", fname(file));
151     }
152     fclose(fp);
153     *psize = datasize;
154     *pspeed = smplrate & 0xFFFFL;
155     return data;
156 }
157 
SNDsaveSound(char * file,char * buffer,int32_t size,SNDTYPE format,const char * name)158 void SNDsaveSound(char *file, char *buffer, int32_t size, SNDTYPE format,
159                   const char *name)
160 {
161     char *data;
162     int32_t datasize;
163     int32_t phys_size;
164     int16_t type;
165     int16_t headsize;
166     uint16_t rate;
167 
168     headsize = sizeof(int16_t) + sizeof(int16_t) + sizeof(int32_t);
169     if (size < headsize) {
170         Warning("SD10", "Sound %s: lump has no header, skipping",
171                 lump_name(name));
172         return;
173     }
174     type = peek_i16_le(buffer);
175     rate = peek_u16_le(buffer + 2);
176     datasize = peek_i32_le(buffer + 4);
177     data = buffer + headsize;
178     if (type != 3)
179         Warning("SD11", "Sound %s: weird type %d, extracting anyway",
180                 lump_name(name), type);
181 
182     phys_size = size - headsize;
183     if (datasize > phys_size) {
184         Warning("SD12",
185                 "Sound %s: declared length %lu > lump size %lu, truncating",
186                 lump_name(name), (unsigned long) datasize,
187                 (unsigned long) phys_size);
188         datasize = phys_size;
189     } else if (datasize < phys_size) {
190         datasize = phys_size;
191     }
192 
193     switch (format) {
194     case SNDWAV:
195         SNDsaveWave(file, data, datasize, rate);
196         break;
197     default:
198         Bug("SD14", "sndsv %d", (int) format);
199     }
200 }
201 
SNDcopyInWAD(struct WADINFO * info,char * file,SNDTYPE format)202 int32_t SNDcopyInWAD(struct WADINFO *info, char *file, SNDTYPE format)
203 {
204     int32_t size = 0;
205     int32_t datasize;
206     int32_t rate;
207     char *data = NULL;
208     long wadrate;
209 
210     switch (format) {
211     case SNDWAV:
212         data = SNDloadWaveFile(file, &datasize, &rate);
213         break;
214     default:
215         Bug("SC10", "sndcw %d", (int) format);
216     }
217     wadrate = rate;
218     switch (rate_policy) {
219     case RP_REJECT:
220         if (rate != 11025)
221             ProgError("SC11", "%s: sample rate not 11025 Hz", fname(file));
222         break;
223 
224     case RP_FORCE:
225         if (rate > 11025) {
226             Warning("SC12", "%s: resampling down from %ld Hz to 11025 Hz",
227                     fname(file), rate);
228             wadrate = 11025;
229             {
230                 double ratio = 11025.0 / rate;
231                 long s;
232 
233                 datasize = (int32_t) (ratio * datasize) + 1;
234                 for (s = 0; s < datasize; s++)
235                     data[s] = data[(size_t) (s / ratio + 0.5)];
236             }
237             data = (char *) Realloc(data, datasize);
238         } else if (rate < 11025) {
239             Warning("SC13", "%s: resampling up from %ld Hz to 11025 Hz",
240                     fname(file), rate);
241             wadrate = 11025;
242             {
243                 double ratio = 11025.0 / rate;
244                 long s;
245 
246                 datasize = (int32_t) (ratio * datasize) + 1;
247                 data = (char *) Realloc(data, datasize);
248                 for (s = datasize - 1; s >= 0; s--)
249                     data[s] = data[(size_t) (s / ratio + 0.5)];
250             }
251         }
252         break;
253 
254     case RP_WARN:
255         if (rate != 11025)
256             Warning("SC14",
257                     "%s: sample rate != 11025 Hz, won't work on Doom < 1.4",
258                     fname(file));
259         break;
260 
261     case RP_ACCEPT:
262         break;
263 
264     default:
265         Bug("SC15", "SNDcopyInWAD: rate_policy %d", (int) rate_policy);
266     }
267 
268     if (datasize > 0) {
269         size = WADRwriteShort(info, 3);
270         size += WADRwriteShort(info, wadrate);
271         size += WADRwriteLong(info, datasize);
272         size += WADRwriteBytes(info, data, datasize);
273     }
274     free(data);
275     return size;
276 }
277 
SNDsavePCSound(const char * lumpname,const char * file,const char * buffer,int32_t size)278 void SNDsavePCSound(const char *lumpname, const char *file,
279                     const char *buffer, int32_t size)
280 {
281     FILE *fp;
282     const char *data;
283     int16_t datasize, type, headsize;
284     int16_t i;
285     headsize = sizeof(int16_t) + sizeof(int16_t);
286     if (size < headsize)
287         ProgError("KW10", "FIXME: wrong size", fname(file));
288     type = peek_i16_le(buffer);
289     datasize = peek_i16_le(buffer + 2);
290     data = buffer + (sizeof(int16_t) + sizeof(int16_t));
291     if (type != 0)
292         Bug("KW11", "FIXME: not a PC sound", fname(file));
293     if (size < datasize + headsize)
294         ProgError("KW12", "FIXME: wrong size", fname(file));
295     fp = fopen(file, FOPEN_WT); /*text file */
296     if (fp == NULL)
297         ProgError("KW13", "%s: %s", fname(file), strerror(errno));
298     for (i = 0; i < datasize; i++) {
299         fprintf(fp, "%d\n", ((int) data[i]) & 0xFF);
300     }
301     if (fclose(fp) != 0)
302         ProgError("KW14", "%s: %s", fname(file), strerror(errno));
303 }
304 
SNDcopyPCSoundInWAD(struct WADINFO * info,char * file)305 int32_t SNDcopyPCSoundInWAD(struct WADINFO *info, char *file)
306 {
307     struct TXTFILE *Txt;
308     int32_t size, datasizepos;
309     int16_t datasize, s;
310     char c;
311     Txt = TXTopenR(file, 1);
312     if (Txt == NULL)
313         ProgError("KR10", "%s: %s", fname(file), strerror(errno));
314     size = WADRwriteShort(info, 0);
315     datasizepos = WADRposition(info);
316     size += WADRwriteShort(info, -1);
317     datasize = 0;
318     while (TXTskipComment(Txt)) {
319         s = TXTreadShort(Txt);
320         if ((s < 0) || (s > 255))
321             ProgError("KR11", "%s: number out of bounds [0-255]",
322                       fname(file));
323         datasize += sizeof(char);
324         c = (char) (s & 0xFF);
325         size += WADRwriteBytes(info, &c, sizeof(c));
326     }
327     WADRsetShort(info, datasizepos, datasize);
328     TXTcloseR(Txt);
329     return size;
330 }
331