Mercurial > hg > dmlib
annotate xm2jss.c @ 86:7108681151a4
Use a cast.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 02 Oct 2012 10:28:54 +0300 |
parents | 35db15881923 |
children | 50f55def91e5 |
rev | line source |
---|---|
0 | 1 /* |
2 * xm2jss - Convert XM module to JSSMOD | |
3 * Programmed and designed by Matti 'ccr' Hamalainen | |
4 * (C) Copyright 2006-2009 Tecnic Software productions (TNSP) | |
5 * | |
6 * Please read file 'COPYING' for information on license and distribution. | |
7 */ | |
8 #include <stdio.h> | |
9 #include <errno.h> | |
10 #include "jss.h" | |
11 #include "jssmod.h" | |
12 #include "dmlib.h" | |
13 #include "dmargs.h" | |
14 #include "dmres.h" | |
15 | |
16 | |
17 char *srcFilename = NULL, *destFilename = NULL; | |
18 BOOL optIgnoreErrors = FALSE, | |
19 optStripExtInstr = FALSE, | |
20 optStripInstr = FALSE, | |
21 optStripSamples = FALSE, | |
22 optOptimize = FALSE; | |
23 | |
24 int optPatternMode = PATMODE_COMP_HORIZ, | |
25 optSampMode16 = jsampDelta, | |
26 optSampMode8 = jsampFlipSign | jsampDelta; | |
27 | |
28 #define SAMPMODE_MASK (jsampFlipSign | jsampSwapEndianess | jsampSplit | jsampDelta) | |
29 | |
30 | |
31 static const char* patModeTable[PATMODE_LAST] = | |
32 { | |
33 "Raw horizontal", | |
34 "Compressed horizontal (similar to XM modules)", | |
35 "Raw vertical", | |
36 "Compressed vertical", | |
37 "Raw vertical for each element", | |
38 }; | |
39 | |
40 | |
41 DMOptArg optList[] = { | |
42 { 0, '?', "help", "Show this help", OPT_NONE }, | |
43 { 5, 'v', "verbose", "Be more verbose", OPT_NONE }, | |
44 { 6, 'o', "output", "Output file", OPT_ARGREQ }, | |
45 { 1, 'i', "ignore", "Ignore errors", OPT_NONE }, | |
46 { 2, 'p', "patterns", "Pattern storage mode", OPT_ARGREQ }, | |
47 { 3, 'E', "strip-ext-instr","Strip ext. instruments (implies -I -S)", OPT_NONE }, | |
48 { 9, 'I', "strip-instr", "Strip instruments (implies -S)", OPT_NONE }, | |
49 { 4, 'S', "strip-samples", "Strip instr. sampledata", OPT_NONE }, | |
50 { 7, '8', "smode8", "8-bit sample conversion flags", OPT_ARGREQ }, | |
51 { 8, '1', "smode16", "16-bit sample conversion flags", OPT_ARGREQ }, | |
52 {10, 'O', "optimize", "Optimize module", OPT_NONE }, | |
53 }; | |
54 | |
55 const int optListN = sizeof(optList) / sizeof(optList[0]); | |
56 | |
57 | |
58 void argShowHelp() | |
59 { | |
60 int i; | |
61 | |
62 dmPrintBanner(stdout, dmProgName, "[options] <module.xm>"); | |
63 dmArgsPrintHelp(stdout, optList, optListN); | |
64 | |
65 printf("\n" | |
66 "Pattern storage modes:\n"); | |
67 | |
68 for (i = 1; i < PATMODE_LAST; i++) | |
69 printf(" %d = %s\n", i, patModeTable[i-1]); | |
70 | |
71 printf( | |
72 "\n" | |
73 "Sample data conversion flags (summative):\n" | |
74 " 1 = Delta encoding (DEF 8 & 16)\n" | |
75 " 2 = Flip signedness (DEF 8)\n" | |
76 " 4 = Swap endianess (affects 16-bit only)\n" | |
77 " 8 = Split and de-interleave hi/lo bytes (affects 16-bit only)\n" | |
78 "\n" | |
79 ); | |
80 } | |
81 | |
82 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
83 { | |
84 (void) optArg; | |
85 | |
86 switch (optN) | |
87 { | |
88 case 0: | |
89 argShowHelp(); | |
90 exit(0); | |
91 break; | |
92 | |
93 case 1: | |
94 optIgnoreErrors = TRUE; | |
95 break; | |
96 | |
97 case 2: | |
98 optPatternMode = atoi(optArg); | |
99 if (optPatternMode <= 0 || optPatternMode >= PATMODE_LAST) | |
100 { | |
101 dmError("Unknown pattern conversion mode %d\n", optPatternMode); | |
102 return FALSE; | |
103 } | |
104 break; | |
105 | |
106 case 7: optSampMode8 = atoi(optArg) & SAMPMODE_MASK; break; | |
107 case 8: optSampMode16 = atoi(optArg) & SAMPMODE_MASK; break; | |
108 | |
109 case 4: optStripSamples = TRUE; break; | |
110 case 3: optStripExtInstr = TRUE; break; | |
111 case 9: optStripInstr = TRUE; break; | |
112 case 10: optOptimize = TRUE; break; | |
113 | |
114 case 5: | |
115 dmVerbosity++; | |
116 break; | |
117 | |
118 case 6: | |
119 destFilename = optArg; | |
120 break; | |
121 | |
122 default: | |
123 dmError("Unknown argument '%s'.\n", currArg); | |
124 return FALSE; | |
125 } | |
126 | |
127 return TRUE; | |
128 } | |
129 | |
130 | |
131 BOOL argHandleFile(char *currArg) | |
132 { | |
133 // Was not option argument | |
134 if (!srcFilename) | |
135 srcFilename = currArg; | |
136 else | |
137 { | |
138 dmError("Gay error '%s'.\n", currArg); | |
139 return FALSE; | |
140 } | |
141 | |
142 return TRUE; | |
143 } | |
144 | |
145 | |
146 /* These functions and the macro mess are meant to make the | |
147 * conversion routines themselves clearer and simpler. | |
148 */ | |
149 BOOL jsPutByte(Uint8 *patBuf, size_t patBufSize, size_t *npatBuf, Uint8 val) | |
150 { | |
151 if (*npatBuf >= patBufSize) | |
152 return FALSE; | |
153 else | |
154 { | |
155 patBuf[*npatBuf] = val; | |
156 (*npatBuf)++; | |
157 return TRUE; | |
158 } | |
159 } | |
160 | |
161 #define JSPUTBYTE(x) do { if (!jsPutByte(patBuf, patBufSize, patSize, x)) return DMERR_BOUNDS; } while (0) | |
162 | |
163 #define JSCOMP(x,z) do { if ((x) != jsetNotSet) { qflags |= (z); qcomp++; } } while (0) | |
164 | |
165 #define JSCOMPPUT(xf,xv,qv) do { \ | |
166 if (qflags & (xf)) { \ | |
167 if ((xv) < 0 || (xv) > 255) \ | |
168 JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, \ | |
169 "%s value out of bounds %d.\n", qv, (xv)); \ | |
170 JSPUTBYTE(xv); \ | |
171 } \ | |
172 } while (0) | |
173 | |
174 #define JSCONVPUT(xv,qv) do { \ | |
175 if ((xv) != jsetNotSet) { \ | |
176 if ((xv) < 0 || (xv) > 254) \ | |
177 JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, \ | |
178 "%s value out of bounds %d.\n", qv, (xv)); \ | |
179 JSPUTBYTE((xv) + 1); \ | |
180 } else { \ | |
181 JSPUTBYTE(0); \ | |
182 } \ | |
183 } while (0) | |
184 | |
185 | |
186 /* Convert a note | |
187 */ | |
188 static int jssConvertNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note) | |
189 { | |
190 Uint8 tmp; | |
191 if (note->note == jsetNotSet) | |
192 tmp = 0; | |
193 else if (note->note == jsetNoteOff) | |
194 tmp = 127; | |
195 else | |
196 tmp = note->note + 1; | |
197 | |
198 if (tmp > 0x7f) | |
199 JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp); | |
200 | |
201 JSPUTBYTE(tmp & 0x7f); | |
202 | |
203 JSCONVPUT(note->instrument, "Instrument"); | |
204 JSCONVPUT(note->volume, "Volume"); | |
205 JSCONVPUT(note->effect, "Effect"); | |
206 | |
207 tmp = (note->param != jsetNotSet) ? note->param : 0; | |
208 JSPUTBYTE(tmp); | |
209 | |
210 return DMERR_OK; | |
211 } | |
212 | |
213 | |
214 /* Compress a note | |
215 */ | |
216 static int jssCompressNote(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSNote *note) | |
217 { | |
218 Uint8 qflags = 0; | |
219 int qcomp = 0; | |
220 | |
221 JSCOMP(note->note, COMP_NOTE); | |
222 JSCOMP(note->instrument, COMP_INSTRUMENT); | |
223 JSCOMP(note->volume, COMP_VOLUME); | |
224 JSCOMP(note->effect, COMP_EFFECT); | |
225 if (note->param != jsetNotSet && note->param != 0) | |
226 { | |
227 qflags |= COMP_PARAM; | |
228 qcomp++; | |
229 } | |
230 | |
231 if (qcomp < 4) | |
232 { | |
233 JSPUTBYTE(qflags | 0x80); | |
234 | |
235 if (note->note != jsetNotSet) | |
236 { | |
237 Uint8 tmp = (note->note != jsetNoteOff) ? note->note : 127; | |
238 if (tmp > 0x7f) | |
239 JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp); | |
240 JSPUTBYTE(tmp); | |
241 } | |
242 | |
243 JSCOMPPUT(COMP_INSTRUMENT, note->instrument, "Instrument"); | |
244 JSCOMPPUT(COMP_VOLUME, note->volume, "Volume"); | |
245 JSCOMPPUT(COMP_EFFECT, note->effect, "Effect"); | |
246 JSCOMPPUT(COMP_PARAM, note->param, "Param"); | |
247 } else | |
248 return jssConvertNote(patBuf, patBufSize, patSize, note); | |
249 | |
250 return DMERR_OK; | |
251 } | |
252 | |
253 | |
254 /* Compress pattern | |
255 */ | |
256 static int jssConvertPatternCompHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) | |
257 { | |
258 int row, channel; | |
259 *patSize = 0; | |
260 | |
261 for (row = 0; row < pattern->nrows; row++) | |
262 for (channel = 0; channel < pattern->nchannels; channel++) | |
263 { | |
264 const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; | |
265 const int res = jssCompressNote(patBuf, patBufSize, patSize, note); | |
266 if (res != DMERR_OK) | |
267 { | |
268 JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
269 patBuf, patBufSize, *patSize, row, channel); | |
270 return res; | |
271 } | |
272 } | |
273 | |
274 return DMERR_OK; | |
275 } | |
276 | |
277 | |
278 static int jssConvertPatternCompVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) | |
279 { | |
280 int row, channel; | |
281 *patSize = 0; | |
282 | |
283 for (channel = 0; channel < pattern->nchannels; channel++) | |
284 for (row = 0; row < pattern->nrows; row++) | |
285 { | |
286 const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; | |
287 const int res = jssCompressNote(patBuf, patBufSize, patSize, note); | |
288 if (res != DMERR_OK) | |
289 { | |
290 JSSERROR(res, res, "Note compression failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
291 patBuf, patBufSize, *patSize, row, channel); | |
292 return res; | |
293 } | |
294 } | |
295 | |
296 return DMERR_OK; | |
297 } | |
298 | |
299 | |
300 /* Convert a pattern | |
301 */ | |
302 static int jssConvertPatternRawHoriz(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) | |
303 { | |
304 int row, channel; | |
305 *patSize = 0; | |
306 | |
307 for (row = 0; row < pattern->nrows; row++) | |
308 for (channel = 0; channel < pattern->nchannels; channel++) | |
309 { | |
310 const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; | |
311 const int res = jssConvertNote(patBuf, patBufSize, patSize, note); | |
312 if (res != DMERR_OK) | |
313 { | |
314 JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
315 patBuf, patBufSize, *patSize, row, channel); | |
316 return res; | |
317 } | |
318 } | |
319 | |
320 return DMERR_OK; | |
321 } | |
322 | |
323 | |
324 static int jssConvertPatternRawVert(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) | |
325 { | |
326 int row, channel; | |
327 *patSize = 0; | |
328 | |
329 for (channel = 0; channel < pattern->nchannels; channel++) | |
330 for (row = 0; row < pattern->nrows; row++) | |
331 { | |
332 const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; | |
333 const int res = jssConvertNote(patBuf, patBufSize, patSize, note); | |
334 if (res != DMERR_OK) | |
335 { | |
336 JSSERROR(res, res, "Note conversion failed [patBuf=%p, patBufSize=%d, patSize=%d, row=%d, chn=%d]\n", | |
337 patBuf, patBufSize, *patSize, row, channel); | |
338 return res; | |
339 } | |
340 } | |
341 | |
342 return DMERR_OK; | |
343 } | |
344 | |
345 | |
346 #define JSFOREACHNOTE1 \ | |
347 for (channel = 0; channel < pattern->nchannels; channel++) \ | |
348 for (row = 0; row < pattern->nrows; row++) { \ | |
349 const JSSNote *note = &pattern->data[(pattern->nchannels * row) + channel]; | |
350 | |
351 #define JSFOREACHNOTE2 } | |
352 | |
353 static int jssConvertPatternRawElem(Uint8 *patBuf, const size_t patBufSize, size_t *patSize, const JSSPattern *pattern) | |
354 { | |
355 Uint8 tmp; | |
356 int row, channel; | |
357 *patSize = 0; | |
358 | |
359 JSFOREACHNOTE1; | |
360 if (note->note == jsetNotSet) | |
361 tmp = 0; | |
362 else if (note->note == jsetNoteOff) | |
363 tmp = 127; | |
364 else | |
365 tmp = note->note + 1; | |
366 if (tmp > 0x7f) | |
367 JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, "Note value out of bounds %d > 0x7f.\n", tmp); | |
368 JSPUTBYTE(tmp); | |
369 JSFOREACHNOTE2; | |
370 | |
371 JSFOREACHNOTE1; | |
372 JSCONVPUT(note->instrument, "Instrument"); | |
373 JSFOREACHNOTE2; | |
374 | |
375 JSFOREACHNOTE1; | |
376 JSCONVPUT(note->volume, "Volume"); | |
377 JSFOREACHNOTE2; | |
378 | |
379 JSFOREACHNOTE1; | |
380 JSCONVPUT(note->effect, "Effect"); | |
381 JSFOREACHNOTE2; | |
382 | |
383 JSFOREACHNOTE1; | |
384 JSCONVPUT(note->param, "Param"); | |
385 JSFOREACHNOTE2; | |
386 | |
387 return DMERR_OK; | |
388 } | |
389 | |
390 #undef JSFOREACHNOTE1 | |
391 #undef JSFOREACHNOTE2 | |
392 | |
393 | |
394 static void jssCopyEnvelope(JSSMODEnvelope *je, JSSEnvelope *e) | |
395 { | |
396 int i; | |
397 | |
398 je->flags = e->flags; | |
399 je->npoints = e->npoints; | |
400 je->sustain = e->sustain; | |
401 je->loopS = e->loopS; | |
402 je->loopE = e->loopE; | |
403 | |
404 for (i = 0; i < e->npoints; i++) | |
405 { | |
406 je->points[i].frame = e->points[i].frame; | |
407 je->points[i].value = e->points[i].value; | |
408 } | |
409 } | |
410 | |
411 | |
412 /* Save a JSSMOD file | |
413 */ | |
414 int jssSaveJSSMOD(FILE *outFile, JSSModule *m, int patMode, int flags8, int flags16) | |
415 { | |
416 JSSMODHeader jssH; | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
417 int i, pattern, order, instr, totalSize; |
0 | 418 const size_t patBufSize = 64*1024; // 64kB pattern buffer |
419 Uint8 *patBuf; | |
420 | |
421 // Check the module | |
422 if (m == NULL) | |
423 JSSERROR(DMERR_NULLPTR, DMERR_NULLPTR, "Module pointer was NULL\n"); | |
424 | |
425 if ((m->nchannels < 1) || (m->npatterns < 1) || (m->norders < 1)) | |
426 JSSERROR(DMERR_BOUNDS, DMERR_BOUNDS, | |
427 "Module had invalid values (nchannels=%i, npatterns=%i, norders=%i)\n", | |
428 m->nchannels, m->npatterns, m->norders); | |
429 | |
430 // Create the JSSMOD header | |
431 jssH.idMagic[0] = 'J'; | |
432 jssH.idMagic[1] = 'M'; | |
433 jssH.idVersion = JSSMOD_VERSION; | |
434 jssH.norders = m->norders; | |
435 jssH.npatterns = m->npatterns; | |
436 jssH.nchannels = m->nchannels; | |
437 jssH.nextInstruments = m->nextInstruments; | |
438 jssH.ninstruments = m->ninstruments; | |
439 jssH.defFlags = m->defFlags; | |
440 jssH.intVersion = m->intVersion; | |
441 jssH.defRestartPos = m->defRestartPos; | |
442 jssH.defSpeed = m->defSpeed; | |
443 jssH.defTempo = m->defTempo; | |
444 jssH.patMode = patMode; | |
445 | |
446 // Write header | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
447 totalSize = sizeof(jssH); |
0 | 448 if (fwrite(&jssH, sizeof(jssH), 1, outFile) != 1) |
449 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD header!\n"); | |
450 | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
451 dmMsg(1," * JSSMOD-header 0x%04x, %d bytes.\n", JSSMOD_VERSION, totalSize); |
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
452 |
0 | 453 // Write orders list |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
454 for (totalSize = order = 0; order < m->norders; order++) |
0 | 455 { |
456 Uint16 tmp = m->orderList[order]; | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
457 totalSize += sizeof(tmp); |
0 | 458 if (fwrite(&tmp, sizeof(tmp), 1, outFile) != 1) |
459 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, "Could not write JSSMOD orders list.\n"); | |
460 } | |
461 | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
462 dmMsg(1," * %d item orders list, %d bytes.\n", |
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
463 m->norders, totalSize); |
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
464 |
0 | 465 // Allocate pattern compression buffer |
466 if ((patBuf = dmMalloc(patBufSize)) == NULL) | |
467 JSSERROR(DMERR_MALLOC, DMERR_MALLOC, | |
468 "Error allocating memory for pattern compression buffer.\n"); | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
469 |
0 | 470 |
471 // Write patterns | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
472 for (totalSize = pattern = 0; pattern < m->npatterns; pattern++) |
0 | 473 { |
474 JSSMODPattern patHead; | |
475 size_t finalSize = 0; | |
476 | |
477 switch (patMode) | |
478 { | |
479 case PATMODE_RAW_HORIZ: | |
480 i = jssConvertPatternRawHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]); | |
481 break; | |
482 case PATMODE_COMP_HORIZ: | |
483 i = jssConvertPatternCompHoriz(patBuf, patBufSize, &finalSize, m->patterns[pattern]); | |
484 break; | |
485 case PATMODE_RAW_VERT: | |
486 i = jssConvertPatternRawVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]); | |
487 break; | |
488 case PATMODE_COMP_VERT: | |
489 i = jssConvertPatternCompVert(patBuf, patBufSize, &finalSize, m->patterns[pattern]); | |
490 break; | |
491 case PATMODE_RAW_ELEM: | |
492 i = jssConvertPatternRawElem(patBuf, patBufSize, &finalSize, m->patterns[pattern]); | |
493 break; | |
494 default: | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
495 i = DMERR_INVALID_DATA; |
0 | 496 dmFree(patBuf); |
497 JSSERROR(DMERR_INVALID_DATA, DMERR_INVALID_DATA, | |
498 "Unsupported pattern conversion mode %d.\n", patMode); | |
499 break; | |
500 } | |
501 | |
502 if (i != DMERR_OK) | |
503 { | |
504 dmFree(patBuf); | |
505 JSSERROR(i, i, "Error converting pattern data #%i\n", pattern); | |
506 } | |
507 else | |
508 { | |
509 dmMsg(3, " - Pattern %d size %d bytes\n", pattern, finalSize); | |
510 patHead.nrows = m->patterns[pattern]->nrows; | |
511 patHead.size = finalSize; | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
512 totalSize += finalSize + sizeof(patHead); |
0 | 513 |
514 if (fwrite(&patHead, sizeof(patHead), 1, outFile) != 1) | |
515 { | |
516 dmFree(patBuf); | |
517 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, | |
518 "Error writing pattern #%d header\n", pattern); | |
519 } | |
520 | |
521 if (fwrite(patBuf, sizeof(Uint8), finalSize, outFile) != finalSize) | |
522 { | |
523 dmFree(patBuf); | |
524 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, | |
525 "Error writing pattern #%d data\n", pattern); | |
526 } | |
527 } | |
528 } | |
529 dmFree(patBuf); | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
530 dmMsg(1," * %d patterns, %d bytes.\n", m->npatterns, totalSize); |
0 | 531 |
532 // Write extended instruments | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
533 for (totalSize = instr = 0; instr < m->nextInstruments; instr++) |
0 | 534 { |
535 JSSMODExtInstrument jssE; | |
536 JSSExtInstrument *einst = m->extInstruments[instr]; | |
537 | |
538 memset(&jssE, 0, sizeof(jssE)); | |
539 | |
540 if (einst) | |
541 { | |
542 // Create header | |
543 jssE.nsamples = einst->nsamples; | |
544 for (i = 0; i < jsetNNotes; i++) | |
545 { | |
546 int snum = einst->sNumForNotes[i]; | |
547 jssE.sNumForNotes[i] = (snum != jsetNotSet) ? snum : 0; | |
548 } | |
549 | |
550 jssCopyEnvelope(&jssE.volumeEnv, &(einst->volumeEnv)); | |
551 jssCopyEnvelope(&jssE.panningEnv, &(einst->panningEnv)); | |
552 jssE.vibratoType = einst->vibratoType; | |
553 jssE.vibratoSweep = einst->vibratoSweep; | |
554 jssE.vibratoDepth = einst->vibratoDepth; | |
555 jssE.fadeOut = einst->fadeOut; | |
556 } else | |
557 JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Extended instrument #%i NULL!\n", instr); | |
558 | |
559 // Write to file | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
560 totalSize += sizeof(jssE); |
0 | 561 if (fwrite(&jssE, sizeof(jssE), 1, outFile) != 1) |
562 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, | |
563 "Could not write JSSMOD extended instrument #%i to file!\n", instr); | |
564 } | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
565 dmMsg(1," * %d Extended Instruments, %d bytes.\n", m->nextInstruments, totalSize); |
0 | 566 |
567 | |
568 // Write sample instrument headers | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
569 for (totalSize = instr = 0; instr < m->ninstruments; instr++) |
0 | 570 { |
571 JSSMODInstrument jssI; | |
572 JSSInstrument *pInst = m->instruments[instr]; | |
573 | |
574 memset(&jssI, 0, sizeof(jssI)); | |
575 | |
576 // Create header | |
577 if (pInst) | |
578 { | |
579 jssI.size = pInst->size; | |
580 jssI.loopS = pInst->loopS; | |
581 jssI.loopE = pInst->loopE; | |
582 jssI.volume = pInst->volume; | |
583 jssI.flags = pInst->flags; | |
584 jssI.C4BaseSpeed = pInst->C4BaseSpeed; | |
585 jssI.ERelNote = pInst->ERelNote; | |
586 jssI.EFineTune = pInst->EFineTune; | |
587 jssI.EPanning = pInst->EPanning; | |
588 jssI.hasData = (pInst->data != NULL) ? TRUE : FALSE; | |
589 jssI.convFlags = (pInst->flags & jsf16bit) ? flags16 : flags8; | |
590 } | |
591 else | |
592 JSSWARNING(DMERR_NULLPTR, DMERR_NULLPTR, "Instrument #%i NULL!\n", instr); | |
593 | |
594 // Write to file | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
595 totalSize += sizeof(jssI); |
0 | 596 if (fwrite(&jssI, sizeof(jssI), 1, outFile) != 1) |
597 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, | |
598 "Could not write JSSMOD instrument #%i to file!\n", instr); | |
599 } | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
600 dmMsg(1," * %d Instrument headers, %d bytes.\n", m->ninstruments, totalSize); |
0 | 601 |
602 // Write sample data | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
603 for (totalSize = instr = 0; instr < m->ninstruments; instr++) |
0 | 604 if (m->instruments[instr]) |
605 { | |
606 JSSInstrument *inst = m->instruments[instr]; | |
607 if (inst->data != NULL) | |
608 { | |
609 size_t res; | |
610 if (inst->flags & jsf16bit) | |
611 { | |
612 jssEncodeSample16(inst->data, inst->size, flags16); | |
613 res = fwrite(inst->data, sizeof(Uint16), inst->size, outFile); | |
614 } | |
615 else | |
616 { | |
617 jssEncodeSample8(inst->data, inst->size, flags8); | |
618 res = fwrite(inst->data, sizeof(Uint8), inst->size, outFile); | |
619 } | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
620 |
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
621 totalSize += inst->size; |
86 | 622 if (res != (size_t) inst->size) |
0 | 623 JSSERROR(DMERR_FWRITE, DMERR_FWRITE, |
624 "Could not write JSSMOD sample #%i to file!\n", instr); | |
625 } | |
626 } | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
627 dmMsg(1," * %d samples, %d bytes.\n", m->ninstruments, totalSize); |
0 | 628 |
629 return DMERR_OK; | |
630 } | |
631 | |
632 | |
633 /* Optimize a given module | |
634 */ | |
635 JSSModule *optimizeModule(JSSModule *m) | |
636 { | |
637 BOOL usedPatterns[jsetMaxPatterns + 1], | |
638 usedInstruments[jsetMaxInstruments + 1], | |
639 usedExtInstruments[jsetMaxInstruments + 1]; | |
640 int mapExtInstruments[jsetMaxInstruments + 1], | |
641 mapInstruments[jsetMaxInstruments + 1], | |
642 mapPatterns[jsetMaxPatterns + 1]; | |
643 JSSModule *r = NULL; | |
644 int i, n8, n16; | |
645 | |
646 // Allocate a new module | |
647 if ((r = jssAllocateModule()) == NULL) | |
648 return NULL; | |
649 | |
650 // Allocate tables | |
651 | |
652 // Copy things | |
653 r->moduleType = m->moduleType; | |
654 r->moduleName = dm_strdup(m->moduleName); | |
655 r->trackerName = dm_strdup(m->trackerName); | |
656 r->defSpeed = m->defSpeed; | |
657 r->defTempo = m->defTempo; | |
658 r->defFlags = m->defFlags; | |
659 r->defRestartPos = m->defRestartPos; | |
660 r->intVersion = m->intVersion; | |
661 r->nchannels = m->nchannels; | |
662 r->norders = m->norders; | |
663 for (i = 0; i < jsetNChannels; i++) | |
664 r->defPanning[i] = m->defPanning[i]; | |
665 | |
666 // Initialize values | |
667 for (i = 0; i <= jsetMaxInstruments; i++) | |
668 { | |
669 usedExtInstruments[i] = FALSE; | |
670 usedInstruments[i] = FALSE; | |
671 mapExtInstruments[i] = jsetNotSet; | |
672 mapInstruments[i] = jsetNotSet; | |
673 } | |
674 | |
675 for (i = 0; i <= jsetMaxPatterns; i++) | |
676 { | |
677 usedPatterns[i] = FALSE; | |
678 mapPatterns[i] = jsetNotSet; | |
679 } | |
680 | |
681 // Find out all used patterns and ext.instruments | |
682 for (i = 0; i < m->norders; i++) | |
683 { | |
684 int pattern = m->orderList[i]; | |
685 if (pattern >= 0 && pattern < m->npatterns) | |
686 { | |
687 JSSPattern *p = m->patterns[pattern]; | |
688 if (p != NULL) | |
689 { | |
690 int row, channel; | |
691 JSSNote *n = p->data; | |
692 | |
693 // Mark pattern as used | |
694 usedPatterns[pattern] = TRUE; | |
695 | |
696 // Check all notes | |
697 for (row = 0; row < p->nrows; row++) | |
698 for (channel = 0; channel < p->nchannels; channel++, n++) | |
699 { | |
700 if (n->instrument != jsetNotSet) | |
701 { | |
702 if (n->instrument >= 0 && n->instrument < m->nextInstruments) | |
703 usedExtInstruments[n->instrument] = TRUE; | |
704 else | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
705 dmMsg(2, "Pattern 0x%x, row=0x%x, chn=%d has invalid instrument 0x%x\n", |
0 | 706 pattern, row, channel, n->instrument); |
707 } | |
708 } | |
709 } | |
710 else | |
711 { | |
712 dmError("Pattern 0x%x is used on order 0x%x, but has no data!\n", | |
713 pattern, i); | |
714 } | |
715 } | |
716 else | |
717 if (pattern != jsetMaxPatterns) | |
718 { | |
719 dmError("Order 0x%x has invalid pattern number 0x%x!\n", | |
720 i, pattern); | |
721 } | |
722 } | |
723 | |
724 // Find out used instruments | |
725 for (i = 0; i <= jsetMaxInstruments; i++) | |
726 if (usedExtInstruments[i] && m->extInstruments[i] != NULL) | |
727 { | |
728 int note; | |
729 JSSExtInstrument *e = m->extInstruments[i]; | |
730 | |
731 for (note = 0; note < jsetNNotes; note++) | |
732 if (e->sNumForNotes[note] != jsetNotSet) | |
733 { | |
734 int q = e->sNumForNotes[note]; | |
735 if (q >= 0 && q < m->ninstruments) | |
736 { | |
737 usedInstruments[q] = TRUE; | |
738 } | |
739 else | |
740 { | |
741 dmError("Ext.instrument #%d sNumForNotes[%d] value out range (%d < %d).\n", | |
742 i, m->ninstruments, q); | |
743 } | |
744 } | |
745 } | |
746 | |
747 // Create pattern mappings | |
748 r->npatterns = 0; | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
749 dmMsg(1, "Unused patterns: "); |
0 | 750 |
751 for (i = 0; i <= jsetMaxPatterns; i++) | |
752 if (m->patterns[i] != NULL) | |
753 { | |
754 if (!usedPatterns[i]) | |
755 { | |
756 dmPrint(2, "0x%x, ", i); | |
757 } | |
758 else | |
759 { | |
760 if (i >= m->npatterns) | |
761 dmError("Pattern 0x%x >= 0x%x, but used!\n", i, m->npatterns); | |
762 | |
763 mapPatterns[i] = r->npatterns; | |
764 r->patterns[r->npatterns] = m->patterns[i]; | |
765 (r->npatterns)++; | |
766 } | |
767 } | |
768 dmPrint(2, "\n"); | |
769 | |
770 dmMsg(1, "%d used patterns, %d unused.\n", | |
771 r->npatterns, m->npatterns - r->npatterns); | |
772 | |
773 | |
774 // Re-map instruments | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
775 dmMsg(1, "Unused instruments: "); |
0 | 776 for (n8 = n16 = i = 0; i <= jsetMaxInstruments; i++) |
777 if (m->instruments[i] != NULL) | |
778 { | |
779 if (!usedInstruments[i]) | |
780 { | |
781 dmPrint(2, "0x%x, ", i); | |
782 } | |
783 else | |
784 { | |
785 JSSInstrument *ip = m->instruments[i]; | |
786 if (i >= m->ninstruments) | |
787 dmError("Instrument 0x%x >= 0x%x, but used!\n", i, m->ninstruments); | |
788 | |
789 mapInstruments[i] = r->ninstruments; | |
790 r->instruments[r->ninstruments] = ip; | |
791 (r->ninstruments)++; | |
792 | |
793 if (ip->flags & jsf16bit) | |
794 n16++; | |
795 else | |
796 n8++; | |
797 } | |
798 } | |
799 dmPrint(2, "\n"); | |
800 dmMsg(1, "Total of (%d) 16-bit, (%d) 8-bit samples, (%d) instruments.\n", | |
801 n16, n8, r->ninstruments); | |
802 | |
803 // Re-map ext.instruments | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
804 dmMsg(1, "Unused ext.instruments: "); |
0 | 805 for (i = 0; i < jsetMaxInstruments; i++) |
806 if (usedExtInstruments[i]) | |
807 { | |
808 if (i >= m->nextInstruments) | |
809 { | |
810 dmError("Ext.instrument 0x%x >= 0x%x, but used!\n", | |
811 i, m->nextInstruments); | |
812 } | |
813 else | |
814 if (m->extInstruments[i] != NULL) | |
815 { | |
816 JSSExtInstrument *e = m->extInstruments[i]; | |
817 int note; | |
818 | |
819 mapExtInstruments[i] = r->nextInstruments; | |
820 r->extInstruments[r->nextInstruments] = e; | |
821 (r->nextInstruments)++; | |
822 | |
823 // Re-map sNumForNotes | |
824 for (note = 0; note < jsetNNotes; note++) | |
825 { | |
826 int q = e->sNumForNotes[note]; | |
827 if (q != jsetNotSet) | |
828 { | |
829 int map; | |
830 if (q >= 0 && q <= jsetMaxInstruments) | |
831 { | |
832 map = mapInstruments[q]; | |
833 } | |
834 else | |
835 { | |
836 map = jsetNotSet; | |
837 dmError("e=%d, note=%d, q=%d/%d\n", i, note, q, r->ninstruments); | |
838 } | |
839 e->sNumForNotes[note] = map; | |
840 } | |
841 } | |
842 } | |
843 else | |
844 { | |
845 dmPrint(2, "[0x%x==NULL], ", i); | |
846 mapExtInstruments[i] = jsetNotSet; | |
847 } | |
848 } | |
849 else | |
850 { | |
851 if (i < m->nextInstruments && m->extInstruments[i] != NULL) | |
852 { | |
853 dmPrint(2, "0x%x, ", i); | |
854 } | |
855 } | |
856 dmPrint(2, "\n"); | |
857 dmMsg(1, "%d extended instruments.\n", r->nextInstruments); | |
858 | |
859 | |
860 // Remap pattern instrument data | |
861 for (i = 0; i < r->npatterns; i++) | |
862 { | |
863 int row, channel; | |
864 JSSPattern *p = r->patterns[i]; | |
865 JSSNote *n = p->data; | |
866 | |
867 for (row = 0; row < p->nrows; row++) | |
868 for (channel = 0; channel < p->nchannels; channel++, n++) | |
869 if (n->instrument >= 0 && n->instrument <= jsetMaxInstruments) | |
870 { | |
871 n->instrument = mapExtInstruments[n->instrument]; | |
872 } | |
873 } | |
874 | |
875 // Remap orders list | |
876 for (i = 0; i < m->norders; i++) | |
877 { | |
878 r->orderList[i] = mapPatterns[m->orderList[i]]; | |
879 } | |
880 | |
881 return r; | |
882 } | |
883 | |
884 | |
885 int main(int argc, char *argv[]) | |
886 { | |
887 DMResource *sfile = NULL; | |
888 FILE *dfile = NULL; | |
889 JSSModule *sm, *dm; | |
890 int result; | |
891 | |
892 dmInitProg("xm2jss", "XM to JSSMOD converter", "0.6", NULL, NULL); | |
893 dmVerbosity = 0; | |
894 | |
895 // Parse arguments | |
896 if (!dmArgsProcess(argc, argv, optList, optListN, | |
897 argHandleOpt, argHandleFile, TRUE)) | |
898 exit(1); | |
899 | |
900 | |
901 // Read the source file | |
902 if (srcFilename == NULL) | |
903 sfile = dmf_create_stdio_stream(stdin); | |
904 else | |
84
35db15881923
Oops, another fix for dmf_create_stdio() changes.
Matti Hamalainen <ccr@tnsp.org>
parents:
9
diff
changeset
|
905 if ((sfile = dmf_create_stdio(srcFilename, "rb")) == NULL) |
0 | 906 { |
84
35db15881923
Oops, another fix for dmf_create_stdio() changes.
Matti Hamalainen <ccr@tnsp.org>
parents:
9
diff
changeset
|
907 dmError("Error opening input file '%s', %d: %s\n", |
35db15881923
Oops, another fix for dmf_create_stdio() changes.
Matti Hamalainen <ccr@tnsp.org>
parents:
9
diff
changeset
|
908 srcFilename, errno, strerror(errno)); |
0 | 909 return 1; |
910 } | |
911 | |
912 // Initialize miniJSS | |
913 jssInit(); | |
914 | |
915 // Read file | |
916 dmMsg(1, "Reading XM-format file ...\n"); | |
917 result = jssLoadXM(sfile, &sm); | |
918 dmf_close(sfile); | |
919 if (result != 0) | |
920 { | |
921 dmError("Error while loading XM file (%i), ", result); | |
922 if (optIgnoreErrors) | |
923 fprintf(stderr, "ignoring. This may cause problems.\n"); | |
924 else | |
925 { | |
926 fprintf(stderr, "giving up. Use --ignore if you want to try to convert anyway.\n"); | |
927 return 2; | |
928 } | |
929 } | |
930 | |
931 // Check stripping settings | |
932 if (optStripExtInstr) optStripInstr = TRUE; | |
933 if (optStripInstr) optStripSamples = TRUE; | |
934 | |
935 // Remove samples | |
936 if (optStripSamples) | |
937 { | |
938 int i; | |
939 | |
940 dmMsg(1, "Stripping samples...\n"); | |
941 for (i = 0; i < sm->ninstruments; i++) | |
942 { | |
943 dmFree(sm->instruments[i]->data); | |
944 sm->instruments[i]->data = NULL; | |
945 } | |
946 } | |
947 | |
948 // Remove instruments | |
949 if (optStripInstr) | |
950 { | |
951 int i; | |
952 | |
953 dmMsg(1, "Stripping instruments...\n"); | |
954 for (i = 0; i < sm->ninstruments; i++) | |
955 { | |
956 dmFree(sm->instruments[i]); | |
957 sm->instruments[i] = NULL; | |
958 } | |
959 sm->ninstruments = 0; | |
960 } | |
961 | |
962 // Remove ext.instruments | |
963 if (optStripExtInstr) | |
964 { | |
965 int i; | |
966 | |
967 dmMsg(1, "Stripping ext.instruments...\n"); | |
968 for (i = 0; i < sm->nextInstruments; i++) | |
969 { | |
970 dmFree(sm->extInstruments[i]); | |
971 sm->extInstruments[i] = NULL; | |
972 } | |
973 sm->nextInstruments = 0; | |
974 } | |
975 // Run the optimization procedure | |
976 if (optOptimize) | |
977 { | |
978 dmMsg(1, "Optimizing module data...\n"); | |
979 dm = optimizeModule(sm); | |
980 } else | |
981 dm = sm; | |
982 | |
983 // Write output file | |
984 if (destFilename == NULL) | |
985 dfile = stdout; | |
986 else if ((dfile = fopen(destFilename, "wb")) == NULL) | |
987 { | |
988 dmError("Error creating output file '%s'. (%s)\n", destFilename, strerror(errno)); | |
989 return 1; | |
990 } | |
991 | |
9
c42ee907de9c
Various improvements in xm2jss output.
Matti Hamalainen <ccr@tnsp.org>
parents:
0
diff
changeset
|
992 dmMsg(1, "Writing JSSMOD-format file [patMode=0x%04x, samp8=0x%02x, samp16=0x%02x]\n", |
0 | 993 optPatternMode, optSampMode8, optSampMode16); |
994 | |
995 result = jssSaveJSSMOD(dfile, dm, optPatternMode, optSampMode8, optSampMode16); | |
996 | |
997 fclose(dfile); | |
998 | |
999 if (result != 0) | |
1000 { | |
1001 dmError("Error while saving JSSMOD file (%i), the resulting file may be broken!\n", result); | |
1002 } | |
1003 | |
1004 dmMsg(1, "Conversion complete.\n"); | |
1005 return 0; | |
1006 } |