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  *  LoaderMTM.cpp
32  *  MilkyPlay Module Loader: Multitracker
33  *
34  *  Warning: This is an one-by-one conversion of an assembler version ;)
35  *
36  */
37 #include "Loaders.h"
38 
identifyModule(const mp_ubyte * buffer)39 const char* LoaderMTM::identifyModule(const mp_ubyte* buffer)
40 {
41 	// check for .MTM module
42 	if (!memcmp(buffer,"MTM\x10",4))
43 	{
44 		return "MTM";
45 	}
46 
47 	return NULL;
48 }
49 
load(XMFileBase & f,XModule * module)50 mp_sint32 LoaderMTM::load(XMFileBase& f, XModule* module)
51 {
52 	module->cleanUp();
53 
54 	// this will make code much easier to read
55 	TXMHeader*		header = &module->header;
56 	TXMInstrument*	instr  = module->instr;
57 	TXMSample*		smp	   = module->smp;
58 	TXMPattern*		phead  = module->phead;
59 
60 	// we're already out of memory here
61 	if (!phead || !instr || !smp)
62 		return MP_OUT_OF_MEMORY;
63 
64 	f.read(header->sig, 1, 3);	// read signature
65 
66 	if (f.readByte() != 0x10)
67 		return MP_LOADER_FAILED;
68 
69 	f.read(header->name, 1, 20);
70 
71 	mp_uword numTracks = f.readWord();
72 
73 	header->flags = XModule::MODULE_PTNEWINSTRUMENT;
74 
75 	header->mainvol = 255;
76 	header->tempo = 6;
77 	header->speed = 125;
78 	header->patnum = f.readByte() + 1;
79 	header->ordnum = f.readByte() + 1;
80 
81 	mp_uword locf = f.readWord();
82 
83 	header->insnum = f.readByte();
84 	header->smpnum = header->insnum;
85 
86 	f.readByte();
87 
88 	mp_sint32 numRows = f.readByte();
89 	mp_sint32 trackSize = numRows*3;
90 
91 	header->channum = f.readByte();
92 
93 	mp_ubyte panPositions[32];
94 	f.read(panPositions, 1, 32);
95 
96 	mp_sint32 i,s = 0;
97 	for (i = 0; i < header->insnum; i++)
98 	{
99 		f.read(instr[i].name,1,22);
100 
101 		mp_uint32 size = f.readDword();
102 		mp_uint32 loopstart = f.readDword();
103 		mp_uint32 loopend = f.readDword();
104 
105 		mp_sbyte finetune = f.readByte()*16;
106 
107 		mp_ubyte volume = XModule::vol64to255(f.readByte());
108 
109 		/*mp_ubyte flags = */f.readByte();
110 
111 		if (size)
112 		{
113 			instr[i].samp = 1;
114 
115 			memcpy(smp[s].name,instr[i].name,22);
116 
117 			smp[s].flags = 1;
118 			smp[s].samplen = size;
119 			smp[s].loopstart = loopstart;
120 			smp[s].looplen = loopend - loopstart;
121 			smp[s].vol = volume;
122 			smp[s].finetune = finetune;
123 
124 			for (mp_sint32 j = 0; j < 120; j++)
125 				instr[i].snum[j] = s;
126 
127 			if ((smp[s].loopstart+smp[s].looplen)>smp[s].samplen)
128 				smp[s].looplen-=(smp[s].loopstart+smp[s].looplen)-smp[s].samplen;
129 
130 			if (smp[s].loopstart<=2) smp[s].loopstart=0;
131 			if (smp[s].looplen<=2)
132 				smp[s].looplen=0;
133 			else smp[s].type=1;
134 
135 			s++;
136 		}
137 
138 	}
139 
140 	header->smpnum = s;
141 
142 	mp_ubyte orders[128];
143 	f.read(orders, 1, 128);
144 
145 	for (i = 0; i < header->ordnum; i++)
146 		header->ord[i] = orders[i];
147 
148 	mp_ubyte* tracks = new mp_ubyte[trackSize*numTracks];
149 
150 	if (tracks == NULL)
151 		return MP_OUT_OF_MEMORY;
152 
153 	f.read(tracks, trackSize, numTracks);
154 
155 	mp_uword* trackSeq = new mp_uword[header->patnum * 32];
156 
157 	if (trackSeq == NULL)
158 	{
159 		delete[] tracks;
160 		return MP_OUT_OF_MEMORY;
161 	}
162 
163 	f.readWords(trackSeq, header->patnum * 32);
164 
165 	mp_uword* pTrackSeq = trackSeq;
166 
167 	for (i = 0; i < header->patnum;i++)
168 	{
169 		phead[i].rows = numRows;
170 		phead[i].effnum = 1;
171 		phead[i].channum = (mp_ubyte)header->channum;
172 
173 		phead[i].patternData = new mp_ubyte[phead[i].rows*header->channum*4];
174 
175 		// out of memory?
176 		if (phead[i].patternData == NULL)
177 		{
178 			delete[] tracks;
179 			delete[] trackSeq;
180 			return MP_OUT_OF_MEMORY;
181 		}
182 
183 		memset(phead[i].patternData,0,phead[i].rows*header->channum*4);
184 
185 		mp_sint32 c;
186 		for (c=0;c<32;c++)
187 		{
188 			if (*pTrackSeq &&
189 				(*pTrackSeq-1) < numTracks &&
190 			    c < header->channum)
191 			{
192 
193 				mp_ubyte *track = (*pTrackSeq-1)*trackSize + tracks;
194 
195 #ifdef VERBOSE
196 				if ((*pTrackSeq-1) >= numTracks)
197 					printf("piiiiep");
198 #endif
199 
200 				for (mp_sint32 row = 0; row < numRows; row++)
201 				{
202 					mp_ubyte* dstSlot = phead[i].patternData+row*phead[i].channum*4+c*4;
203 
204 					mp_ubyte note = track[0]>>2;
205 
206 					if (note)
207 						note+=25;
208 
209 					mp_ubyte ins = ((track[0]&0x03)<<4)+(track[1]>>4);
210 					mp_ubyte eff = track[1]&0x0f;
211 					mp_ubyte op = track[2];
212 
213 					if (eff==0xE)
214 					{
215 						eff=(op>>4)+0x30;
216 						op&=0xf;
217 
218 						if (eff == 0x38)
219 						{
220 							eff = 0x08;
221 							op <<= 4;
222 						}
223 
224 					}
225 
226 					if ((!eff)&&op)
227 						eff=0x20;
228 
229 					// old style modules don't support last effect for:
230 					// - portamento up/down
231 					// - volume slide
232 					if (eff==0x1&&(!op)) eff = 0;
233 					if (eff==0x2&&(!op)) eff = 0;
234 					if (eff==0xA&&(!op)) eff = 0;
235 
236 					if (eff==0x5&&(!op)) eff=0x3;
237 					if (eff==0x6&&(!op)) eff=0x4;
238 
239 					if (eff==0xC)
240 						op = XModule::vol64to255(op);
241 
242 					dstSlot[0] = note;
243 					dstSlot[1] = ins;
244 					dstSlot[2] = eff;
245 					dstSlot[3] = op;
246 
247 					track+=3;
248 				}
249 			}
250 
251 			pTrackSeq++;
252 
253 		}
254 
255 	}
256 
257 	delete[] trackSeq;
258 
259 	delete[] tracks;
260 
261 	/*for (i = 0; i < locf; i++)
262 	{
263 		printf("%c",f.readByte());
264 	}
265 
266 	printf("\n");*/
267 
268 	// song message
269 	if (locf)
270 	{
271 		char* unpackedSongMessage = new char[locf];
272 
273 		f.read(unpackedSongMessage, 1, locf);
274 
275 		mp_sint32 size = locf;
276 
277 		// song message isn't null terminated
278 		for (i = 0; i < size; i++)
279 			if (unpackedSongMessage[i] == '\0') unpackedSongMessage[i] = ' ';
280 
281 		for (i = 0; i < size; i++)
282 		{
283 			char line[50];
284 			memset(line, 0, sizeof(line));
285 
286 			if (size - i >= 39)
287 			{
288 				XModule::convertStr(line, unpackedSongMessage+i, 40, false);
289 				i+=39;
290 			}
291 			else
292 			{
293 				XModule::convertStr(line, unpackedSongMessage+i, size-i, false);
294 				i+=size-i;
295 			}
296 			module->addSongMessageLine(line);
297 		}
298 
299 		delete[] unpackedSongMessage;
300 
301 #ifdef VERBOSE
302 		printf("%s\n",module->message);
303 #endif
304 
305 	}
306 
307 	mp_sint32 result = module->loadModuleSamples(f, XModule::ST_UNSIGNED);
308 	if (result != MP_OK)
309 		return result;
310 
311 	strcpy(header->tracker,"Multitracker");
312 
313 	module->setDefaultPanning();
314 
315 	module->postProcessSamples();
316 
317 	return MP_OK;
318 }
319