1 /*
2  * Copyright (c) 2009, The MilkyTracker Team.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * - Neither the name of the <ORGANIZATION> nor the names of its contributors
14  *   may be used to endorse or promote products derived from this software
15  *   without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /*
31  *  SampleLoaderIFF.cpp
32  *  MilkyPlay
33  *
34  *  Created by Peter Barth on 07.01.06.
35  *
36  */
37 
38 #include "SampleLoaderIFF.h"
39 #include "XMFile.h"
40 #include "XModule.h"
41 #include "LittleEndian.h"
42 
SampleLoaderIFF(const SYSCHAR * fileName,XModule & theModule)43 SampleLoaderIFF::SampleLoaderIFF(const SYSCHAR* fileName, XModule& theModule) :
44 	SampleLoaderAbstract(fileName, theModule)
45 {
46 }
47 
identifySample()48 bool SampleLoaderIFF::identifySample()
49 {
50 	return getNumChannels() != 0;
51 }
52 
getNumChannels()53 mp_sint32 SampleLoaderIFF::getNumChannels()
54 {
55 	mp_ubyte ID[4], buffer[4];
56 	mp_dword chunkLen;
57 
58 	XMFile f(theFileName);
59 
60 	f.read(ID, 4, 1);
61 	if (memcmp(ID, "FORM", 4) != 0)
62 		return 0;
63 
64 	f.seek(0);
65 
66 	bool hasFORM = false;
67 	bool hasVHDR = false;
68 	bool hasBODY = false;
69 
70 	while (!(hasFORM && hasVHDR && hasBODY))
71 	{
72 
73 		mp_uint32 bytesRead = f.read(ID, 4, 1);
74 		if (bytesRead != 4)
75 			break;
76 
77 		bytesRead = f.read(buffer, 4, 1);
78 		if (bytesRead != 4)
79 			break;
80 
81 		chunkLen = BigEndian::GET_DWORD(buffer);
82 
83 		switch (BigEndian::GET_DWORD(ID))
84 		{
85 			case 0x464F524D:	// 'FORM'
86 			{
87 				f.read(buffer, 4, 1);
88 				if (memcmp(buffer, "8SVX", 4) == 0)
89 					hasFORM = true;
90 				else if (memcmp(buffer, "16SV", 4) == 0)
91 					hasFORM = true;
92 
93 				break;
94 			}
95 
96 			/*case 0x4E414D45:	// 'NAME'
97 			{
98 				break;
99 			}*/
100 
101 			case 0x56484452:	// 'VHDR'
102 			{
103 				hasVHDR = true;
104 				mp_uint32 pos = f.pos();
105 				f.seek(pos + chunkLen);
106 				break;
107 			}
108 
109 			case 0x424F4459:	// 'BODY'
110 			{
111 				hasBODY = true;
112 				mp_uint32 pos = f.pos();
113 				f.seek(pos + chunkLen);
114 				break;
115 			}
116 
117 			default:
118 			{
119 				mp_uint32 pos = f.pos();
120 				f.seek(pos + chunkLen);
121 			}
122 		}
123 
124 		// odd chunks are padded with zero
125 		if (chunkLen&1)
126 			f.readByte();
127 	}
128 
129 	return (hasFORM && hasVHDR && hasBODY) ? 1 : 0;
130 }
131 
132 /* 8SVX Voice8Header Structure Definition */
133 struct SVX8_Vhdr
134 {
135 	mp_dword Oneshothi;						/* Num Samples in high octave 1-shot part */
136 	mp_dword Repeathi;						/* Num Samples in high octave repeat part */
137 	mp_dword Samplescycle;					/* Num Samples/cycle in high octave, else 0 */
138 	mp_uword Samplessec;					/* Samples Per Second - Sampling Rate */
139 	mp_ubyte Octaves;						/* Number of Octaves */
140 	mp_ubyte Compression;					/* Compression algorithm used - see below */
141 	mp_sint32 Volume;						/* Volume from 0 to Unity - see below */
142 };
143 
loadSample(mp_sint32 index,mp_sint32 channelIndex)144 mp_sint32 SampleLoaderIFF::loadSample(mp_sint32 index, mp_sint32 channelIndex)
145 {
146 	mp_ubyte ID[4], buffer[4];
147 	mp_dword chunkLen;
148 	SVX8_Vhdr vhdr;
149 
150 	XMFile f(theFileName);
151 
152 	f.read(ID, 4, 1);
153 	if (memcmp(ID, "FORM", 4) != 0)
154 		return MP_LOADER_FAILED;
155 
156 	f.seek(0);
157 
158 	bool hasFORM = false;
159 	bool hires = false;
160 	bool hasVHDR = false;
161 	bool hasBODY = false;
162 
163 	mp_ubyte* name = NULL;
164 	mp_ubyte* anno = NULL;
165 
166 	mp_sbyte* sampleData = NULL;
167 	mp_sint32 sampleDataLen = 0;
168 
169 	mp_dword iffSize = 0;
170 
171 	while (!(hasFORM && hasVHDR && hasBODY))
172 	{
173 
174 		mp_uint32 bytesRead = f.read(ID, 4, 1);
175 		if (bytesRead != 4)
176 			break;
177 
178 		bytesRead = f.read(buffer, 4, 1);
179 		if (bytesRead != 4)
180 			break;
181 
182 		chunkLen = BigEndian::GET_DWORD(buffer);
183 
184 		switch (BigEndian::GET_DWORD(ID))
185 		{
186 			case 0x464F524D:	// 'FORM'
187 			{
188 				f.read(buffer, 4, 1);
189 				if (memcmp(buffer, "8SVX", 4) == 0)
190 				{
191 					hasFORM = true;
192 					hires = false;
193 				}
194 				else if (memcmp(buffer, "16SV", 4) == 0)
195 				{
196 					hasFORM = true;
197 					hires = true;
198 				}
199 
200 				iffSize = chunkLen;
201 
202 				break;
203 			}
204 
205 			case 0x4E414D45:	// 'NAME'
206 			{
207 				if (name)
208 					delete[] name;
209 
210 				name = new mp_ubyte[chunkLen+1];
211 				if (name == NULL)
212 				{
213 					if (sampleData)
214 						delete[] sampleData;
215 					if (name)
216 						delete[] name;
217 					if (anno)
218 						delete[] anno;
219 					return MP_OUT_OF_MEMORY;
220 				}
221 
222 				f.read(name, 1, chunkLen);
223 				name[chunkLen] = '\0';
224 				break;
225 			}
226 
227 			case 0x414E4E4F :	// 'ANNO'
228 			{
229 				if (anno)
230 					delete[] anno;
231 
232 				anno = new mp_ubyte[chunkLen+1];
233 				if (anno == NULL)
234 				{
235 					if (sampleData)
236 						delete[] sampleData;
237 					if (name)
238 						delete[] name;
239 					if (anno)
240 						delete[] anno;
241 					return MP_OUT_OF_MEMORY;
242 				}
243 
244 				f.read(anno, 1, chunkLen);
245 				anno[chunkLen] = '\0';
246 				break;
247 			}
248 
249 			case 0x56484452:	// 'VHDR'
250 			{
251 				hasVHDR = true;
252 				mp_uint32 pos = f.pos();
253 
254 				if (chunkLen < 20)
255 				{
256 					if (sampleData)
257 						delete[] sampleData;
258 					if (name)
259 						delete[] name;
260 					if (anno)
261 						delete[] anno;
262 					return MP_LOADER_FAILED;
263 				}
264 
265 				f.read(buffer, 1, 4);
266 				vhdr.Oneshothi = BigEndian::GET_DWORD(buffer);		// sample len
267 				f.read(buffer, 1, 4);
268 				vhdr.Repeathi = BigEndian::GET_DWORD(buffer);		// loop start
269 				f.read(buffer, 1, 4);
270 				vhdr.Samplescycle = BigEndian::GET_DWORD(buffer);	// loop len
271 				f.read(buffer, 1, 2);
272 				vhdr.Samplessec = BigEndian::GET_WORD(buffer);		// c4 speed
273 				vhdr.Octaves = f.readByte();
274 				vhdr.Compression = f.readByte();
275 				f.read(buffer, 1, 4);
276 				vhdr.Volume = BigEndian::GET_DWORD(buffer);			// volume
277 
278 				f.seek(pos + chunkLen);
279 				break;
280 			}
281 
282 			case 0x424F4459:	// 'BODY'
283 			{
284 				if (!hasVHDR)
285 					goto bail;
286 
287 				mp_uint32 pos = f.pos();
288 
289 				switch (vhdr.Compression)
290 				{
291 					// uncompressed IFF
292 					case 0:
293 					{
294 						hasBODY = true;
295 
296 						// check for some nasty invalid TFMX IFF
297 						// (contains zero length body but file contains actually some data)
298 						if (chunkLen == 0)
299 						{
300 							sampleDataLen = iffSize - f.pos();
301 						}
302 						else
303 						{
304 							sampleDataLen = chunkLen;
305 						}
306 
307 						if (vhdr.Oneshothi < chunkLen)
308 							vhdr.Oneshothi = chunkLen;
309 
310 						if (sampleDataLen > (signed)vhdr.Oneshothi)
311 							sampleDataLen = vhdr.Oneshothi;
312 						else if (sampleDataLen < (signed)vhdr.Oneshothi)
313 							sampleDataLen = vhdr.Oneshothi;
314 
315 						sampleData = new mp_sbyte[sampleDataLen];
316 						if (sampleData == NULL)
317 						{
318 							if (sampleData)
319 								delete[] sampleData;
320 							if (name)
321 								delete[] name;
322 							if (anno)
323 								delete[] anno;
324 							return MP_OUT_OF_MEMORY;
325 						}
326 						memset(sampleData, 0, sampleDataLen);
327 
328 						f.read(sampleData, 1, (signed)chunkLen > sampleDataLen ? sampleDataLen : chunkLen);
329 						break;
330 					}
331 
332 					case 1:
333 					case 2:
334 					{
335 						if (hires)
336 							goto bail;
337 
338 						hasBODY = true;
339 
340 		            	mp_sbyte *Body,*src,*dst;
341 						mp_ubyte d;
342 						mp_sbyte x;
343 						mp_sint32 i,n,lim;
344 						mp_sbyte fibtab[16] = { -34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21 };
345 						mp_sbyte exptab[16] = {-128,-64,-32,-16,-8,-4,-2,-1,0,1,2,4,8,16,32,64};
346 
347 						Body = new mp_sbyte[chunkLen];
348 						if (!Body)
349 						{
350 							if (sampleData)
351 								delete[] sampleData;
352 							if (name)
353 								delete[] name;
354 							if (anno)
355 								delete[] anno;
356 							return MP_OUT_OF_MEMORY;
357 						}
358 
359 						f.read(Body, 1, chunkLen);
360 
361 						sampleDataLen = chunkLen<<1;
362 						sampleData = new mp_sbyte[sampleDataLen];
363 
364 						if (!sampleData)
365 						{
366 							if (sampleData)
367 								delete[] sampleData;
368 							if (name)
369 								delete[] name;
370 							if (anno)
371 								delete[] anno;
372 							if (Body)
373 								delete[] Body;
374 							return MP_OUT_OF_MEMORY;
375 						}
376 
377 						/* Fibonacci Delta Decompression */
378 						mp_sbyte* CodeToDelta = NULL;
379 						if (vhdr.Compression == 1)
380 							 CodeToDelta = fibtab;
381 						/* Exponential Delta Decompression */
382 						else
383 							 CodeToDelta = exptab;
384 
385 						src = Body+2;
386 						dst = sampleData;
387 						n = chunkLen-2;
388 						x = Body[1];
389 						lim = n << 1;
390 						for(i = 0; i < lim; ++i)
391 						{
392 							d = src[i >> 1];
393 							if(i & 1) d &= 0xF;
394 							else d >>= 4;
395 							x += CodeToDelta[d];
396 							dst[i] = x;
397 						}
398 						delete[] Body;
399 
400 						if ((signed)vhdr.Oneshothi < sampleDataLen)
401 							vhdr.Oneshothi = sampleDataLen;
402 
403 						break;
404 					}
405 
406 					default:
407 						hasBODY = false;
408 						goto bail;
409 
410 
411 				}
412 
413 				f.seek(pos + chunkLen);
414 				break;
415 			}
416 
417 			default:
418 			{
419 bail:
420 				mp_uint32 pos = f.pos();
421 				f.seek(pos + chunkLen);
422 			}
423 		}
424 
425 		// odd chunks are padded with zero
426 		if (chunkLen&1)
427 			f.readByte();
428 
429 	}
430 
431 	if (hasFORM && hasVHDR && hasBODY)
432 	{
433 		TXMSample* smp = &theModule.smp[index];
434 
435 		if (smp->sample)
436 		{
437 			theModule.freeSampleMem((mp_ubyte*)smp->sample);
438 			smp->sample = NULL;
439 		}
440 
441 		smp->samplen = hires ? sampleDataLen >> 1 : sampleDataLen;
442 
443 		smp->sample = (mp_sbyte*)theModule.allocSampleMem(hires ? (smp->samplen<<1) : smp->samplen);
444 		if (smp->sample == NULL)
445 		{
446 			if (sampleData)
447 				delete[] sampleData;
448 			if (name)
449 				delete[] name;
450 			if (anno)
451 				delete[] anno;
452 			return MP_OUT_OF_MEMORY;
453 		}
454 
455 		memcpy(smp->sample, sampleData, sampleDataLen);
456 
457 		if (hires)
458 		{
459 			// huuuu? 16 bit IFF samples are little endian? how stupid is that?
460 			mp_uword* ptr = (mp_uword*)smp->sample;
461 			for (mp_uint32 i = 0; i < smp->samplen; i++)
462 			{
463 				*ptr = LittleEndian::GET_WORD(ptr);
464 				ptr++;
465 			}
466 		}
467 
468 		delete[] sampleData;
469 
470 		smp->loopstart = hires ? vhdr.Repeathi >> 1 : vhdr.Repeathi;
471 		smp->looplen = hires ? vhdr.Samplescycle >> 1 : vhdr.Samplescycle;
472 		mp_sint32 vol = (vhdr.Volume*255)>>16;
473 		smp->vol = (mp_ubyte)(vol > 255 ? 255 : vol);
474 		smp->pan = 0x80;
475 		smp->flags = 3;
476 
477 		memset(smp->name, 0, sizeof(smp->name));
478 
479 		if (name || anno)
480 		{
481 			const char* buff = name ? (const char*)name : (const char*)anno;
482 
483 			nameToSample(buff, smp);
484 
485 			if (name)
486 				delete[] name;
487 			if (anno)
488 				delete[] anno;
489 		}
490 		else
491 		{
492 			nameToSample(preferredDefaultName, smp);
493 		}
494 
495 		smp->type = 0;
496 
497 		if (smp->looplen)
498 			smp->type |= 1;
499 		if (hires)
500 			smp->type |= 16;
501 
502 		XModule::convertc4spd(vhdr.Samplessec, &smp->finetune, &smp->relnote);
503 
504 		if (smp->samplen > vhdr.Oneshothi)
505 			smp->samplen = vhdr.Oneshothi;
506 
507 		return MP_OK;
508 	}
509 
510 	delete[] name;
511 
512 	delete[] sampleData;
513 
514 	return MP_LOADER_FAILED;
515 }
516 
swapW(mp_uword x)517 static inline mp_uword swapW(mp_uword x)
518 {
519 	return (x>>8)+((x&255)<<8);
520 }
521 
swapDW(mp_dword x)522 static inline mp_dword swapDW(mp_dword x)
523 {
524 	return (mp_dword)swapW(x>>16) | (mp_dword)swapW(x&0xFFFF) << 16;
525 }
526 
saveSample(const SYSCHAR * fileName,mp_sint32 index)527 mp_sint32 SampleLoaderIFF::saveSample(const SYSCHAR* fileName, mp_sint32 index)
528 {
529 	TXMSample* smp = &theModule.smp[index];
530 
531 	XMFile f(fileName, true);
532 
533 	f.write("FORM", 1, 4);
534 
535 	bool hires = (smp->type & 16) != 0;
536 
537 	mp_dword chunkLen = 70 + (hires ? smp-> samplen << 1 : smp-> samplen);
538 	f.writeDword(swapDW(chunkLen));
539 
540 	f.write(hires ? "16SV" : "8SVX", 1, 4);
541 
542 	f.write("NAME", 1, 4);
543 	f.writeDword(swapDW(22));
544 	f.write(smp->name, 1, 22);
545 
546 	SVX8_Vhdr vhdr;
547 
548 	vhdr.Oneshothi = hires ? smp->samplen << 1 : smp->samplen;
549 	vhdr.Repeathi = (smp->type & 3) ? (hires ? smp->loopstart << 1 : smp->loopstart) : 0;
550 	vhdr.Samplescycle = (smp->type & 3) ? (hires ? smp->looplen << 1 : smp->looplen) : 0;
551 	vhdr.Samplessec = XModule::getc4spd(smp->relnote,smp->finetune);
552 	vhdr.Octaves = 1;
553 	vhdr.Compression = 0;
554 	vhdr.Volume = (smp->vol*65536)/255;
555 
556 	f.write("VHDR", 1, 4);
557 	f.writeDword(swapDW(0x14));
558 	f.writeDword(swapDW(vhdr.Oneshothi));
559 	f.writeDword(swapDW(vhdr.Repeathi));
560 	f.writeDword(swapDW(vhdr.Samplescycle));
561 	f.writeWord(swapW(vhdr.Samplessec));
562 	f.writeByte(vhdr.Octaves);
563 	f.writeByte(vhdr.Compression);
564 	f.writeDword(swapDW(vhdr.Volume));
565 
566 	f.write("BODY", 1, 4);
567 	f.writeDword(swapDW(hires ? smp->samplen << 16 : smp->samplen));
568 
569 	if (smp->type & 16)
570 	{
571 		for (mp_uint32 i = 0; i < smp->samplen; i++)
572 			f.writeWord(smp->getSampleValue(i));
573 	}
574 	else
575 	{
576 		for (mp_uint32 i = 0; i < smp->samplen; i++)
577 			f.writeByte(smp->getSampleValue(i));
578 	}
579 
580 	return MP_OK;
581 }
582