comparison minijss/jloadjss.c @ 658:c430112449a7

Move miniJSS into a subdirectory.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 16 Apr 2013 07:32:29 +0300
parents jloadjss.c@cd57ba1130eb
children ed60a7ee3ebb
comparison
equal deleted inserted replaced
657:7e25ec0bbf59 658:c430112449a7
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_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_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 #ifdef 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 #ifdef 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 #ifdef 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 #ifdef 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 #ifdef JM_SUP_PATMODE_5
242
243 #undef JSGETBYTE
244 #define JSGETBYTE(XV) if (!jsGetBufByte(&buf, bufLeft, XV)) return DMERR_OUT_OF_DATA
245
246 #define JSFOREACHNOTE1 \
247 for (channel = 0; channel < pattern->nchannels; channel++) \
248 for (row = 0; row < pattern->nrows; row++) { \
249 JSSNote *note = pattern->data + (pattern->nchannels * row) + channel;
250
251 #define JSFOREACHNOTE2 }
252
253 static int jssGetPatternRawVertElem(Uint8 *buf, size_t *bufLeft, JSSPattern *pattern)
254 {
255 int row, channel;
256 Uint8 tmp;
257
258 assert(buf != NULL);
259 assert(pattern != NULL);
260
261 JSFOREACHNOTE1
262 JSGETBYTE(&tmp);
263 if (tmp == 0)
264 note->note = jsetNotSet;
265 else if (tmp == 127)
266 note->note = jsetNoteOff;
267 else
268 note->note = tmp - 1;
269 JSFOREACHNOTE2
270
271 JSFOREACHNOTE1
272 JSGETBYTE(&tmp);
273 note->instrument = (tmp > 0) ? tmp - 1 : jsetNotSet;
274 JSFOREACHNOTE2
275
276 JSFOREACHNOTE1
277 JSGETBYTE(&tmp);
278 note->volume = (tmp > 0) ? tmp - 1 : jsetNotSet;
279 JSFOREACHNOTE2
280
281 JSFOREACHNOTE1
282 JSGETBYTE(&tmp);
283 note->effect = (tmp > 0) ? tmp - 1 : jsetNotSet;
284 JSFOREACHNOTE2
285
286 JSFOREACHNOTE1
287 JSGETBYTE(&tmp);
288 note->param = (tmp == 0 && note->effect == jsetNotSet) ? jsetNotSet : tmp;
289 JSFOREACHNOTE2
290
291 return DMERR_OK;
292 }
293 #endif
294
295
296 #undef JSGETBUF
297 #undef JSGETBYTE
298 #define JSGETBUF(XV, XT) do { \
299 if (!jsGetBufData(&buf, &bufLeft, XV, sizeof(XT))) \
300 JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA, \
301 "Out of data at getting " # XT " (%d bytes)\n", sizeof(XT)); \
302 } while (0)
303 #define JSGETBYTE(XV) if (!jsGetBufByte(&buf, &bufLeft, XV)) return DMERR_OUT_OF_DATA
304
305
306 #ifdef JM_SUP_EXT_INSTR
307 static void jssCopyEnvelope(JSSEnvelope *e, JSSMODEnvelope *je)
308 {
309 int i;
310
311 e->flags = je->flags;
312 e->npoints = je->npoints;
313 e->sustain = je->sustain;
314 e->loopS = je->loopS;
315 e->loopE = je->loopE;
316
317 for (i = 0; i < je->npoints; i++)
318 {
319 e->points[i].frame = je->points[i].frame;
320 e->points[i].value = je->points[i].value;
321 }
322 }
323 #endif
324
325
326 int jssLoadJSSMOD(Uint8 *bufStart, const size_t bufSize, JSSModule **ppModule)
327 {
328 JSSModule *module;
329 JSSMODHeader jssH;
330 Uint8 *buf = bufStart;
331 size_t bufLeft = bufSize;
332 int index;
333
334 assert(ppModule != NULL);
335 assert(bufStart != NULL);
336 *ppModule = NULL;
337
338 // Check the JSSMOD header
339 memset(&jssH, 0, sizeof(jssH));
340 JSGETBUF(&jssH, JSSMODHeader);
341
342 if (memcmp(jssH.idMagic, "JM", 2) != 0)
343 {
344 JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
345 "Not a valid JSSMOD file, header signature missing!\n");
346 }
347
348 if (jssH.idVersion != JSSMOD_VERSION)
349 {
350 JSSERROR(DMERR_VERSION, DMERR_VERSION,
351 "Unsupported version of JSSMOD 0x%4x, this version only supports 0x%4x!\n",
352 jssH.idVersion, JSSMOD_VERSION);
353 }
354
355 // Allocate the module
356 module = jssAllocateModule();
357 if (module == NULL)
358 {
359 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
360 "Could not allocate memory for module structure.\n");
361 }
362 *ppModule = module;
363
364 // Copy header information
365 module->norders = jssH.norders;
366 module->npatterns = jssH.npatterns;
367 module->nchannels = jssH.nchannels;
368 module->nextInstruments = jssH.nextInstruments;
369 module->ninstruments = jssH.ninstruments;
370 module->defFlags = jssH.defFlags;
371 module->intVersion = jssH.intVersion;
372 module->defRestartPos = jssH.defRestartPos;
373 module->defSpeed = jssH.defSpeed;
374 module->defTempo = jssH.defTempo;
375
376 // Get the orders list
377 for (index = 0; index < module->norders; index++)
378 {
379 Sint16 order;
380 JSGETBUF(&order, Sint16);
381 module->orderList[index] = order;
382 }
383
384 // Parse the patterns
385 for (index = 0; index < module->npatterns; index++)
386 {
387 JSSMODPattern jssP;
388 int result = DMERR_INVALID_DATA;
389 size_t bufSize;
390
391 // Get header and check size
392 memset(&jssP, 0, sizeof(jssP));
393 JSGETBUF(&jssP, JSSMODPattern);
394 bufSize = jssP.size;
395 if (bufLeft < jssP.size)
396 {
397 JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
398 "Out of data for pattern #%d.\n", index);
399 }
400
401 // Allocate pattern
402 module->patterns[index] = jssAllocatePattern(jssP.nrows, module->nchannels);
403 if (module->patterns[index] == NULL)
404 {
405 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
406 "Could not allocate memory for pattern #%d.\n", index);
407 }
408
409 // Get pattern data
410 switch (jssH.patMode)
411 {
412 #ifdef JM_SUP_PATMODE_1
413 case PATMODE_RAW_HORIZ:
414 result = jssGetPatternRawHoriz(buf, &bufSize, module->patterns[index]);
415 break;
416 #endif
417 #ifdef JM_SUP_PATMODE_2
418 case PATMODE_COMP_HORIZ:
419 result = jssGetPatternCompHoriz(buf, &bufSize, module->patterns[index]);
420 break;
421 #endif
422 #ifdef JM_SUP_PATMODE_3
423 case PATMODE_RAW_VERT:
424 result = jssGetPatternRawVert(buf, &bufSize, module->patterns[index]);
425 break;
426 #endif
427 #ifdef JM_SUP_PATMODE_4
428 case PATMODE_COMP_VERT:
429 result = jssGetPatternCompVert(buf, &bufSize, module->patterns[index]);
430 break;
431 #endif
432 #ifdef JM_SUP_PATMODE_5
433 case PATMODE_RAW_ELEM:
434 result = jssGetPatternRawVertElem(buf, &bufSize, module->patterns[index]);
435 break;
436 #endif
437 default:
438 JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA,
439 "Unsupported pattern mode %d. Check compilation options.", jssH.patMode);
440 break;
441 }
442
443 if (bufSize > 0)
444 {
445 JSSWARNING(DMERR_EXTRA_DATA, DMERR_EXTRA_DATA,
446 "Unparsed data after pattern (%d bytes), possibly broken file.\n", bufSize);
447 }
448
449 if (result != DMERR_OK)
450 {
451 JSSERROR(result, result, "Error in unpacking pattern #%i data\n", index);
452 }
453
454 buf += jssP.size;
455 bufLeft -= jssP.size;
456 }
457
458 #ifdef JM_SUP_EXT_INSTR
459 // Read extended instruments
460 for (index = 0; index < module->nextInstruments; index++)
461 {
462 JSSMODExtInstrument jssE;
463 JSSExtInstrument *einst;
464 int i;
465
466 memset(&jssE, 0, sizeof(jssE));
467 JSGETBUF(&jssE, JSSMODExtInstrument);
468
469 if ((einst = jssAllocateExtInstrument()) == NULL)
470 {
471 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
472 "Could not allocate extended instrument structure #%i\n", index);
473 }
474
475 module->extInstruments[index] = einst;
476
477 einst->nsamples = jssE.nsamples;
478 einst->vibratoType = jssE.vibratoType;
479 einst->vibratoSweep = jssE.vibratoSweep;
480 einst->vibratoDepth = jssE.vibratoDepth;
481 einst->vibratoRate = jssE.vibratoRate;
482 einst->fadeOut = jssE.fadeOut;
483
484 for (i = 0; i < jsetNNotes; i++)
485 {
486 int snum = jssE.sNumForNotes[i];
487 einst->sNumForNotes[i] = (snum > 0) ? snum : jsetNotSet;
488 }
489
490 jssCopyEnvelope(&(einst->volumeEnv), &jssE.volumeEnv);
491 jssCopyEnvelope(&(einst->panningEnv), &jssE.panningEnv);
492 }
493
494 #ifdef JM_SUP_INSTR
495 // Read sample instrument headers
496 for (index = 0; index < module->ninstruments; index++)
497 {
498 JSSMODInstrument jssI;
499 JSSInstrument *inst;
500
501 memset(&jssI, 0, sizeof(jssI));
502 JSGETBUF(&jssI, JSSMODInstrument);
503
504 if ((inst = jssAllocateInstrument()) == NULL)
505 {
506 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
507 "Could not allocate instrument structure #%i\n", index);
508 }
509
510 module->instruments[index] = inst;
511
512 inst->size = jssI.size;
513 inst->loopS = jssI.loopS;
514 inst->loopE = jssI.loopE;
515 inst->volume = jssI.volume;
516 inst->flags = jssI.flags;
517 inst->C4BaseSpeed = jssI.C4BaseSpeed;
518 inst->ERelNote = jssI.ERelNote;
519 inst->EFineTune = jssI.EFineTune;
520 inst->EPanning = jssI.EPanning;
521 inst->hasData = jssI.hasData;
522 inst->convFlags = jssI.convFlags;
523 }
524
525 #ifdef JM_SUP_SAMPLES
526 // Read sample data
527 for (index = 0; index < module->ninstruments; index++)
528 {
529 JSSInstrument *inst = module->instruments[index];
530
531 if (inst && inst->hasData)
532 {
533 size_t sz;
534
535 // Calculate data size
536 if (inst->flags & jsf16bit)
537 sz = inst->size * sizeof(Uint16);
538 else
539 sz = inst->size * sizeof(Uint8);
540
541 // Check if we can get as much?
542 if (bufLeft < sz)
543 {
544 JSSERROR(DMERR_OUT_OF_DATA, DMERR_OUT_OF_DATA,
545 "Out of data for instrument sample #%d (%d < %d)\n",
546 index, bufLeft, sz);
547 }
548
549 // Allocate
550 if ((inst->data = dmMalloc(sz)) == NULL)
551 {
552 JSSERROR(DMERR_MALLOC, DMERR_MALLOC,
553 "Could not allocate sample data #%d\n", index);
554 }
555
556 // Copy data
557 memcpy(inst->data, buf, sz);
558 buf += sz;
559 bufLeft -= sz;
560
561 // Convert, if needed
562 if (inst->flags & jsf16bit)
563 jssDecodeSample16(inst->data, inst->size, inst->convFlags);
564 else
565 jssDecodeSample8(inst->data, inst->size, inst->convFlags);
566 }
567 }
568 #else
569 # warning Not including JSSMOD sample loading!
570 #endif // JM_SUP_SAMPLES
571 #else
572 # warning Not including JSSMOD instrument loading!
573 #endif // JM_SUP_INSTR
574 #else
575 # warning Not including JSSMOD ext.instrument loading!
576 #endif // JM_SUP_EXT_INSTR
577
578 return DMERR_OK;
579 }