0
|
1 /*
|
|
2 * miniJSS - JSSMOD module loader
|
|
3 * Programmed and designed by Matti 'ccr' Hamalainen
|
|
4 * (C) Copyright 2007-2009 Tecnic Software productions (TNSP)
|
|
5 */
|
|
6 #include "jssmod.h"
|
|
7 #include <string.h>
|
|
8
|
|
9
|
|
10 #ifdef JM_SUP_PATMODE_ALL
|
|
11 #define JM_SUP_PATMODE_1 1
|
|
12 #define JM_SUP_PATMODE_2 1
|
|
13 #define JM_SUP_PATMODE_3 1
|
|
14 #define JM_SUP_PATMODE_4 1
|
|
15 #define JM_SUP_PATMODE_5 1
|
|
16 #endif
|
|
17
|
|
18
|
|
19 static BOOL jsGetBufData(Uint8 **buf, size_t *bufLeft, void *data, const size_t dataSize)
|
|
20 {
|
|
21 if (*bufLeft >= dataSize)
|
|
22 {
|
|
23 memcpy(data, *buf, dataSize);
|
|
24 *buf += dataSize;
|
|
25 *bufLeft -= dataSize;
|
|
26 return TRUE;
|
|
27 }
|
|
28 else
|
|
29 return FALSE;
|
|
30 }
|
|
31
|
|
32
|
|
33 static BOOL jsGetBufByte(Uint8 **buf, size_t *bufLeft, Uint8 *data)
|
|
34 {
|
|
35 if (*bufLeft > 0)
|
|
36 {
|
|
37 *data = **buf;
|
|
38 (*buf)++;
|
|
39 (*bufLeft)--;
|
|
40 return TRUE;
|
|
41 }
|
|
42 else
|
|
43 return FALSE;
|
|
44 }
|
|
45
|
|
46
|
|
47 #define JSGETBUF(XV, XT) if (!jsGetBufData(buf, bufLeft, XV, sizeof(XT))) return DMERR_OUT_OF_DATA
|
|
48 #define JSGETBYTE(XV) if (!jsGetBufByte(buf, bufLeft, XV)) return DMERR_OUT_OF_DATA
|
|
49
|
|
50
|
|
51 #if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_1) || defined(JM_SUP_PATMODE_3)
|
|
52 static int jssGetConvertedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
|
|
53 {
|
|
54 Uint8 tmp;
|
|
55
|
|
56 JSGETBYTE(&tmp);
|
|
57
|
|
58 if (tmp == 127)
|
|
59 note->note = jsetNoteOff;
|
|
60 else if (tmp == 0)
|
|
61 note->note = jsetNotSet;
|
|
62 else
|
|
63 note->note = tmp - 1;
|
|
64
|
|
65 JSGETBYTE(&tmp);
|
|
66 note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
|
|
67
|
|
68 JSGETBYTE(&tmp);
|
|
69 note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
|
|
70
|
|
71 JSGETBYTE(&tmp);
|
|
72 note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
|
|
73
|
|
74 JSGETBYTE(&tmp);
|
|
75 note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
|
|
76
|
|
77 return DMERR_OK;
|
|
78 }
|
|
79 #endif
|
|
80
|
|
81
|
|
82 #if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_2) || defined(JM_SUP_PATMODE_4)
|
|
83 static int jssGetCompressedNote(Uint8 **buf, size_t *bufLeft, JSSNote *note)
|
|
84 {
|
|
85 Uint8 packb, tmp;
|
|
86
|
|
87 JSGETBYTE(&packb);
|
|
88 if (packb & 0x80)
|
|
89 {
|
|
90 if (packb & COMP_NOTE)
|
|
91 {
|
|
92 JSGETBYTE(&tmp);
|
|
93 if (tmp == 127)
|
|
94 note->note = jsetNoteOff;
|
|
95 else
|
|
96 note->note = tmp;
|
|
97 }
|
|
98
|
|
99 if (packb & COMP_INSTRUMENT)
|
|
100 {
|
|
101 JSGETBYTE(&tmp);
|
|
102 note->instrument = tmp;
|
|
103 }
|
|
104
|
|
105 if (packb & COMP_VOLUME)
|
|
106 {
|
|
107 JSGETBYTE(&tmp);
|
|
108 note->volume = tmp;
|
|
109 }
|
|
110
|
|
111 if (packb & COMP_EFFECT)
|
|
112 {
|
|
113 JSGETBYTE(&tmp);
|
|
114 note->effect = tmp;
|
|
115 note->param = 0;
|
|
116 }
|
|
117
|
|
118 if (packb & COMP_PARAM)
|
|
119 {
|
|
120 JSGETBYTE(&tmp);
|
|
121 note->param = tmp;
|
|
122 }
|
|
123 }
|
|
124 else
|
|
125 {
|
|
126 tmp = packb;
|
|
127
|
|
128 if (tmp == 127)
|
|
129 note->note = jsetNoteOff;
|
|
130 else if (tmp == 0)
|
|
131 note->note = jsetNotSet;
|
|
132 else
|
|
133 note->note = tmp - 1;
|
|
134
|
|
135 JSGETBYTE(&tmp);
|
|
136 note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
|
|
137
|
|
138 JSGETBYTE(&tmp);
|
|
139 note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
|
|
140
|
|
141 JSGETBYTE(&tmp);
|
|
142 note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
|
|
143
|
|
144 JSGETBYTE(&tmp);
|
|
145 note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
|
|
146 }
|
|
147
|
|
148 return DMERR_OK;
|
|
149 }
|
|
150 #endif
|
|
151
|
|
152
|
|
153 #if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_2)
|
|
154 static int jssGetPatternCompHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
|
|
155 {
|
|
156 int row, channel;
|
|
157
|
|
158 assert(buf != NULL);
|
|
159 assert(pattern != NULL);
|
|
160
|
|
161 for (row = 0; row < pattern->nrows; row++)
|
|
162 for (channel = 0; channel < pattern->nchannels; channel++)
|
|
163 {
|
|
164 JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
|
|
165 int res = jssGetCompressedNote(&buf, bufLeft, note);
|
|
166 if (res != DMERR_OK)
|
|
167 JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
|
|
168 }
|
|
169
|
|
170 return DMERR_OK;
|
|
171 }
|
|
172 #endif
|
|
173
|
|
174
|
|
175 #if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_4)
|
|
176 static int jssGetPatternCompVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
|
|
177 {
|
|
178 int row, channel;
|
|
179
|
|
180 assert(buf != NULL);
|
|
181 assert(pattern != NULL);
|
|
182
|
|
183 for (channel = 0; channel < pattern->nchannels; channel++)
|
|
184 for (row = 0; row < pattern->nrows; row++)
|
|
185 {
|
|
186 JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
|
|
187 int res = jssGetCompressedNote(&buf, bufLeft, note);
|
|
188 if (res != DMERR_OK)
|
|
189 JSSERROR(res, res, "Error uncompressing note on row=%d, chn=%d\n", row, channel);
|
|
190 }
|
|
191
|
|
192 return DMERR_OK;
|
|
193 }
|
|
194 #endif
|
|
195
|
|
196
|
|
197 #if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_1)
|
|
198 static int jssGetPatternRawHoriz(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
|
|
199 {
|
|
200 int row, channel;
|
|
201
|
|
202 assert(buf != NULL);
|
|
203 assert(pattern != NULL);
|
|
204
|
|
205 for (row = 0; row < pattern->nrows; row++)
|
|
206 for (channel = 0; channel < pattern->nchannels; channel++)
|
|
207 {
|
|
208 JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
|
|
209 int res = jssGetConvertedNote(&buf, bufLeft, note);
|
|
210 if (res != DMERR_OK)
|
|
211 JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
|
|
212 }
|
|
213
|
|
214 return DMERR_OK;
|
|
215 }
|
|
216 #endif
|
|
217
|
|
218
|
|
219 #if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_3)
|
|
220 static int jssGetPatternRawVert(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
|
|
221 {
|
|
222 int row, channel;
|
|
223
|
|
224 assert(buf != NULL);
|
|
225 assert(pattern != NULL);
|
|
226
|
|
227 for (channel = 0; channel < pattern->nchannels; channel++)
|
|
228 for (row = 0; row < pattern->nrows; row++)
|
|
229 {
|
|
230 JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel];
|
|
231 int res = jssGetConvertedNote(&buf, bufLeft, note);
|
|
232 if (res != DMERR_OK)
|
|
233 JSSERROR(res, res, "Error converting note on row=%d, chn=%d\n", row, channel);
|
|
234 }
|
|
235
|
|
236 return DMERR_OK;
|
|
237 }
|
|
238 #endif
|
|
239
|
|
240
|
|
241 #if defined(JM_SUP_PATMODE_ALL) || defined(JM_SUP_PATMODE_5)
|
|
242 static int jssGetPatternRawVertElem(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
|
|
243 {
|
|
244 int row, channel;
|
|
245
|
|
246 assert(buf != NULL);
|
|
247 assert(pattern != NULL);
|
|
248
|
|
249 return DMERR_OK;
|
|
250 }
|
|
251 #endif
|
|
252
|
|
253
|
|
254 #undef JSGETBUF
|
|
255 #undef JSGETBYTE
|
|
256 #define JSGETBUF(XV, XT) do { \
|
|
257 if (!jsGetBufData(&buf, &bufLeft, XV, sizeof(XT))) \
|
|
258 JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA, \
|
|
259 "Out of data at getting " # XT " (%d bytes)\n", sizeof(XT)); \
|
|
260 } while (0)
|
|
261 #define JSGETBYTE(XV) if (!jsGetBufByte(&buf, &bufLeft, XV)) return DMERR_OUT_OF_DATA
|
|
262
|
|
263
|
|
264 #ifdef JM_SUP_EXT_INSTR
|
|
265 static void jssCopyEnvelope(JSSEnvelope *e, JSSMODEnvelope *je)
|
|
266 {
|
|
267 int i;
|
|
268
|
|
269 e->flags = je->flags;
|
|
270 e->npoints = je->npoints;
|
|
271 e->sustain = je->sustain;
|
|
272 e->loopS = je->loopS;
|
|
273 e->loopE = je->loopE;
|
|
274
|
|
275 for (i = 0; i < je->npoints; i++)
|
|
276 {
|
|
277 e->points[i].frame = je->points[i].frame;
|
|
278 e->points[i].value = je->points[i].value;
|
|
279 }
|
|
280 }
|
|
281 #endif
|
|
282
|
|
283
|
|
284 int jssLoadJSSMOD(Uint8 *bufStart, const size_t bufSize, JSSModule **ppModule)
|
|
285 {
|
|
286 JSSModule *module;
|
|
287 JSSMODHeader jssH;
|
|
288 Uint8 *buf = bufStart;
|
|
289 size_t bufLeft = bufSize;
|
|
290 int index;
|
|
291
|
|
292 assert(ppModule != NULL);
|
|
293 assert(bufStart != NULL);
|
|
294 *ppModule = NULL;
|
|
295
|
|
296 // Check the JSSMOD header
|
|
297 JSGETBUF(&jssH, JSSMODHeader);
|
|
298
|
|
299 if (memcmp(jssH.idMagic, "JM", 2) != 0)
|
|
300 {
|
|
301 JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
|
|
302 "Not a valid JSSMOD file, header signature missing!\n");
|
|
303 }
|
|
304
|
|
305 if (jssH.idVersion != JSSMOD_VERSION)
|
|
306 {
|
|
307 JSSERROR(DMERR_VERSION, DMERR_VERSION,
|
|
308 "Unsupported version of JSSMOD 0x%4x, this version only supports 0x%4x!\n",
|
|
309 jssH.idVersion, JSSMOD_VERSION);
|
|
310 }
|
|
311
|
|
312 // Allocate the module
|
|
313 module = jssAllocateModule();
|
|
314 if (module == NULL)
|
|
315 {
|
|
316 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
|
|
317 "Could not allocate memory for module structure.\n");
|
|
318 }
|
|
319 *ppModule = module;
|
|
320
|
|
321 // Copy header information
|
|
322 module->norders = jssH.norders;
|
|
323 module->npatterns = jssH.npatterns;
|
|
324 module->nchannels = jssH.nchannels;
|
|
325 module->nextInstruments = jssH.nextInstruments;
|
|
326 module->ninstruments = jssH.ninstruments;
|
|
327 module->defFlags = jssH.defFlags;
|
|
328 module->intVersion = jssH.intVersion;
|
|
329 module->defRestartPos = jssH.defRestartPos;
|
|
330 module->defSpeed = jssH.defSpeed;
|
|
331 module->defTempo = jssH.defTempo;
|
|
332
|
|
333 // Get the orders list
|
|
334 for (index = 0; index < module->norders; index++)
|
|
335 {
|
|
336 Sint16 order;
|
|
337 JSGETBUF(&order, Sint16);
|
|
338 module->orderList[index] = order;
|
|
339 }
|
|
340
|
|
341 // Parse the patterns
|
|
342 for (index = 0; index < module->npatterns; index++)
|
|
343 {
|
|
344 JSSMODPattern jssP;
|
|
345 int result = DMERR_INVALID_DATA;
|
|
346 size_t bufSize;
|
|
347
|
|
348 // Get header and check size
|
|
349 JSGETBUF(&jssP, JSSMODPattern);
|
|
350 bufSize = jssP.size;
|
|
351 if (bufLeft < jssP.size)
|
|
352 {
|
|
353 JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
|
|
354 "Out of data for pattern #%d.\n", index);
|
|
355 }
|
|
356
|
|
357 // Allocate pattern
|
|
358 module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels);
|
|
359 if (module->patterns[index] == NULL)
|
|
360 {
|
|
361 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
|
|
362 "Could not allocate memory for pattern #%d.\n", index);
|
|
363 }
|
|
364
|
|
365 // Get pattern data
|
|
366 switch (jssH.patMode)
|
|
367 {
|
|
368 #ifdef JM_SUP_PATMODE_1
|
|
369 case PATMODE_RAW_HORIZ:
|
|
370 result = jssGetPatternRawHoriz(buf, &bufSize, module->patterns[index]);
|
|
371 break;
|
|
372 #endif
|
|
373 #ifdef JM_SUP_PATMODE_2
|
|
374 case PATMODE_COMP_HORIZ:
|
|
375 result = jssGetPatternCompHoriz(buf, &bufSize, module->patterns[index]);
|
|
376 break;
|
|
377 #endif
|
|
378 #ifdef JM_SUP_PATMODE_3
|
|
379 case PATMODE_RAW_VERT:
|
|
380 result = jssGetPatternRawVert(buf, &bufSize, module->patterns[index]);
|
|
381 break;
|
|
382 #endif
|
|
383 #ifdef JM_SUP_PATMODE_4
|
|
384 case PATMODE_COMP_VERT:
|
|
385 result = jssGetPatternCompVert(buf, &bufSize, module->patterns[index]);
|
|
386 break;
|
|
387 #endif
|
|
388 #ifdef JM_SUP_PATMODE_5
|
|
389 case PATMODE_RAW_ELEM:
|
|
390 result = jssGetPatternRawVertElem(buf, &bufSize, module->patterns[index]);
|
|
391 break;
|
|
392 #endif
|
|
393 default:
|
|
394 JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
|
|
395 "Unsupported pattern mode %d. Check compilation options.", jssH.patMode);
|
|
396 break;
|
|
397 }
|
|
398
|
|
399 if (bufSize > 0)
|
|
400 {
|
|
401 JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA,
|
|
402 "Unparsed data after pattern (%d bytes), possibly broken file.\n", bufSize);
|
|
403 }
|
|
404
|
|
405 if (result != DMERR_OK)
|
|
406 {
|
|
407 JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index);
|
|
408 }
|
|
409
|
|
410 buf += jssP.size;
|
|
411 bufLeft -= jssP.size;
|
|
412 }
|
|
413
|
|
414 #ifdef JM_SUP_EXT_INSTR
|
|
415 // Read extended instruments
|
|
416 for (index = 0; index < module->nextInstruments; index++)
|
|
417 {
|
|
418 JSSMODExtInstrument jssE;
|
|
419 JSSExtInstrument *einst;
|
|
420 int i;
|
|
421
|
|
422 JSGETBUF(&jssE, JSSMODExtInstrument);
|
|
423
|
|
424 if ((einst = jssAllocateExtInstrument()) == NULL)
|
|
425 {
|
|
426 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
|
|
427 "Could not allocate extended instrument structure #%i\n", index);
|
|
428 }
|
|
429
|
|
430 module->extInstruments[index] = einst;
|
|
431
|
|
432 einst->nsamples = jssE.nsamples;
|
|
433 einst->vibratoType = jssE.vibratoType;
|
|
434 einst->vibratoSweep = jssE.vibratoSweep;
|
|
435 einst->vibratoDepth = jssE.vibratoDepth;
|
|
436 einst->vibratoRate = jssE.vibratoRate;
|
|
437 einst->fadeOut = jssE.fadeOut;
|
|
438
|
|
439 for (i = 0; i < jsetNNotes; i++)
|
|
440 {
|
|
441 int snum = jssE.sNumForNotes[i];
|
|
442 einst->sNumForNotes[i] = (snum > 0) ? snum : jsetNotSet;
|
|
443 }
|
|
444
|
|
445 jssCopyEnvelope(&(einst->volumeEnv), &jssE.volumeEnv);
|
|
446 jssCopyEnvelope(&(einst->panningEnv), &jssE.panningEnv);
|
|
447 }
|
|
448
|
|
449 #ifdef JM_SUP_INSTR
|
|
450 // Read sample instrument headers
|
|
451 for (index = 0; index < module->ninstruments; index++)
|
|
452 {
|
|
453 JSSMODInstrument jssI;
|
|
454 JSSInstrument *inst;
|
|
455
|
|
456 JSGETBUF(&jssI, JSSMODInstrument);
|
|
457
|
|
458 if ((inst = jssAllocateInstrument()) == NULL)
|
|
459 {
|
|
460 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
|
|
461 "Could not allocate instrument structure #%i\n", index);
|
|
462 }
|
|
463
|
|
464 module->instruments[index] = inst;
|
|
465
|
|
466 inst->size = jssI.size;
|
|
467 inst->loopS = jssI.loopS;
|
|
468 inst->loopE = jssI.loopE;
|
|
469 inst->volume = jssI.volume;
|
|
470 inst->flags = jssI.flags;
|
|
471 inst->C4BaseSpeed = jssI.C4BaseSpeed;
|
|
472 inst->ERelNote = jssI.ERelNote;
|
|
473 inst->EFineTune = jssI.EFineTune;
|
|
474 inst->EPanning = jssI.EPanning;
|
|
475 inst->hasData = jssI.hasData;
|
|
476 inst->convFlags = jssI.convFlags;
|
|
477 }
|
|
478
|
|
479 #ifdef JM_SUP_SAMPLES
|
|
480 // Read sample data
|
|
481 for (index = 0; index < module->ninstruments; index++)
|
|
482 {
|
|
483 JSSInstrument *inst = module->instruments[index];
|
|
484
|
|
485 if (inst && inst->hasData)
|
|
486 {
|
|
487 size_t sz;
|
|
488
|
|
489 // Calculate data size
|
|
490 if (inst->flags & jsf16bit)
|
|
491 sz = inst->size * sizeof(Uint16);
|
|
492 else
|
|
493 sz = inst->size * sizeof(Uint8);
|
|
494
|
|
495 // Check if we can get as much?
|
|
496 if (bufLeft < sz)
|
|
497 {
|
|
498 JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
|
|
499 "Out of data for instrument sample #%d (%d < %d)\n",
|
|
500 index, bufLeft, sz);
|
|
501 }
|
|
502
|
|
503 // Allocate
|
|
504 if ((inst->data = dmMalloc(sz)) == NULL)
|
|
505 {
|
|
506 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
|
|
507 "Could not allocate sample data #%d\n", index);
|
|
508 }
|
|
509
|
|
510 // Copy data
|
|
511 memcpy(inst->data, buf, sz);
|
|
512 buf += sz;
|
|
513 bufLeft -= sz;
|
|
514
|
|
515 // Convert, if needed
|
|
516 if (inst->flags & jsf16bit)
|
|
517 jssDecodeSample16(inst->data, inst->size, inst->convFlags);
|
|
518 else
|
|
519 jssDecodeSample8(inst->data, inst->size, inst->convFlags);
|
|
520 }
|
|
521 }
|
|
522 #else
|
|
523 #warning Not including JSSMOD sample loading!
|
|
524 #endif // JM_SUP_SAMPLES
|
|
525 #else
|
|
526 #warning Not including JSSMOD instrument loading!
|
|
527 #endif // JM_SUP_INSTR
|
|
528 #else
|
|
529 #warning Not including JSSMOD ext.instrument loading!
|
|
530 #endif // JM_SUP_EXT_INSTR
|
|
531
|
|
532 return DMERR_OK;
|
|
533 }
|