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  *  LoaderDIGI.cpp
32  *  MilkyPlay Module Loader: Digibooster 1.0 - 1.7
33  */
34 
35 #include "Loaders.h"
36 
identifyModule(const mp_ubyte * buffer)37 const char* LoaderDIGI::identifyModule(const mp_ubyte* buffer)
38 {
39 	if (strcmp((const char*)buffer, "DIGI Booster module") == 0)
40 	{
41 		return "DIGI";
42 	}
43 
44 	return NULL;
45 }
46 
convertEvent(mp_ubyte * dst,mp_ubyte * src)47 static void convertEvent(mp_ubyte* dst, mp_ubyte* src)
48 {
49 	mp_ubyte b1 = src[0];
50 	mp_ubyte b2 = src[1];
51 	mp_ubyte b3 = src[2];
52 	mp_ubyte b4 = src[3];
53 
54 	mp_sint32 note,ins,eff,notenum = 0;
55 	note = ((b1&0xf)<<8)+b2;
56 	ins = (b1&0xf0)+(b3>>4);
57 	eff = b3&0xf;
58 
59 	if (eff==0xE) {
60 		eff=(b4>>4)+0x30;
61 		b4&=0xf;
62 
63 		if (eff == 0x33)
64 		{
65 			eff = 0x4F;
66 			b4 = 1;
67 		}
68 		if (eff == 0x34)
69 		{
70 			eff = 0x51; // key off at tick 0
71 			b4 = 0;
72 		}
73 		if (eff == 0x35)
74 		{
75 			if (b4 == 0)
76 			{
77 				eff = 0x50; // AMS set channel mastervolume to zero
78 				b4 = 0;
79 			}
80 			else if (b4 == 1)
81 			{
82 				eff = 0x50; // AMS set channel mastervolume to 255
83 				b4 = 255;
84 			}
85 		}
86 		if (eff == 0x38)
87 		{
88 #ifdef VERBOSE
89 			printf("Unsupported Digibooster effect: extended offset\n");
90 #endif
91 			eff = b4 = 0;
92 		}
93 		if (eff == 0x39)
94 		{
95 #ifdef VERBOSE
96 			printf("Unsupported Digibooster effect: retrace\n");
97 #endif
98 			eff = b4 = 0;
99 		}
100 	}
101 
102 	if ((!eff)&&b4)
103 		eff=0x20;
104 
105 	if (eff==0x8)
106 		eff = b4 = 0;
107 
108 	// old style modules don't support last effect for:
109 	// - portamento up/down
110 	// - volume slide
111 	if (eff==0x1&&(!b4)) eff = 0;
112 	if (eff==0x2&&(!b4)) eff = 0;
113 	if (eff==0xA&&(!b4)) eff = 0;
114 
115 	if (eff==0x5&&(!b4)) eff = 0x3;
116 	if (eff==0x6&&(!b4)) eff = 0x4;
117 
118 	if (eff==0xC) {
119 		b4 = XModule::vol64to255(b4);
120 	}
121 
122 	if (note)
123 		notenum = XModule::amigaPeriodToNote(note);
124 
125 	dst[0] = notenum;
126 	dst[1] = ins;
127 	dst[2] = eff;
128 	dst[3] = b4;
129 }
130 
load(XMFileBase & f,XModule * module)131 mp_sint32 LoaderDIGI::load(XMFileBase& f, XModule* module)
132 {
133 	module->cleanUp();
134 
135 	// this will make code much easier to read
136 	TXMHeader*		header = &module->header;
137 	TXMInstrument*	instr  = module->instr;
138 	TXMSample*		smp	   = module->smp;
139 	TXMPattern*		phead  = module->phead;
140 
141 	// we're already out of memory here
142 	if (!phead || !instr || !smp)
143 		return MP_OUT_OF_MEMORY;
144 
145 	mp_sint32 i,j;
146 	mp_ubyte buffer[31*4];
147 
148 	// read header
149 	f.read(header->sig, 1, 16);
150 	// skip 4 bytes (ule\0)
151 	f.readDword();
152 	// skip version string
153 	f.readDword();
154 	// check version
155 	mp_ubyte ver = f.readByte();
156 	if (ver < 0x10 ||
157 		ver > 0x17)
158 		return MP_LOADER_FAILED;
159 	// read numchannels
160 	header->channum = f.readByte();
161 	// packenable
162 	mp_ubyte pack = f.readByte();
163 	f.read(buffer, 1, 19);
164 	// read numpatterns
165 	header->patnum = (mp_sword)f.readByte() + 1;
166 	// read songlength
167 	header->ordnum = (mp_sword)f.readByte() + 1;
168 	f.read(header->ord, 1, 128);
169 
170 	header->insnum = 31;
171 
172 	f.read(buffer, 4, 31);
173 
174 	mp_sint32 s = 0;
175 	for (i = 0; i < header->insnum; i++)
176 	{
177 		mp_sint32 slen = BigEndian::GET_DWORD(buffer+i*4);
178 		if (slen)
179 		{
180 			instr[i].samp = 1;
181 
182 			for (j = 0; j < 120; j++)
183 				instr[i].snum[j] = s;
184 
185 			smp[s].samplen = slen;
186 			s++;
187 		}
188 	}
189 	header->smpnum = s;
190 
191 	// loop starts
192 	f.read(buffer, 4, 31);
193 	for (i = 0; i < header->insnum; i++)
194 	{
195 		if (instr[i].samp)
196 		{
197 			s = instr[i].snum[0];
198 			smp[s].loopstart = BigEndian::GET_DWORD(buffer+i*4);
199 		}
200 	}
201 
202 	// loop lengths
203 	f.read(buffer, 4, 31);
204 	for (i = 0; i < header->insnum; i++)
205 	{
206 		if (instr[i].samp)
207 		{
208 			s = instr[i].snum[0];
209 			smp[s].looplen = BigEndian::GET_DWORD(buffer+i*4);
210 			if (smp[s].looplen)
211 				smp[s].type = 1;
212 		}
213 	}
214 
215 	// volumes & finetunes
216 	f.read(buffer, 1, 31);
217 	f.read(buffer+31, 1, 31);
218 	for (i = 0; i < header->insnum; i++)
219 	{
220 		if (instr[i].samp)
221 		{
222 			s = instr[i].snum[0];
223 			smp[s].vol = XModule::vol64to255(buffer[i]);
224 			smp[s].pan = 0x80;
225 			smp[s].flags = 1;
226 			smp[s].finetune = ((mp_sbyte)buffer[i+31])*16;
227 		}
228 	}
229 
230 	f.read(header->name, 1, 32);
231 
232 	for (i = 0; i < header->insnum; i++)
233 	{
234 		f.read(instr[i].name, 1, 30);
235 		if (instr[i].samp)
236 		{
237 			s = instr[i].snum[0];
238 			memcpy(smp[s].name, instr[i].name, 30);
239 		}
240 	}
241 
242 	// read patterns
243 	for (i = 0; i < header->patnum; i++)
244 	{
245 		phead[i].rows = 64;
246 		phead[i].effnum = 1;
247 		phead[i].channum = (mp_ubyte)header->channum;
248 
249 		phead[i].patternData = new mp_ubyte[phead[i].rows*header->channum*4];
250 
251 		// out of memory?
252 		if (phead[i].patternData == NULL)
253 		{
254 			return MP_OUT_OF_MEMORY;
255 		}
256 
257 		memset(phead[i].patternData,0,phead[i].rows*header->channum*4);
258 
259 		mp_sint32 pSize = 64*header->channum*4 + 64;
260 		if (pack)
261 		{
262 			f.read(buffer, 1, 2);
263 			pSize = BigEndian::GET_WORD(buffer);
264 			if (pSize && pSize < 64)
265 				return MP_LOADER_FAILED;
266 			// read packing mask
267 			f.read(buffer, 1, 64);
268 		}
269 		else
270 		{
271 			memset(buffer, 0xFF, 64);
272 		}
273 
274 		if (!pSize)
275 			continue;
276 
277 		mp_ubyte* pattern = new mp_ubyte[pSize-64];
278 
279 		if (pattern == NULL)
280 			return MP_OUT_OF_MEMORY;
281 
282 		f.read(pattern, 1, pSize-64);
283 
284 		mp_ubyte* patPtr = pattern;
285 
286 		mp_sint32 r,c,cnt=0;
287 
288 		if (pack)
289 		{
290 			for (r=0;r<64;r++) {
291 				for (c=0;c<header->channum;c++) {
292 					if (buffer[r] & (1 << (7-c)))
293 					{
294 						convertEvent(phead[i].patternData + cnt, patPtr);
295 
296 						patPtr+=4;
297 					}
298 					cnt+=4;
299 				}
300 			}
301 		}
302 		else
303 		{
304 			for (c=0;c<header->channum;c++) {
305 				for (r=0;r<64;r++) {
306 					cnt = r*header->channum*4+c*4;
307 
308 					convertEvent(phead[i].patternData + cnt, patPtr);
309 
310 					patPtr+=4;
311 				}
312 			}
313 		}
314 
315 
316 		delete[] pattern;
317 	}
318 
319 	// No for something really stupid: Pattern loop correction
320 	for (mp_sint32 p = 0; p < header->patnum; p++)
321 	{
322 		struct Position
323 		{
324 			mp_sint32 row, channel;
325 		};
326 
327 		mp_ubyte* pattern = phead[p].patternData;
328 
329 		Position loopStart = {-1, -1}, loopEnd = {-1, -1};
330 
331 		for (i = 0; i < 64; i++) {
332 			for (j = 0; j < header->channum; j++)
333 			{
334 				mp_ubyte* slot = pattern + i*header->channum*4 + j*4;
335 
336 				// Loop start
337 				if (slot[2] == 0x36 && !slot[3])
338 				{
339 					loopStart.row = i;
340 					loopStart.channel = j;
341 				}
342 				else if (slot[2] == 0x36 && slot[3])
343 				{
344 					loopEnd.row = i;
345 					loopEnd.channel = j;
346 				}
347 			}
348 
349 			if (loopEnd.row != -1 && loopEnd.channel != -1 &&
350 				loopStart.row != -1 && loopStart.channel != -1 &&
351 				loopStart.channel != loopEnd.channel)
352 			{
353 
354 				// sanity check
355 				if (loopStart.row == loopEnd.row)
356 				{
357 					mp_ubyte* slot = pattern + loopStart.row*header->channum*4 + loopStart.channel*4;
358 					slot[2] = slot[3] = 0;
359 					slot = pattern + loopEnd.row*header->channum*4 + loopEnd.channel*4;
360 					slot[2] = slot[3] = 0;
361 				}
362 				else
363 				{
364 
365 					for (j = 0; j < header->channum; j++)
366 					{
367 						mp_ubyte* slotStart = pattern + loopStart.row*header->channum*4 + j*4;
368 						mp_ubyte* slotEnd = pattern + loopEnd.row*header->channum*4 + j*4;
369 
370 						if (!slotStart[2] && !slotEnd[2] &&
371 							!slotStart[3] && !slotEnd[3])
372 						{
373 							mp_ubyte* slot = pattern + loopStart.row*header->channum*4 + loopStart.channel*4;
374 							slotStart[2] = slot[2];
375 							slotStart[3] = slot[3];
376 							slot[2] = slot[3] = 0;
377 							slot = pattern + loopEnd.row*header->channum*4 + loopEnd.channel*4;
378 							slotEnd[2] = slot[2];
379 							slotEnd[3] = slot[3];
380 							slot[2] = slot[3] = 0;
381 							break;
382 						}
383 					}
384 
385 				}
386 
387 				loopStart.row = loopStart.channel = loopEnd.row = loopEnd.channel = -1;
388 
389 			}
390 		}
391 
392 	}
393 
394 	mp_sint32 result = module->loadModuleSamples(f);
395 	if (result != MP_OK)
396 		return result;
397 
398 	header->speed = 125;
399 	header->tempo = 6;
400 	header->mainvol = 255;
401 
402 	strcpy(header->tracker,"Digibooster");
403 
404 	module->setDefaultPanning();
405 
406 	module->postProcessSamples();
407 
408 	return MP_OK;
409 }
410