added apex audio system
[gbajam21] / tools / conv2aas / conv2aas.c
1 /* Copyright (c) 2003-2021 James Daniels */
2 /* Distributed under the MIT License */
3 /* license terms: see LICENSE file in root or http://opensource.org/licenses/MIT */
4
5 /* Notes: */
6 /*  + Max number of MODs = 256 */
7 /*  + Max total size of all samples = 33,554,432 bytes */
8 /*  + Max total size of all patterns = 33,554,432 bytes */
9 /*  + All functions declared static to work around problem with GCC */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <dirent.h>
16
17 #define BYTE signed char
18 #define UBYTE unsigned char
19 #define BOOL unsigned char
20 #define UWORD unsigned short
21 #define ULONG unsigned int
22 #define WORD signed short
23 #define TRUE 1
24 #define FALSE 0
25
26 __inline static int Min(int a, int b)
27 {
28         if (a < b)
29                 return a;
30         else
31                 return b;
32 }
33
34 __inline static UWORD SwapUWORD( UWORD a )
35 {
36         return (a>>8)+((a&0xff)<<8);
37 }
38
39 static int memcmp_new( UBYTE* a, UBYTE* b, int length )
40 {
41         for(; length > 0; --length )
42                 if ( *a++ != *b++ )
43                         return 1;
44
45         return 0;
46 }
47
48 static void memcpy_new( UBYTE* dest, UBYTE* source, int length )
49 {
50         for(; length > 0; --length )
51                 *dest++ = *source++;
52 }
53
54 #define MAX_DATA_LENGTH 33554432
55 typedef struct data_pack
56 {
57         const char* chunk_name;
58         int chunk_data_length;
59         int chunk_unique;
60         int chunk_actual;
61         unsigned char chunk_data[MAX_DATA_LENGTH];
62 } DATAPACK;
63
64 static DATAPACK* DATA_Create( const char* name )
65 {
66         DATAPACK* new_data = (DATAPACK*)malloc(sizeof(DATAPACK));
67
68         if ( new_data )
69         {
70                 /*printf( "DATA_Create( %s ): addr:%p\n", name, new_data ); */
71
72                 new_data->chunk_name = name;
73                 new_data->chunk_data_length = 0;
74                 new_data->chunk_unique = 0;
75                 new_data->chunk_actual = 0;
76         }
77         else
78         {
79                 printf( "DATA_Create( %s ): Failed (address:%p length:%d)...\n", name, (void*)new_data, (int)sizeof(DATAPACK) );
80         }
81
82         return new_data;
83 }
84
85 static int DATA_AppendAligned( DATAPACK* curr_data, unsigned char* data, int length, int block_length )
86 {
87         ++curr_data->chunk_actual;
88
89         if ( (block_length*((int)(length/block_length))) != length )
90         {
91                 printf( "DATA_AppendAligned( %p, %p, %d, %d ): Warning length not exactly divisible by block_length!\n",
92                                 (void*)curr_data, data, length, block_length );
93                 return 0;
94         }
95         else if ( length == 0 )
96         {
97                 return 0;
98         }
99         else
100         {
101                 int offset = 0;
102
103                 while ( (offset + block_length) <= curr_data->chunk_data_length )
104                 {
105                         if ( memcmp( data, curr_data->chunk_data + offset, length ) == 0 )
106                         {
107                                 return offset/block_length;
108                         }
109
110                         offset += block_length;
111                 };
112
113                 if ( curr_data->chunk_data_length <= (MAX_DATA_LENGTH-length) )
114                 {
115                         memcpy( curr_data->chunk_data + offset, data, length );
116                         curr_data->chunk_data_length += length;
117                         ++curr_data->chunk_unique;
118                         return offset/block_length;
119                 }
120                 else
121                 {
122                         --curr_data->chunk_actual;
123                         printf( "DATA_AppendAligned( %p, %p, %d, %d ): Data >%d bytes!\n",
124                                         (void*)curr_data, data, length, block_length, MAX_DATA_LENGTH );
125                         return 0;
126                 }
127         }
128 }
129
130 static int DATA_Append( DATAPACK* curr_data, unsigned char* data, int length )
131 {
132         ++curr_data->chunk_actual;
133
134         if ( length == 0 )
135         {
136                 return 0;
137         }
138         else
139         {
140                 int offset = 0;
141
142                 while ( (offset + length) <= curr_data->chunk_data_length )
143                 {
144                         if ( memcmp_new( data, curr_data->chunk_data + offset, length ) == 0 )
145                                 return offset;
146                         ++offset;
147                 };
148
149                 if ( curr_data->chunk_data_length <= (MAX_DATA_LENGTH-length) )
150                 {
151                         offset = curr_data->chunk_data_length;
152                         memcpy_new( curr_data->chunk_data + offset, data, length );
153                         curr_data->chunk_data_length += length;
154                         ++curr_data->chunk_unique;
155                         return offset;
156                 }
157                 else
158                 {
159                         --curr_data->chunk_actual;
160                         printf( "DATA_Append( %p, %d ): Data >%d bytes!\n", data, length, MAX_DATA_LENGTH );
161                         return 0;
162                 }
163         }
164 }
165
166 static void DATA_Write( DATAPACK* curr_data, FILE* out_file )
167 {
168         int i, len;
169         UBYTE val;
170
171         len = curr_data->chunk_data_length;
172         if ( len & 0x3 )
173         {
174                 len &= ~0x3;
175                 len += 0x4;
176         }
177
178         printf( "Writing %s (Unique:%d Actual:%d Length:%d)...", curr_data->chunk_name, curr_data->chunk_unique, curr_data->chunk_actual, len );
179
180         fprintf( out_file, "\n.ALIGN\n.GLOBAL %s\n%s:\n.byte", curr_data->chunk_name, curr_data->chunk_name );
181
182         if ( len > 0 )
183         {
184                 for( i = 0; i < len; ++i )
185                 {
186                         if ( i >= curr_data->chunk_data_length )
187                                 val = 0;
188                         else
189                                 val = *(curr_data->chunk_data + i);
190                         if ( i == 0 )
191                                 fprintf( out_file, " %d", val );
192                         else
193                                 fprintf( out_file, ", %d", val );
194                 }
195                 fprintf( out_file, "\n" );
196         }
197         else
198         {
199                 fprintf( out_file, " 0\n" );
200         }
201
202         printf( "Done!\n" );
203
204         free( curr_data );
205 }
206
207 struct ModSampleTemp
208 {
209         UWORD repeat; /* in halfwords */
210         UWORD length; /* in halfwords */
211         UWORD truelen; /* in halfwords */
212         UBYTE volume;
213         UBYTE finetune;
214 };
215
216 struct ModSample
217 {
218         ULONG data; /* offset in bytes */
219         UWORD repeat; /* in halfwords */
220         UWORD length; /* in halfwords */
221         UBYTE finetune;
222         UBYTE volume;
223 };
224
225 static void MISC_ProcessSound( BYTE* data, int length )
226 {
227         for(; length > 0; --length )
228         {
229                 int val = *data;
230
231                 if ( val == -128 )
232                         val = -127;
233
234                 *data++ = val;
235         }
236 }
237
238 static void MISC_ConvSoundToSigned( UBYTE* data, int length )
239 {
240         BYTE* bdata = (BYTE*)data;
241         for(; length > 0; --length )
242         {
243                 int val = *data;
244                 val -= 128;
245                 *bdata++ = val;
246         }
247 }
248
249 static void MOD_LoadSound( struct ModSample* smp, FILE* in_file, DATAPACK* samples, int length, int truelen, int repeat )
250 {
251         BYTE* samp = malloc( (length<<1)+16 );
252         int data;
253
254         fread( samp+16, (length<<1), 1, in_file );
255
256         if ( truelen > 0 )
257         {
258                 memset( samp, 0, 16 );
259                 if ( (repeat < 65535) && (truelen > repeat) )
260                         memcpy( samp+(repeat<<1), samp+(truelen<<1), 16 ); /* should really merge, also wasteful of memory */
261                 MISC_ProcessSound( samp, (truelen<<1)+16 );
262                 data = DATA_Append( samples, (UBYTE*)samp, (truelen<<1)+16 );
263                 smp->data = data+16;
264         }
265         else
266         {
267                 smp->data = 0;
268         }
269         smp->length = truelen;
270
271         free( samp );
272 }
273
274 #define MAX_MODS 256
275
276 static struct ModSample samps[MAX_MODS][31];
277 static WORD patts[MAX_MODS][128][16]; /* [mod][pattern][channel] */
278 static UBYTE mod_num_chans[MAX_MODS];
279 static UBYTE mod_song_restart_pos[MAX_MODS];
280 static int max_song_length = 0;
281
282 static const char* effect_name[32] = { "*0: Arpeggio", "*1: Slide Up", "*2: Slide Down", "*3: Tone Portamento", "*4: Vibrato", "*5: Tone Portamento + Volume Slide", "*6: Vibrato + Volume Slide", "*7: Tremolo", "8: Set Panning Position", "*9: Set Sample Offset", "*A: Volume Slide", "*B: Position Jump", "*C: Set Volume", "*D: Pattern Break", "*F: Set Speed (BPM)", "*F: Set Speed (Ticks)", "*E0: Set Filter", "*E1: Fine Slide Up", "*E2: Fine Slide Down", "E3: Glissando Control", "E4: Set Vibrato Waveform", "E5: Set FineTune", "*E6: Set/Jump to Loop", "E7: Set Tremolo Waveform", "E8: Illegal", "*E9: Retrigger Note", "*EA: Fine Volume Slide Up", "*EB: Fine Volume Slide Down", "*EC: Note Cut", "*ED: Note Delay", "*EE: Pattern Delay", "EF: Invert Loop" };
283
284 static int MOD_FindNote( int period )
285 {
286         const int period_table[61] =
287         {
288                 0,
289                 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960 , 906,
290                 856, 808 , 762 , 720 , 678 , 640 , 604 , 570 , 538 , 508 , 480 , 453,
291                 428, 404 , 381 , 360 , 339 , 320 , 302 , 285 , 269 , 254 , 240 , 226,
292                 214, 202 , 190 , 180 , 170 , 160 , 151 , 143 , 135 , 127 , 120 , 113,
293                 107, 101 , 95  , 90  , 85  , 80  , 75  , 71  , 67  , 63  , 60  , 56
294         };
295         int i, d, r;
296         int closest = 1000000000;
297
298         r = -1;
299
300         for( i = 0; i < 61; ++i )
301         {
302                 d = abs( period_table[i] - period );
303                 if ( d < closest )
304                 {
305                         r = i;
306                         closest = d;
307                 }
308         }
309
310         if ( r == -1 )
311         {
312                 printf( "Can't find note with period:%d...\n", period );
313         }
314
315         return r;
316 }
317
318 #define FOUR_CHAR_CODE( ch0, ch1, ch2, ch3 ) \
319         ( (long)(unsigned char)(ch0) | ( (long)(unsigned char)(ch1) << 8 ) | \
320           ( (long)(unsigned char)(ch2) << 16 ) | ( (long)(unsigned char)(ch3) << 24 ) )
321
322 static int MOD_GetNumChans( ULONG file_format )
323 {
324         switch( file_format )
325         {
326         case FOUR_CHAR_CODE( 'T', 'D', 'Z', '1'):
327                 return 1;
328                 break;
329
330         case FOUR_CHAR_CODE( '2', 'C', 'H', 'N'):
331         case FOUR_CHAR_CODE( 'T', 'D', 'Z', '2'):
332                 return 2;
333                 break;
334
335         case FOUR_CHAR_CODE( 'T', 'D', 'Z', '3'):
336                 return 3;
337                 break;
338
339         case FOUR_CHAR_CODE( 'M', '.', 'K', '.'):
340         case FOUR_CHAR_CODE( 'F', 'L', 'T', '4'):
341         case FOUR_CHAR_CODE( 'M', '!', 'K', '!'):
342                 return 4;
343                 break;
344
345         case FOUR_CHAR_CODE( '5', 'C', 'H', 'N'):
346                 return 5;
347                 break;
348
349         case FOUR_CHAR_CODE( '6', 'C', 'H', 'N'):
350                 return 6;
351                 break;
352
353         case FOUR_CHAR_CODE( '7', 'C', 'H', 'N'):
354                 return 7;
355                 break;
356
357         case FOUR_CHAR_CODE( '8', 'C', 'H', 'N'):
358         case FOUR_CHAR_CODE( 'O', 'C', 'T', 'A'):
359         case FOUR_CHAR_CODE( 'C', 'D', '8', '1'):
360                 return 8;
361                 break;
362
363         case FOUR_CHAR_CODE( 'F', 'L', 'T', '8'):
364                 return -2; /* Unsupported MOD type */
365                 break;
366
367         case FOUR_CHAR_CODE( '9', 'C', 'H', 'N'):
368                 return 9;
369                 break;
370
371         case FOUR_CHAR_CODE( '1', '0', 'C', 'H'):
372                 return 10;
373                 break;
374
375         case FOUR_CHAR_CODE( '1', '1', 'C', 'H'):
376                 return 11;
377                 break;
378
379         case FOUR_CHAR_CODE( '1', '2', 'C', 'H'):
380                 return 12;
381                 break;
382
383         case FOUR_CHAR_CODE( '1', '3', 'C', 'H'):
384                 return 13;
385                 break;
386
387         case FOUR_CHAR_CODE( '1', '4', 'C', 'H'):
388                 return 14;
389                 break;
390
391         case FOUR_CHAR_CODE( '1', '5', 'C', 'H'):
392                 return 15;
393                 break;
394
395         case FOUR_CHAR_CODE( '1', '6', 'C', 'H'):
396                 return 16;
397                 break;
398
399         case FOUR_CHAR_CODE( '1', '7', 'C', 'H'):
400         case FOUR_CHAR_CODE( '1', '8', 'C', 'H'):
401         case FOUR_CHAR_CODE( '1', '9', 'C', 'H'):
402         case FOUR_CHAR_CODE( '2', '0', 'C', 'H'):
403         case FOUR_CHAR_CODE( '2', '1', 'C', 'H'):
404         case FOUR_CHAR_CODE( '2', '2', 'C', 'H'):
405         case FOUR_CHAR_CODE( '2', '3', 'C', 'H'):
406         case FOUR_CHAR_CODE( '2', '4', 'C', 'H'):
407         case FOUR_CHAR_CODE( '2', '5', 'C', 'H'):
408         case FOUR_CHAR_CODE( '2', '6', 'C', 'H'):
409         case FOUR_CHAR_CODE( '2', '7', 'C', 'H'):
410         case FOUR_CHAR_CODE( '2', '8', 'C', 'H'):
411         case FOUR_CHAR_CODE( '2', '9', 'C', 'H'):
412         case FOUR_CHAR_CODE( '3', '0', 'C', 'H'):
413         case FOUR_CHAR_CODE( '3', '1', 'C', 'H'):
414         case FOUR_CHAR_CODE( '3', '2', 'C', 'H'):
415         case FOUR_CHAR_CODE( '3', '3', 'C', 'H'):
416         case FOUR_CHAR_CODE( '3', '4', 'C', 'H'):
417         case FOUR_CHAR_CODE( '3', '5', 'C', 'H'):
418         case FOUR_CHAR_CODE( '3', '6', 'C', 'H'):
419         case FOUR_CHAR_CODE( '3', '7', 'C', 'H'):
420         case FOUR_CHAR_CODE( '3', '8', 'C', 'H'):
421         case FOUR_CHAR_CODE( '3', '9', 'C', 'H'):
422         case FOUR_CHAR_CODE( '4', '0', 'C', 'H'):
423         case FOUR_CHAR_CODE( '4', '1', 'C', 'H'):
424         case FOUR_CHAR_CODE( '4', '2', 'C', 'H'):
425         case FOUR_CHAR_CODE( '4', '3', 'C', 'H'):
426         case FOUR_CHAR_CODE( '4', '4', 'C', 'H'):
427         case FOUR_CHAR_CODE( '4', '5', 'C', 'H'):
428         case FOUR_CHAR_CODE( '4', '6', 'C', 'H'):
429         case FOUR_CHAR_CODE( '4', '7', 'C', 'H'):
430         case FOUR_CHAR_CODE( '4', '8', 'C', 'H'):
431         case FOUR_CHAR_CODE( '4', '9', 'C', 'H'):
432         case FOUR_CHAR_CODE( '5', '0', 'C', 'H'):
433         case FOUR_CHAR_CODE( '5', '1', 'C', 'H'):
434         case FOUR_CHAR_CODE( '5', '2', 'C', 'H'):
435         case FOUR_CHAR_CODE( '5', '3', 'C', 'H'):
436         case FOUR_CHAR_CODE( '5', '4', 'C', 'H'):
437         case FOUR_CHAR_CODE( '5', '5', 'C', 'H'):
438         case FOUR_CHAR_CODE( '5', '6', 'C', 'H'):
439         case FOUR_CHAR_CODE( '5', '7', 'C', 'H'):
440         case FOUR_CHAR_CODE( '5', '8', 'C', 'H'):
441         case FOUR_CHAR_CODE( '5', '9', 'C', 'H'):
442         case FOUR_CHAR_CODE( '6', '0', 'C', 'H'):
443         case FOUR_CHAR_CODE( '6', '1', 'C', 'H'):
444         case FOUR_CHAR_CODE( '6', '2', 'C', 'H'):
445         case FOUR_CHAR_CODE( '6', '3', 'C', 'H'):
446         case FOUR_CHAR_CODE( '6', '4', 'C', 'H'):
447                 return -3; /* Too many channels */
448                 break;
449
450         default:
451                 return -1; /* Unrecognised MOD type */
452                 break;
453         }
454
455         /* 'FLT4', 'FLT8': Startrekker 4/8 channel file. ('FLT6' doesn't exist) */
456         /* 'CD81'        : Falcon 8 channel MODs */
457         /* '2CHN'        : FastTracker 2 Channel MODs */
458         /* 'yyCH' where yy can be 10, 12, .. 30, 32: FastTracker yy Channel MODs */
459         /* 'yyCH' where yy can be 11, 13, 15: TakeTracker 11, 13, 15 channel MODs */
460         /* 'TDZx' where x can be 1, 2 or 3: TakeTracker 1, 2, 3 channel MODs */
461         /* ModPlug Tracker saves 33-64 channel MODs as '33CH' to '64CH' */
462 }
463
464 static void MOD_ConvMod( FILE* out_file, DATAPACK* samples, DATAPACK* patterns, const char* filename, int mod_num )
465 {
466         FILE* in_file;
467
468         mod_num_chans[mod_num] = 0;
469
470         in_file = fopen( filename, "rb" );
471         if ( in_file )
472         {
473                 struct ModSampleTemp samps_temp[32];
474                 char temp[23];
475                 int i, tmp, line, chan;
476                 UBYTE song_length, last_pattern;
477                 UBYTE song_data[128];
478                 int pat[64][16];
479                 UWORD tmp_uword;
480                 UBYTE tmp_ubyte;
481                 ULONG notes_temp[16][64];
482                 int effect_count[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
483                 BOOL first;
484                 ULONG new_temp;
485                 int num_chans;
486                 UBYTE song_restart_pos;
487
488                 /*printf( "\nMOD_ConvMod( %s ): Reading...\n", filename ); */
489
490                 fread( temp, 20, 1, in_file ); /* mod name */
491
492                 for( i = 1; i <= 31; ++i )
493                 {
494                         fread( temp, 22, 1, in_file ); /* sample name */
495
496                         fread( &tmp_uword, 2, 1, in_file ); /* sample length */
497                         samps_temp[i].length = SwapUWORD( tmp_uword );
498
499                         fread( &tmp_ubyte, 1, 1, in_file ); /* finetune */
500                         /*if ( tmp_ubyte & 8 ) */
501                         /*      tmp_ubyte -= 8; */
502                         /*else */
503                         /*      tmp_ubyte += 8; */
504                         samps_temp[i].finetune = tmp_ubyte;
505                         if ( tmp_ubyte > 15 )
506                                 printf( "MOD_ConvMod( %s ): Error: finetune > 15...\n", filename );
507
508                         fread( &tmp_ubyte, 1, 1, in_file ); /* volume */
509                         samps_temp[i].volume = tmp_ubyte;
510
511                         if ( tmp_ubyte > 64 )
512                         {
513                                 printf( "Failed! Reason: Sample vol > 64\n" );
514                                 return;
515                         }
516
517                         fread( &tmp_uword, 2, 1, in_file );
518                         samps_temp[i].repeat = SwapUWORD( tmp_uword );
519
520                         fread( &tmp_uword, 2, 1, in_file );
521                         tmp = SwapUWORD( tmp_uword );
522
523                         /*samps_temp[i].truelen = samps_temp[i].length; */
524
525                         if ( tmp <= 1 )
526                         {
527                                 samps_temp[i].repeat = 65535;
528                                 samps_temp[i].truelen = samps_temp[i].length;
529                         }
530                         else
531                         {
532                                 samps_temp[i].truelen = Min( samps_temp[i].repeat + tmp, samps_temp[i].length );
533                         }
534
535                         /*printf( "i:%d len:%d vol:%d rep:%d fine:%d\n", i, samps_temp[i].truelen, samps_temp[i].volume, samps_temp[i].repeat, samps_temp[i].finetune ); */
536                 }
537
538                 fread( &song_length, 1, 1, in_file );
539                 fread( &song_restart_pos, 1, 1, in_file );
540
541                 if ( (song_length < 1) || (song_length > 128) )
542                         printf( "MOD_ConvMod( %s ): song_length:%d\n", filename, song_length );
543
544                 last_pattern = 0;
545                 for( i = 0; i <= 127; ++i )
546                 {
547                         fread( &tmp_ubyte, 1, 1, in_file );
548                         song_data[i] = tmp_ubyte;
549                         if ( tmp_ubyte > last_pattern )
550                                 last_pattern = tmp_ubyte;
551                 }
552
553                 if ( last_pattern > 63 )
554                         printf( "MOD_ConvMod( %s ): last_pattern:%d\n", filename, last_pattern );
555
556                 fread( &new_temp, 4, 1, in_file );
557                 num_chans = MOD_GetNumChans( new_temp );
558                 if ( num_chans <= 0 )
559                 {
560                         switch( num_chans )
561                         {
562                         case -2: /* Unsupported MOD type */
563                                 printf( "Failed! Reason: Unsupported MOD type (%x)\n", new_temp );
564                                 break;
565
566                         case -3: /* Too many channels */
567                                 printf( "Failed! Reason: Too many channels\n" );
568                                 break;
569
570                         default: /* Unrecognised MOD type */
571                                 printf( "Failed! Reason: Unrecognised MOD type (%x)\n", new_temp );
572                                 break;
573                         }
574                         return;
575                 }
576                 else
577                 {
578                         if ( num_chans == 1 )
579                                 printf( "%d channel...", num_chans );
580                         else
581                                 printf( "%d channels...", num_chans );
582
583                         mod_num_chans[mod_num] = num_chans;
584                 }
585
586                 for( i = 0; i <= last_pattern; ++i )
587                 {
588                         /*curr_note = &notes_temp[0][0]; */
589                         for( line = 0; line < 64; ++line )
590                         {
591                                 for( chan = 0; chan < num_chans; ++chan )
592                                 {
593                                         UBYTE byte1, byte2, byte3, byte4;
594                                         int samp_num, period, effect;
595
596                                         fread( &byte1, 1, 1, in_file );
597                                         fread( &byte2, 1, 1, in_file );
598                                         fread( &byte3, 1, 1, in_file );
599                                         fread( &byte4, 1, 1, in_file );
600
601                                         samp_num = (byte1&0xf0)+(byte3>>4);
602                                         period = (((int)(byte1&0xf))<<8) + ((int)byte2);
603                                         effect = (((int)(byte3&0xf))<<8) + byte4;
604                                         if ( effect != 0 )
605                                         {
606                                                 if ( (effect>>8) == 0xe )
607                                                         ++effect_count[((effect&0xf0)>>4)+16];
608                                                 else
609                                                 {
610                                                         if ( (effect>>8) == 0xf )
611                                                         {
612                                                                 if ( (effect&0xff) > 31 )
613                                                                         ++effect_count[14];
614                                                                 else
615                                                                         ++effect_count[15];
616                                                         }
617                                                         else
618                                                         {
619                                                                 if ( (effect>>8) == 0xc )
620                                                                         if ( (effect&0xff) > 64 )
621                                                                                 effect = 0xc40;
622                                                                 if ( (effect>>8) == 0xd ) /* pattern break */
623                                                                         effect = (effect&0xf00) + (((effect>>4)&0xf)*10)+(effect&0xf);
624                                                                 ++effect_count[effect>>8];
625                                                         }
626                                                 }
627                                         }
628                                         notes_temp[chan][line] = (samp_num<<24) + (MOD_FindNote(period)<<12) + effect;
629                                 }
630                         }
631
632                         for( chan = 0; chan < num_chans; ++chan )
633                         {
634                                 pat[i][chan] = DATA_AppendAligned( patterns, (UBYTE*)(&notes_temp[chan][0]), 256, 256 );
635                         }
636                 }
637
638                 for( i = 0; i <= 127; ++i )
639                 {
640                         if ( i >= song_length )
641                         {
642                                 for( chan = 0; chan < num_chans; ++chan )
643                                         patts[mod_num][i][chan] = -1;
644                         }
645                         else
646                         {
647                                 for( chan = 0; chan < num_chans; ++chan )
648                                         patts[mod_num][i][chan] = pat[song_data[i]][chan];
649                         }
650                 }
651
652                 if ( song_length > max_song_length )
653                         max_song_length = song_length;
654
655                 if ( song_restart_pos < 127 )
656                 {
657                         if ( patts[mod_num][song_restart_pos][0] == -1 )
658                                 song_restart_pos = 0;
659                 }
660                 else
661                 {
662                         song_restart_pos = 0;
663                 }
664
665                 mod_song_restart_pos[mod_num] = song_restart_pos;
666
667                 for( i = 1; i <= 31; ++i )
668                 {
669                         if ( samps_temp[i].length > 0 )
670                         {
671                                 MOD_LoadSound( &samps[mod_num][i-1], in_file, samples, samps_temp[i].length, samps_temp[i].truelen, samps_temp[i].repeat );
672                                 samps[mod_num][i-1].volume = samps_temp[i].volume;
673                                 samps[mod_num][i-1].finetune = samps_temp[i].finetune;
674                                 samps[mod_num][i-1].repeat = samps_temp[i].repeat;
675                         }
676                 }
677
678                 printf( "Done!\n" );
679
680                 first = TRUE;
681                 for( i = 0; i <= 31; ++i )
682                         if ( effect_count[i] > 0 )
683                                 if ( *(effect_name[i]) != '*' )
684                                 {
685                                         if ( first )
686                                         {
687                                                 printf( "Unsupported effects:\n" );
688                                                 first = FALSE;
689                                         }
690                                         printf( "%s (%d)\n", effect_name[i], effect_count[i] );
691                                 }
692
693                 printf( "song_length:%d last_pattern:%d\n\n", song_length, last_pattern );
694
695                 fclose( in_file );
696         }
697         else
698         {
699                 printf( "MOD_ConvMod( %s ): Unable to open file for reading...\n", filename );
700         }
701 }
702
703 static void MOD_WriteSamples( FILE* out_file, int num_mods )
704 {
705         int i, s;
706
707         fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_ModSamples\nAAS_ModSamples:" );
708
709         for( i = 0; i < num_mods; ++i )
710         {
711                 fprintf( out_file, "\n.word" );
712                 for( s = 0; s < 31; ++s )
713                 {
714                         if ( s == 0 )
715                                 fprintf( out_file, " %u, %u, %u", samps[i][s].data, (samps[i][s].length<<16)+samps[i][s].repeat, ((samps[i][s].volume&0xff)<<8)+(samps[i][s].finetune&0xff) );
716                         else
717                                 fprintf( out_file, ", %u, %u, %u", samps[i][s].data, (samps[i][s].length<<16)+samps[i][s].repeat, ((samps[i][s].volume&0xff)<<8)+(samps[i][s].finetune&0xff) );
718                 }
719         }
720
721         fprintf( out_file, "\n" );
722 }
723
724 static void MOD_WritePatterns( FILE* out_file, int num_mods )
725 {
726         int mod_num, pattern_num, channel_num;
727         BOOL first;
728
729         fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_Sequence\nAAS_Sequence:" );
730
731         for( mod_num = 0; mod_num < num_mods; ++mod_num )
732         {
733                 fprintf( out_file, "\n.short" );
734                 first = TRUE;
735                 for( pattern_num = 0; pattern_num < 128; ++pattern_num )
736                 {
737                         for( channel_num = 0; channel_num < 16; ++channel_num )
738                         {
739                                 if ( first )
740                                 {
741                                         first = FALSE;
742                                         fprintf( out_file, " %d", patts[mod_num][pattern_num][channel_num] );
743                                 }
744                                 else
745                                 {
746                                         fprintf( out_file, ", %d", patts[mod_num][pattern_num][channel_num] );
747                                 }
748                         }
749                 }
750         }
751
752         fprintf( out_file, "\n" );
753 }
754
755 static void MOD_WriteNumChans( FILE* out_file, int num_mods )
756 {
757         int mod_num;
758
759         fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_NumChans\nAAS_NumChans:\n.byte" );
760
761         for( mod_num = 0; mod_num < num_mods; ++mod_num )
762         {
763                 if ( mod_num )
764                         fprintf( out_file, ", %d", mod_num_chans[mod_num] );
765                 else
766                         fprintf( out_file, " %d", mod_num_chans[mod_num] );
767         }
768
769         fprintf( out_file, "\n" );
770 }
771
772 static void MOD_WriteSongRestartPos( FILE* out_file, int num_mods )
773 {
774         int mod_num;
775
776         fprintf( out_file, "\n.ALIGN\n.GLOBAL AAS_RestartPos\nAAS_RestartPos:\n.byte" );
777
778         for( mod_num = 0; mod_num < num_mods; ++mod_num )
779         {
780                 if ( mod_num )
781                         fprintf( out_file, ", %d", mod_song_restart_pos[mod_num] );
782                 else
783                         fprintf( out_file, " %d", mod_song_restart_pos[mod_num] );
784         }
785
786         fprintf( out_file, "\n" );
787 }
788
789 /*
790    static void MOD_WritePeriodConvTable( FILE* out_file, int mix_rate )
791    {
792    int i, freq;
793
794    fprintf( out_file, "\nconst UWORD AAS_MOD_period_conv_table[2048] = { 65535" );
795
796    for( i = 1; i < 2048; ++i )
797    {
798    freq = (int)((((double)7093789.2)/((double)(2*i))));
799    if ( freq > 65535 )
800    freq = 65535;
801    fprintf( out_file, ", %d", freq );
802    }
803
804    fprintf( out_file, " };\n" );
805    }
806    */
807
808 static int last_samp_length = 0;
809
810 static int RAW_LoadSound( const char* filename, DATAPACK* sfx )
811 {
812         int data = -1;
813         struct stat file_info;
814
815         if ( stat( filename, &file_info ) == 0 )
816         {
817                 FILE* in_file;
818                 int length;
819                 length = file_info.st_size;
820
821                 in_file = fopen( filename, "rb" );
822                 if ( in_file )
823                 {
824                         BYTE* samp = (BYTE*)malloc( length+16 );
825
826                         /*printf( "\nRAW_LoadSound( %s ): Reading...\n", filename ); */
827
828                         last_samp_length = length;
829
830                         fread( samp+16, 1, length, in_file );
831                         fclose( in_file );
832
833                         memcpy( samp, samp+length, 16 );
834
835                         MISC_ProcessSound( samp, length+16 );
836
837                         data = DATA_Append( sfx, (UBYTE*)samp, length+16 ) + 16;
838
839                         free( samp );
840
841                         printf( "Done!\n\n" );
842                 }
843                 else
844                 {
845                         printf( "\nRAW_LoadSound( %s ): Unable to open...\n", filename );
846                 }
847         }
848         else
849         {
850                 printf( "\nRAW_LoadSound( %s ): Unable to open...\n", filename );
851         }
852
853         return data;
854 }
855
856 static int WAV_LoadSound( const char* filename, DATAPACK* sfx )
857 {
858         int data = -1;
859         FILE* in_file;
860
861         last_samp_length = 0;
862
863         in_file = fopen( filename, "rb" );
864         if ( in_file )
865         {
866                 ULONG tmp;
867
868                 fread( &tmp, 4, 1, in_file );
869
870                 if ( tmp == 0x46464952 ) /* "RIFF" */
871                 {
872                         fread( &tmp, 4, 1, in_file );
873                         fread( &tmp, 4, 1, in_file );
874
875                         if ( tmp == 0x45564157 ) /* "WAVE" */
876                         {
877                                 fread( &tmp, 4, 1, in_file );
878
879                                 if ( tmp == 0x20746d66 ) /* "fmt " */
880                                 {
881                                         unsigned short format;
882
883                                         fread( &tmp, 4, 1, in_file );
884                                         fread( &format, 2, 1, in_file );
885
886                                         if ( format == 1 ) /* PCM format */
887                                         {
888                                                 unsigned short channels;
889
890                                                 fread( &channels, 2, 1, in_file );
891
892                                                 if ( channels == 1 ) /* mono */
893                                                 {
894                                                         unsigned short bits_per_sample;
895
896                                                         fread( &tmp, 4, 1, in_file ); /* sample rate in hz */
897                                                         fread( &tmp, 4, 1, in_file );
898                                                         fread( &tmp, 2, 1, in_file );
899                                                         fread( &bits_per_sample, 2, 1, in_file );
900
901                                                         if ( bits_per_sample == 8 )
902                                                         {
903                                                                 int ret, length;
904                                                                 BOOL done;
905
906                                                                 done = FALSE;
907                                                                 length = 0;
908
909                                                                 do
910                                                                 {
911                                                                         ret = fread( &tmp, 4, 1, in_file );
912
913                                                                         if ( ret == 1 )
914                                                                         {
915                                                                                 if ( tmp == 0x61746164 ) /* "data" */
916                                                                                 {
917                                                                                         ret = fread( &length, 4, 1, in_file );
918
919                                                                                         if ( ret == 1 )
920                                                                                         {
921                                                                                                 if ( length > 0 )
922                                                                                                 {
923                                                                                                         done = TRUE;
924                                                                                                 }
925                                                                                         }
926                                                                                         else
927                                                                                         {
928                                                                                                 done = TRUE;
929                                                                                         }
930                                                                                 }
931                                                                                 else
932                                                                                 {
933                                                                                         int size;
934
935                                                                                         ret = fread( &size, 4, 1, in_file );
936
937                                                                                         if ( ret == 1 )
938                                                                                         {
939                                                                                                 fseek( in_file, size, SEEK_CUR );
940                                                                                         }
941                                                                                         else
942                                                                                         {
943                                                                                                 done = TRUE;
944                                                                                         }
945                                                                                 }
946                                                                         }
947                                                                         else
948                                                                         {
949                                                                                 done = TRUE;
950                                                                         }
951                                                                 }
952                                                                 while ( !done );
953
954                                                                 if ( length > 0 )
955                                                                 {
956                                                                         BYTE* samp = (BYTE*)malloc( length+16 );
957
958                                                                         last_samp_length = length;
959
960                                                                         fread( samp+16, 1, length, in_file );
961
962                                                                         memcpy( samp, samp+length, 16 );
963
964                                                                         MISC_ConvSoundToSigned( (UBYTE*)samp, length+16 );
965
966                                                                         MISC_ProcessSound( samp, length+16 );
967
968                                                                         data = DATA_Append( sfx, (UBYTE*)samp, length+16 ) + 16;
969
970                                                                         free( samp );
971
972                                                                         printf( "Done!\n\n" );
973                                                                 }
974                                                                 else
975                                                                 {
976                                                                         printf( "Failed: Unable to find valid data subchunk...\n" );
977                                                                 }
978                                                         }
979                                                         else
980                                                         {
981                                                                 printf( "\nWAV_LoadSound( %s ): Failed: Not 8 bit...\n", filename );
982                                                         }
983                                                 }
984                                                 else
985                                                 {
986                                                         printf( "\nWAV_LoadSound( %s ): Failed: Not mono...\n", filename );
987                                                 }
988                                         }
989                                         else
990                                         {
991                                                 printf( "\nWAV_LoadSound( %s ): Failed: Not in PCM format...\n", filename );
992                                         }
993                                 }
994                                 else
995                                 {
996                                         printf( "\nWAV_LoadSound( %s ): Failed: Missing fmt subchunk...\n", filename );
997                                 }
998                         }
999                         else
1000                         {
1001                                 printf( "\nWAV_LoadSound( %s ): Failed: Not a WAVE file...\n", filename );
1002                         }
1003                 }
1004                 else
1005                 {
1006                         printf( "\nWAV_LoadSound( %s ): Failed: RIFF header missing...\n", filename );
1007                 }
1008
1009                 fclose( in_file );
1010         }
1011         else
1012         {
1013                 printf( "\nWAV_LoadSound( %s ): Failed: Unable to open...\n", filename );
1014         }
1015
1016         return data;
1017 }
1018
1019 __inline static char String_GetChar( const char* txt, int offset )
1020 {
1021         return *(txt+offset);
1022 }
1023
1024 static BOOL String_EndsWithMOD( const char* txt )
1025 {
1026         int txt_len = strlen( txt );
1027
1028         if ( txt_len > 4 )
1029         {
1030                 if ( (String_GetChar( txt, txt_len-4 ) == '.') &&
1031                                 ((String_GetChar( txt, txt_len-3 ) == 'm') || (String_GetChar( txt, txt_len-3 ) == 'M')) &&
1032                                 ((String_GetChar( txt, txt_len-2 ) == 'o') || (String_GetChar( txt, txt_len-2 ) == 'O')) &&
1033                                 ((String_GetChar( txt, txt_len-1 ) == 'd') || (String_GetChar( txt, txt_len-1 ) == 'D')) )
1034                 {
1035                         return TRUE;
1036                 }
1037         }
1038
1039         return FALSE;
1040 }
1041
1042 static BOOL String_EndsWithRAW( const char* txt )
1043 {
1044         int txt_len = strlen( txt );
1045
1046         if ( txt_len > 4 )
1047         {
1048                 if ( (String_GetChar( txt, txt_len-4 ) == '.') &&
1049                                 ((String_GetChar( txt, txt_len-3 ) == 'r') || (String_GetChar( txt, txt_len-3 ) == 'R')) &&
1050                                 ((String_GetChar( txt, txt_len-2 ) == 'a') || (String_GetChar( txt, txt_len-2 ) == 'A')) &&
1051                                 ((String_GetChar( txt, txt_len-1 ) == 'w') || (String_GetChar( txt, txt_len-1 ) == 'W')) )
1052                 {
1053                         return TRUE;
1054                 }
1055         }
1056
1057         return FALSE;
1058 }
1059
1060 static BOOL String_EndsWithWAV( const char* txt )
1061 {
1062         int txt_len = strlen( txt );
1063
1064         if ( txt_len > 4 )
1065         {
1066                 if ( (String_GetChar( txt, txt_len-4 ) == '.') &&
1067                                 ((String_GetChar( txt, txt_len-3 ) == 'w') || (String_GetChar( txt, txt_len-3 ) == 'W')) &&
1068                                 ((String_GetChar( txt, txt_len-2 ) == 'a') || (String_GetChar( txt, txt_len-2 ) == 'A')) &&
1069                                 ((String_GetChar( txt, txt_len-1 ) == 'v') || (String_GetChar( txt, txt_len-1 ) == 'V')) )
1070                 {
1071                         return TRUE;
1072                 }
1073         }
1074
1075         return FALSE;
1076 }
1077
1078 static void String_MakeSafe( char* temp )
1079 {
1080         char c;
1081
1082         while( *temp != (char)0 )
1083         {
1084                 c = *temp;
1085                 if ( !(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9'))) )
1086                         *temp = '_';
1087                 ++temp;
1088         }
1089 }
1090
1091 static void ShowHelp()
1092 {
1093         printf( " Usage: Conv2AAS input_dir\n\n" );
1094         printf( "  input_dir  Directory containing all Protracker\n" );
1095         printf( "             MODs & sample data\n\n" );
1096         printf( "  Output goes to files AAS_Data.s & AAS_Data.h\n" );
1097 }
1098
1099 int main( int argc, char *argv[] )
1100 {
1101         setbuf( stdout, 0 );
1102
1103         printf( "\n" );
1104         printf( "/---------------------------------------------\\\n" );
1105         printf( "| Conv2AAS v1.11        WAV, RAW & MOD -> AAS |\n" );
1106         printf( "| Copyright (c) 2005, Apex Designs            |\n" );
1107         printf( "\\---------------------------------------------/\n\n" );
1108
1109         if ( argc < 2 )
1110         {
1111                 ShowHelp();
1112         }
1113         else if ( (argc == 2) && ((strcmp( argv[1], "-help" ) == 0) || (strcmp( argv[1], "-?" ) == 0)) )
1114         {
1115                 ShowHelp();
1116         }
1117         else
1118         {
1119                 FILE* out_file_s;
1120
1121                 out_file_s = fopen( "data/aas_data.s", "w" );
1122                 if ( out_file_s )
1123                 {
1124                         FILE* out_file_h;
1125
1126                         out_file_h = fopen( "data/aas_data.h", "w" );
1127                         if ( out_file_h )
1128                         {
1129                                 DIR* dir_info;
1130
1131                                 dir_info = opendir( argv[1] );
1132                                 if ( dir_info )
1133                                 {
1134                                         int files_converted = 0;
1135                                         int mods_found = 0;
1136                                         DATAPACK* samples;
1137                                         DATAPACK* patterns;
1138                                         struct dirent* file_info;
1139
1140                                         samples = DATA_Create( "AAS_SampleData" );
1141                                         patterns = DATA_Create( "AAS_PatternData" );
1142
1143                                         fprintf( out_file_h, "#ifndef __AAS_DATA__\n#define __AAS_DATA__\n\n#include \"AAS.h\"\n\n#if AAS_VERSION != 0x111\n#error AAS version does not match Conv2AAS version\n#endif\n\nAAS_BEGIN_DECLS\n" );
1144
1145                                         fprintf( out_file_s, ".TEXT\n.SECTION .rodata\n.ALIGN\n.ARM\n\n.ALIGN\n.EXTERN AAS_lib_v111\n.GLOBAL AAS_data_v111\nAAS_data_v111:\n.word AAS_lib_v111\n" );
1146
1147                                         do
1148                                         {
1149                                                 file_info = readdir( dir_info );
1150                                                 if ( file_info )
1151                                                 {
1152                                                         char temp[512];
1153
1154                                                         strcpy( temp, argv[1] );
1155                                                         strcat( temp, "/" );
1156                                                         strcat( temp, file_info->d_name );
1157
1158                                                         if ( String_EndsWithMOD( file_info->d_name ) )
1159                                                         {
1160                                                                 ++files_converted;
1161                                                                 printf( "Adding MOD %s...", file_info->d_name );
1162                                                                 MOD_ConvMod( out_file_h, samples, patterns, temp, mods_found );
1163                                                                 /*printf( "Done!\n" ); */
1164                                                                 strcpy( temp, file_info->d_name );
1165                                                                 *(temp + strlen(temp) - 4) = 0;
1166                                                                 String_MakeSafe( temp );
1167                                                                 fprintf( out_file_h, "\nextern const AAS_u8 AAS_DATA_MOD_%s;\n", temp );
1168                                                                 fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_MOD_%s\nAAS_DATA_MOD_%s:\n.byte %d\n", temp, temp, mods_found );
1169                                                                 ++mods_found;
1170                                                         }
1171                                                         else if ( String_EndsWithRAW( file_info->d_name ) )
1172                                                         {
1173                                                                 int val;
1174                                                                 ++files_converted;
1175                                                                 printf( "Adding RAW %s...", file_info->d_name );
1176                                                                 val = RAW_LoadSound( temp, samples );
1177                                                                 if ( val >= 0 )
1178                                                                 {
1179                                                                         strcpy( temp, file_info->d_name );
1180                                                                         *(temp + strlen(temp) - 4) = 0;
1181                                                                         String_MakeSafe( temp );
1182                                                                         fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_START_%s;\n", temp );
1183                                                                         fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_START_%s\nAAS_DATA_SFX_START_%s:\n.word AAS_SampleData + %d\n", temp, temp, val );
1184                                                                         fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_END_%s;\n", temp );
1185                                                                         fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_END_%s\nAAS_DATA_SFX_END_%s:\n.word AAS_SampleData + %d\n", temp, temp, val+last_samp_length );
1186                                                                 }
1187                                                         }
1188                                                         else if ( String_EndsWithWAV( file_info->d_name ) )
1189                                                         {
1190                                                                 int val;
1191                                                                 ++files_converted;
1192                                                                 printf( "Adding WAV %s...", file_info->d_name );
1193                                                                 val = WAV_LoadSound( temp, samples );
1194                                                                 if ( val >= 0 )
1195                                                                 {
1196                                                                         strcpy( temp, file_info->d_name );
1197                                                                         *(temp + strlen(temp) - 4) = 0;
1198                                                                         String_MakeSafe( temp );
1199                                                                         fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_START_%s;\n", temp );
1200                                                                         fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_START_%s\nAAS_DATA_SFX_START_%s:\n.word AAS_SampleData + %d\n", temp, temp, val );
1201                                                                         fprintf( out_file_h, "\nextern const AAS_s8* const AAS_DATA_SFX_END_%s;\n", temp );
1202                                                                         fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_SFX_END_%s\nAAS_DATA_SFX_END_%s:\n.word AAS_SampleData + %d\n", temp, temp, val+last_samp_length );
1203                                                                 }
1204                                                         }
1205                                                 }
1206                                         } while ( file_info );
1207
1208                                         closedir( dir_info );
1209
1210                                         fprintf( out_file_s, "\n.ALIGN\n.GLOBAL AAS_DATA_NUM_MODS\nAAS_DATA_NUM_MODS:\n.short %d\n", mods_found );
1211
1212                                         MOD_WriteSamples( out_file_s, mods_found );
1213                                         MOD_WritePatterns( out_file_s, mods_found );
1214                                         MOD_WriteNumChans( out_file_s, mods_found );
1215                                         MOD_WriteSongRestartPos( out_file_s, mods_found );
1216
1217                                         /*MOD_WritePeriodConvTable( out_file_h, 24002 ); */
1218
1219                                         DATA_Write( samples, out_file_s );
1220                                         DATA_Write( patterns, out_file_s );
1221
1222                                         printf( "\n" );
1223
1224                                         fprintf( out_file_h, "\nAAS_END_DECLS\n\n#endif\n" );
1225                                 }
1226                                 else
1227                                 {
1228                                         printf( "Unable to open directory %s...\n", argv[1] );
1229                                 }
1230
1231                                 fclose( out_file_h );
1232                         }
1233                         else
1234                         {
1235                                 printf( "Unable to open AASData.h for writing...\n" );
1236                         }
1237
1238                         fclose( out_file_s );
1239                 }
1240                 else
1241                 {
1242                         printf( "Unable to open AASData.s for writing...\n" );
1243                 }
1244         }
1245
1246         return 0;
1247 }