1 /* OpenCP Module Player
2 * copyright (c) 2019 Stian Skjelstad <stian.skjelstad@gmail.com>
3 *
4 * Utility: Dumping the raw contents of a AHX file
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21
22 #include "config.h"
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <getopt.h>
26 #include <string.h>
27 #include <stdio.h>
28 #include <sys/mman.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include "types.h"
33
34 #define roundup(x,y) (((x) + (y) - 1) & ~((y) - 1))
35
36 int usecolor = 0;
37
38 char *FONT_RESET = "";
39 char *FONT_BRIGHT_BLACK = "";
40 char *FONT_BRIGHT_RED = "";
41 char *FONT_BRIGHT_GREEN = "";
42 char *FONT_BRIGHT_YELLOW = "";
43 char *FONT_BRIGHT_BLUE = "";
44 char *FONT_BRIGHT_PURPLE = "";
45 char *FONT_BRIGHT_CYAN = "";
46
DumpPrefix(unsigned char * mem,int len,int base,int baselen)47 int DumpPrefix (unsigned char *mem, int len, int base, int baselen)
48 {
49 int retval = 0;
50 int i;
51 printf ("[%s%08x%s]%s", FONT_BRIGHT_BLUE, base, FONT_RESET, FONT_BRIGHT_PURPLE);
52 for (i=0; i < baselen; i++)
53 {
54 if (base+i >= len)
55 {
56 printf (" \?\?");
57 retval = -1;
58 } else {
59 printf (" %02x", mem[base+i]);
60 }
61 }
62 switch (baselen)
63 {
64 case 3: printf ( "%s ", FONT_RESET); break;
65 case 2: printf ( "%s ", FONT_RESET); break;
66 case 1: printf ( "%s ", FONT_RESET); break;
67 case 0: printf ( "%s ", FONT_RESET); break;
68 default: printf ("%s\n ", FONT_RESET); break;
69 }
70 return retval;
71 }
72
73 uint8_t SMP; // Number of samples
74
75 uint8_t SS; // SubSongs
76 uint16_t LEN; // Number of orders (each order has 4 track references with transpose tagged to it)
77 uint8_t TRK; // Number of tracks
78 uint8_t TRL; // Length of each track / rows
79
80 int HaveTrack0; // True if Track 0 is stored in the file
81
DumpHeader(unsigned char * mem,int len)82 int DumpHeader (unsigned char *mem, int len)
83 {
84 int i;
85 int isAHX1 = 0;
86 uint16_t RES;
87
88 printf ("[%sHEADER%s]\n", FONT_BRIGHT_CYAN, FONT_RESET);
89
90 if (len < 14)
91 {
92 fprintf (stderr, "%sERROR: len < sizeof(FileHeader)%s\n", FONT_BRIGHT_RED, FONT_RESET);
93 return 1;
94 }
95
96 /* hdr.name */
97 DumpPrefix (mem, len, 0x00, 4);
98 printf ("Signature: \"");
99 for (i=0; i < 4; i++)
100 {
101 if ((mem[0x00+i] & 0xc0) == 0x40)
102 {
103 printf ("%c", (char)mem[0x00+i]);
104 } else if (mem[0x00+i] < 9)
105 {
106 printf ("\\%d", mem[0x00+i]);
107 } else {
108 printf ("%s\\x%02x%s", FONT_BRIGHT_RED, mem[0x00+i], FONT_RESET);
109 }
110 }
111 printf ("\" - ");
112 if (!memcmp(mem + 0x00, "THX\0", 4))
113 {
114 printf ("%sAHX version 0.00 - 1.27%s\n", FONT_BRIGHT_GREEN, FONT_RESET);
115 } else if (!memcmp (mem + 0x00, "THX\1", 4))
116 {
117 printf ("%sAHX version >= 2.00%s\n", FONT_BRIGHT_GREEN, FONT_RESET);
118 isAHX1 = 1;
119 } else {
120 printf ("%sUnknown%s\n", FONT_BRIGHT_RED, FONT_RESET);
121 }
122
123 DumpPrefix (mem, len, 0x04, 2);
124 printf ("Titles and Sample names is stored at ptr [%s0xXXXX%04x%s]\n", FONT_BRIGHT_BLUE, (mem[0x04]<<8)|mem[0x05], FONT_RESET);
125
126 DumpPrefix (mem, len, 0x06, 1);
127 HaveTrack0 = !(mem[0x06] & 0x80);
128 printf ("Track 0 present: %d\n", HaveTrack0);
129 if (isAHX1)
130 {
131 printf (" Speed: ");
132 switch (mem[0x06] & 0x70)
133 {
134 case 0x00: printf ("50Hz\n"); break;
135 case 0x10: printf ("100Hz\n"); break;
136 case 0x20: printf ("150Hz\n"); break;
137 case 0x30: printf ("200Hz\n"); break;
138 default: printf ("%sInvalid value%s\n", FONT_BRIGHT_RED, FONT_RESET); break;
139 }
140 }
141
142 DumpPrefix (mem, len, 0x07, 1);
143 LEN = ((mem[0x06] << 8) | mem[0x07]) & 0xfff;
144 if ((LEN >= 1) && (LEN <= 999))
145 {
146 printf ("LEN (number of positions): %d # Orders\n", LEN);
147 } else {
148 printf ("LEN (number of positions): %s%d OUT OF RANGE%s # Orders\n", FONT_BRIGHT_RED, LEN, FONT_RESET);
149 }
150
151 DumpPrefix (mem, len, 0x08, 2);
152 RES = ((mem[0x08] << 8) | mem[0x09]);
153 if (RES < LEN)
154 {
155 printf ("RES (loop position): %d\n", LEN);
156 } else {
157 printf ("RES (loop position): %s%d OUT OF RANGE%s\n", FONT_BRIGHT_RED, LEN, FONT_RESET);
158 }
159
160 DumpPrefix (mem, len, 0x0a, 1);
161 TRL = mem[0x0a];
162 if ((TRL >= 1) && (TRL <= 64))
163 {
164 printf ("TRL (track length): %d # Pattern Length\n", TRL);
165 } else {
166 printf ("TRL (track length): %s%d OUT OF RANGE # Pattern Length%s\n", FONT_BRIGHT_RED, TRL, FONT_RESET);
167 }
168
169 DumpPrefix (mem, len, 0x0b, 1);
170 TRK = mem[0x0b];
171 printf ("TRK (number of tracks saved): %d # Patterns\n", (int)TRK);
172
173 DumpPrefix (mem, len, 0x0c, 1);
174 SMP = mem[0x0c];
175 if (SMP <= 63)
176 {
177 printf ("SMP (samples stored): %d\n", SMP);
178 } else {
179 printf ("SMP (samples stored): %s%d OUT OF RANGE%s\n", FONT_BRIGHT_RED, SMP, FONT_RESET);
180 }
181
182 DumpPrefix (mem, len, 0x0d, 1);
183 SS = mem[0x0d];
184 printf ("SS (number of subsongs): %d\n", SS);
185
186 return 0;
187 }
188
DumpSubSongs(unsigned char * mem,int offset,int len)189 int DumpSubSongs (unsigned char *mem, int offset, int len)
190 {
191 int i;
192 printf ("[%sSUBSONGS%s]\n", FONT_BRIGHT_CYAN, FONT_RESET);
193 for (i=0; i < SS; i++)
194 {
195 uint16_t SL;
196 DumpPrefix (mem, len, offset + (i<<1), 2);
197 if (len < offset + (i<<1) + 2)
198 {
199 fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET);
200 return 1;
201 }
202 SL = ((mem[offset+(i<<1)] << 8) | mem[offset+(i<<1)+1]);
203
204 if (SL >= LEN)
205 {
206 printf ("SubSong %d: Starts at position %s0x%04x # OUT OF RANGE, must be <LEN%s\n", i, FONT_BRIGHT_RED, SL, FONT_RESET);
207 } else {
208 printf ("SubSong %d: Starts at position 0x%04x\n", i, SL);
209 }
210
211 }
212 return 0;
213 }
214
DumpPositions(unsigned char * mem,int offset,int len)215 int DumpPositions (unsigned char *mem, int offset, int len)
216 {
217 int i, j;
218 printf ("[%sPOSITIONS (Orders)%s]\n", FONT_BRIGHT_CYAN, FONT_RESET);
219 for (i=0; i < LEN; i++)
220 {
221 int error = 0;
222
223 if (DumpPrefix (mem, len, offset + (i<<3), 8))
224 {
225 fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET);
226 return 1;
227 }
228
229 printf ("Position %3d: ", i);
230 for (j=0; j < 4; j++)
231 {
232 uint8_t Track = mem[offset + (i<<3) + (j<<1)];
233 int8_t Transpose = mem[offset + (i<<3) + (j<<1) + 1];
234 if (Track > TRK)
235 {
236 error = 1;
237 printf (" | %s%3d%s transpose %-4d", FONT_BRIGHT_RED, Track, FONT_RESET, Transpose);
238 } else {
239 printf (" | %3d transpose %-4d", Track, Transpose);
240 }
241 }
242 if (error)
243 {
244 printf (" | %s# TRACK REFERENCE OUT OF RANGE%s\n", FONT_BRIGHT_RED, FONT_RESET);
245 } else {
246 printf (" |\n");
247 }
248 }
249 return 0;
250 }
251
print_note(uint8_t note)252 void print_note (uint8_t note)
253 {
254 char *N[12] = {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"};
255 if (!note)
256 {
257 printf("...");
258 return;
259 }
260 if (note >= 61)
261 {
262 printf("%s???%s", FONT_BRIGHT_RED, FONT_RESET);
263 return;
264 }
265 note--;
266 printf ("%s%d", N[note%12], (note / 12)+1);
267 }
268
print_sample(uint8_t sample)269 void print_sample (uint8_t sample)
270 {
271 if (!sample)
272 {
273 printf ("..");
274 return;
275 }
276 printf ("%02d", sample);
277 }
278
279 /* Commands
280 0 00 no-op
281 0 .x Position jump hi
282 1 xx Portamento up (period slide down)
283 2 xx Portamento down
284 3 xx Tone portamento
285 4 xx Filter override
286 5 xx Volume Slide + Tone Portamento
287 6 ????????????
288 7 xx Panning
289 8 ????????????
290 9 xx Set squarewave offset
291 A xx Volume Slide
292 B xx Position jump
293 C xx Volume
294 D xx Pattern Break
295 E 0x ?????????????
296 E 1x Fineslide up
297 E 2x Fineslide down
298 E 3x ?????????????
299 E 4x Vibrato control
300 E 5x ?????????????
301 E 6x ?????????????
302 E 7x ?????????????
303 E 8x ?????????????
304 E 9x ?????????????
305 E Ax Fine volume up
306 E Bx Fine volume down
307 E Cx Note cut
308 E Dx Note delay
309 E Ex ?????????????
310 E Fx Misc flags
311 F xx Speed
312 */
313
print_command(uint8_t cmd,uint8_t info)314 void print_command (uint8_t cmd, uint8_t info)
315 {
316 if ((cmd == 0) && (info == 0))
317 {
318 printf ("...");
319 } else {
320 printf ("%X%02X", cmd, info);
321 }
322 }
323
DumpTracks(unsigned char * mem,int offset,int len)324 int DumpTracks (unsigned char *mem, int offset, int len)
325 {
326 int i, j;
327 printf ("[%sTRACKS (Patterns)%s]\n", FONT_BRIGHT_CYAN, FONT_RESET);
328 for (i=(HaveTrack0?0:1); i <= TRK; i++)
329 {
330 printf ("Track: %d\n", i);
331 for (j=0; j < TRL; j++)
332 {
333 uint8_t note;
334 uint8_t sample;
335 uint8_t command;
336 uint8_t data;
337
338 if (DumpPrefix (mem, len, offset, 3))
339 {
340 fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET);
341 return 1;
342 }
343 note = mem[offset ] >> 2;
344 sample = ((mem[offset ] << 4) & 0x30) |
345 (mem[offset + 1] >> 4);
346 command = mem[offset + 1] & 0x0f;
347 data = mem[offset + 2];
348
349 offset += 3;
350
351 printf ("%02x: ", j);
352 print_note (note);
353 printf (" ");
354 print_sample (sample);
355 printf (" ");
356 print_command (command, data);
357 printf ("\n");
358 }
359 }
360 return 0;
361 }
362
DumpSamples(unsigned char * mem,int * offset,int len)363 int DumpSamples (unsigned char *mem, int *offset, int len)
364 {
365 int i;
366
367 printf ("[%sSAMPLES (Instruments)%s]\n", FONT_BRIGHT_CYAN, FONT_RESET);
368
369 for (i=0; i < SMP; i++)
370 {
371 uint8_t square_modulation_lower_limit_min = 1;
372 uint8_t PLEN;
373 int j;
374
375 printf ("Sample: %d\n", i+1);
376
377 if (DumpPrefix (mem, len, *offset, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
378 if (mem[*offset+0] > 64)
379 {
380 printf ("Master volume: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+0], FONT_RESET);
381 } else {
382 printf ("Master volume: %d\n", mem[*offset+0]);
383 }
384
385 if (DumpPrefix (mem, len, *offset+1, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
386 if ((mem[*offset+1] & 7) > 5)
387 {
388 printf ("WaveLen: %s[%d]?? # OUT OF RANGE%s\n", FONT_BRIGHT_RED, 4<<(mem[*offset+1] & 7), FONT_RESET);
389 } else {
390 printf ("WaveLen: %d\n", 4<<(mem[*offset+1] & 7));
391 square_modulation_lower_limit_min = 32 >> (mem[*offset+1] & 7);
392 }
393
394 if (DumpPrefix (mem, len, *offset+2, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
395 if (mem[*offset+2] == 0)
396 {
397 printf ("Attack Length: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+2], FONT_RESET);
398 } else {
399 printf ("Attack Length: %d\n", mem[*offset+2]);
400 }
401
402 if (DumpPrefix (mem, len, *offset+3, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
403 if (mem[*offset+3] > 64)
404 {
405 printf ("Attack volume: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+3], FONT_RESET);
406 } else {
407 printf ("Attack volume: %d\n", mem[*offset+3]);
408 }
409
410 if (DumpPrefix (mem, len, *offset+4, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
411 if (mem[*offset+4] == 0)
412 {
413 printf ("Decay Length: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+4], FONT_RESET);
414 } else {
415 printf ("Decay Length: %d\n", mem[*offset+4]);
416 }
417
418 if (DumpPrefix (mem, len, *offset+5, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
419 if (mem[*offset+5] > 64)
420 {
421 printf ("Decay volume: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+5], FONT_RESET);
422 } else {
423 printf ("Decay volume: %d\n", mem[*offset+5]);
424 }
425
426 if (DumpPrefix (mem, len, *offset+6, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
427 if (mem[*offset+6] == 0)
428 {
429 printf ("Sustain Length: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+6], FONT_RESET);
430 } else {
431 printf ("Sustain Length: %d\n", mem[*offset+6]);
432 }
433
434 if (DumpPrefix (mem, len, *offset+7, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
435 if (mem[*offset+7] == 0)
436 {
437 printf ("Release Length: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+7], FONT_RESET);
438 } else {
439 printf ("Release Length: %d\n", mem[*offset+7]);
440 }
441
442 if (DumpPrefix (mem, len, *offset+8, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
443 if (mem[*offset+8] > 64)
444 {
445 printf ("Release volume: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+8], FONT_RESET);
446 } else {
447 printf ("Release volume: %d\n", mem[*offset+8]);
448 }
449
450 if (DumpPrefix (mem, len, *offset+9, 3)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
451 printf ("Reserved\n");
452
453 if (DumpPrefix (mem, len, *offset+12, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
454 /* must be all zero for AHX0 */
455 if ((!(mem[*offset+12] & 0x7f)) || ((mem[*offset+12] & 0x7f) > 63))
456 {
457 printf ("Filter modulation lower limit: %s %d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+12] & 0x7f, FONT_RESET);
458 } else {
459 printf ("Filter modulation lower limit: %d\n", mem[*offset+12] & 0x7f);
460 }
461
462 if (DumpPrefix (mem, len, *offset+13, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
463 printf ("Vibrato Delay: %d\n", mem[*offset+13]);
464
465 if (DumpPrefix (mem, len, *offset+14, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
466 if (mem[*offset+14] & 0x80) /* must be all zero for AHX0 */
467 {
468 printf ("Hardcut: enabled %d\n", (mem[*offset+14] >> 4) & 7);
469 } else {
470 printf ("Hardcut: disabled (%d)\n", (mem[*offset+14] >> 4) & 7);
471 }
472 printf (" Vibrato: %d\n", mem[*offset+14] & 15);
473
474 if (DumpPrefix (mem, len, *offset+15, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
475 if (mem[*offset+15] > 63)
476 {
477 printf ("Vibrato: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+15], FONT_RESET);
478 } else {
479 printf ("Vibrato: %d\n", mem[*offset+15]);
480 }
481
482 if (DumpPrefix (mem, len, *offset+16, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
483 if ((mem[*offset+16] > 63) || (mem[*offset+16] < square_modulation_lower_limit_min))
484 {
485 printf ("Square modulation lower limit: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+16], FONT_RESET);
486 } else {
487 printf ("Square modulation lower limit: %d\n", mem[*offset+16]);
488 }
489
490 if (DumpPrefix (mem, len, *offset+17, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
491 if ((mem[*offset+17] > 63) || (mem[*offset+17] < mem[*offset+16]))
492 {
493 printf ("Square modulation upper limit: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+17], FONT_RESET);
494 } else {
495 printf ("Square modulation upper limit: %d\n", mem[*offset+17]);
496 }
497
498 if (DumpPrefix (mem, len, *offset+18, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
499 printf ("Square modulation speed: %d\n", mem[*offset+18]);
500
501 if (DumpPrefix (mem, len, *offset+19, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
502 /* must be all zero for AHX0 */
503 if ((!(mem[*offset+19] & 0x7f)) || ((mem[*offset+19] & 0x7f) > 63))
504 {
505 printf ("Filter modulation upper limit: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+19], FONT_RESET);
506 } else {
507 printf ("Filter modulation upper limit: %d\n", mem[*offset+19]);
508 }
509
510 /* Must be all zero for AHX0 */
511 printf (" Filter modulation speed: %d\n",
512 ((mem[*offset+ 1] & 0xf8) >> 3) |
513 ((mem[*offset+12] & 0x80) >> 2) |
514 ((mem[*offset+19] & 0x80) >> 1));
515
516 if (DumpPrefix (mem, len, *offset+20, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
517 /* must be all zero for AHX0 */
518 if (!mem[*offset+20])
519 {
520 printf ("Playlist default speed: %s%d # OUT OF RANGE%s\n", FONT_BRIGHT_RED, mem[*offset+20], FONT_RESET);
521 } else {
522 printf ("Playlist default speed: %d\n", mem[*offset+20]);
523 }
524
525 if (DumpPrefix (mem, len, *offset+21, 1)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
526 if (!mem[*offset+21])
527 {
528 printf ("Playlist length: 0 # EMPTY SAMPLE, IGNORE\n");
529 *offset += 22;
530 continue;
531 } else {
532 PLEN = mem[*offset+21];
533 printf ("Playlist length: %d\n", mem[*offset+21]);
534 *offset += 22;
535 }
536 for (j=0; j < PLEN; j++)
537 {
538 int noteon, note, waveform, fx1, fx2, info1, info2;
539
540 if (DumpPrefix (mem, len, *offset, 4)) { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
541
542 fx2 = ((mem[*offset + 0] & 0xe0) >> 5);
543 fx1 = ((mem[*offset + 0] & 0x1c) >> 2);
544 waveform = ((mem[*offset + 0] & 0x03) << 1) |
545 ((mem[*offset + 1] & 0x80) >> 7);
546 noteon = mem[*offset + 1] & 0x40;
547 note = mem[*offset + 1] & 0x3f;
548 info1 = mem[*offset + 2];
549 info2 = mem[*offset + 3];
550
551 *offset += 4;
552
553 printf ("%03d | %c", j, noteon ? '*' : '.');
554 print_note (note);
555 if (waveform > 4)
556 {
557 printf (" %s%d%s", FONT_BRIGHT_RED, waveform, FONT_RESET);
558 } else {
559 printf (" %d", waveform);
560 }
561
562 if ((!fx1) && (!info1))
563 {
564 printf (" ...");
565 } else {
566 printf (" %d%02X", fx1, info1);
567 }
568
569 if ((!fx2) && (!info2))
570 {
571 printf (" ...");
572 } else {
573 printf (" %d%02X", fx2, info2);
574 }
575
576 printf (" |\n");
577 }
578 }
579
580 return 0;
581 }
582
DumpStrings(unsigned char * mem,int * offset,int len)583 int DumpStrings (unsigned char *mem, int *offset, int len)
584 {
585 int ptr, i;
586
587 printf ("[%sSTRINGS%s]\n", FONT_BRIGHT_CYAN, FONT_RESET);
588
589 for (ptr=*offset; ;ptr++)
590 {
591 if (ptr > len)
592 {
593 { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
594 }
595 if (!mem[ptr])
596 {
597 break;
598 }
599 }
600 DumpPrefix (mem, len, *offset, ptr-(*offset)+1);
601 printf ("Title: \"");
602 for (ptr=*offset; mem[ptr] ;ptr++)
603 {
604 if ((mem[ptr] < 0x20) || (mem[ptr] == 127))
605 {
606 printf ("%s\\x%02x%s", FONT_BRIGHT_RED, mem[ptr], FONT_RESET);
607 } else if (mem[ptr] >= 128)
608 {
609 printf ("\\x%02x", mem[ptr]);
610 } else {
611 printf ("%c", mem[ptr]);
612 }
613 }
614 printf ("\"\n");
615 *offset = ptr + 1;
616
617 for (i=0; i < SMP; i++)
618 {
619 for (ptr=*offset; ;ptr++)
620 {
621 if (ptr > len)
622 {
623 { fprintf (stderr, "\n%sERROR: ran out of data%s\n", FONT_BRIGHT_RED, FONT_RESET); return 1; }
624 }
625 if (!mem[ptr])
626 {
627 break;
628 }
629 }
630 DumpPrefix (mem, len, *offset, ptr-(*offset)+1);
631 printf ("Sample %2d: \"", i + 1);
632 for (ptr=*offset; mem[ptr] ;ptr++)
633 {
634 if ((mem[ptr] < 0x20) || (mem[ptr] == 127))
635 {
636 printf ("%s\\x%02x%s", FONT_BRIGHT_RED, mem[ptr], FONT_RESET);
637 } else if (mem[ptr] >= 128)
638 {
639 printf ("\\x%02x", mem[ptr]);
640 } else {
641 printf ("%c", mem[ptr]);
642 }
643 }
644 printf ("\"\n");
645 *offset = ptr + 1;
646 }
647
648 return 0;
649 }
650
ParseAHX(unsigned char * mem,int len)651 int ParseAHX (unsigned char *mem, int len)
652 {
653 int offset;
654
655 if (DumpHeader (mem, len))
656 {
657 return -1;
658 }
659 if (DumpSubSongs (mem, 14, len))
660 {
661 return -1;
662 }
663 if (DumpPositions (mem, 14+SS*2, len))
664 {
665 return -1;
666 }
667 if (DumpTracks (mem, 14+SS*2 + LEN*8, len))
668 {
669 return -1;
670 }
671 offset = 14+SS*2 + LEN*8 + (TRK+HaveTrack0)*TRL*3;
672 if (DumpSamples (mem, &offset, len))
673 {
674 return -1;
675 }
676 if (DumpStrings (mem, &offset, len))
677 {
678 return -1;
679 }
680
681 return 0;
682 }
683
684
main(int argc,char * argv[])685 int main(int argc, char *argv[])
686 {
687 struct stat st;
688 size_t ps = sysconf(_SC_PAGE_SIZE);
689 int fd;
690 size_t data_mmaped_len;
691 unsigned char *data;
692 int c;
693 char *color = "auto";
694 int help = 0;
695
696 while (1)
697 {
698 int option_index = 0;
699 static struct option long_options[] =
700 {
701 {"color", optional_argument, 0, 0},
702 {"help", no_argument, 0, 'h'},
703 {0, 0, 0, 0}
704 };
705
706 c = getopt_long(argc, argv, "h", long_options, &option_index);
707 if (c == -1)
708 break;
709
710 switch (c)
711 {
712 case 0:
713 if (option_index == 0)
714 {
715 color = optarg;
716 }
717 break;
718 case 'h':
719 help = 1;
720 break;
721 case '?':
722 help = 3;
723 break;
724 default:
725 printf("?? getopt returned character code 0%o ??\n", c);
726 }
727 }
728
729 if (optind != (argc-1))
730 {
731 help = 4;
732 }
733
734 if (!color)
735 {
736 usecolor = 1;
737 } else if (!strcmp (color, "auto"))
738 {
739 usecolor = isatty ( 1 );
740 } else if ((strcmp (color, "never")) && (strcmp (color, "no")))
741 {
742 usecolor = 1;
743 } else {
744 usecolor = 0;
745 }
746
747 if (help)
748 {
749 fprintf (stderr, "Usage:\n%s [--color=auto/never/on] [--savesamples -s] [--savepatterns -p] [--help] file.stm (%d)\n", argv[0], help);
750 return 1;
751 }
752
753 fd = open (argv[optind], O_RDONLY);
754 if (fd < 0)
755 {
756 perror ("open()");
757 return 0;
758 }
759 if (fstat(fd, &st))
760 {
761 perror("fstat()");
762 close (fd);
763 return 0;
764 }
765 if (!st.st_size)
766 {
767 fprintf (stderr, "Zero-size file\n");
768 close (fd);
769 return 0;
770 }
771
772 // s.data_len = st.st_size;
773 data_mmaped_len = roundup (st.st_size, ps);
774 data = mmap (0, data_mmaped_len, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0);
775
776 if (data == MAP_FAILED)
777 {
778 perror ("mmap() failed");
779 close (fd);
780 return 0;
781 }
782
783 if (usecolor)
784 {
785 FONT_RESET = "\033[0m";
786 FONT_BRIGHT_BLACK = "\033[30;1m";
787 FONT_BRIGHT_RED = "\033[31;1m";
788 FONT_BRIGHT_GREEN = "\033[32;1m";
789 FONT_BRIGHT_YELLOW = "\033[33;1m";
790 FONT_BRIGHT_BLUE = "\033[34;1m";
791 FONT_BRIGHT_PURPLE = "\033[35;1m";
792 FONT_BRIGHT_CYAN = "\033[36;1m";
793 }
794
795 if (ParseAHX (data, st.st_size))
796 {
797 goto failed;
798 }
799
800 failed:
801 munmap (data, data_mmaped_len);
802 close (fd);
803 return 0;
804 }
805