1 /*--------------------------------------------------------------------------*
2 Project: Revolution Audio sound file converter
3 File: sndconv.c
4
5 Copyright (C)1998-2006 Nintendo All Rights Reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Log: sndconv.c,v $
14 Revision 1.3 2008/10/17 01:33:23 aka
15 Changed location of soundSetDefaultFormat() in main().
16
17 Revision 1.2 2006/02/09 06:26:26 aka
18 Changed copyright.
19
20
21 *--------------------------------------------------------------------------*/
22 #include <windows.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "soundconv.h"
26
27 static int state;
28 static int line;
29 static char seps[] = " ,\t\n";
30
31 static char *inputPath;
32 static char path[1024];
33 static char error[1024];
34
35 HINSTANCE hDllSoundfile;
36 HINSTANCE hDllDsptool;
37
38
39 /*--------------------------------------------------------------------------*
40 absolutePath()
41
42 - changes '/' to '\\' to handle paths from bash
43 *--------------------------------------------------------------------------*/
absolutePath(char * token)44 int absolutePath(char *token)
45 {
46 if ((strchr(token, ':')) || (*token == '\\'))
47 return 1;
48
49 return 0;
50 }
51
52
53 /*--------------------------------------------------------------------------*
54 fixString()
55
56 - changes '/' to '\\' to handle paths from bash
57 *--------------------------------------------------------------------------*/
fixString(char * ch)58 void fixString(char *ch)
59 {
60 while(*ch)
61 {
62 if (*ch == '/')
63 *ch = '\\';
64
65 ch++;
66 }
67 }
68
69
70 /*--------------------------------------------------------------------------*
71 parsePathStatement()
72
73 - parse for path to set
74 - copy the string for later use
75 *--------------------------------------------------------------------------*/
parsePathStatement(void)76 void parsePathStatement(void)
77 {
78 char *token;
79
80 if (token = strtok(NULL, seps))
81 {
82 if (token[0] != ';')
83 {
84 // save the path for file accessing
85 strcpy(path, token);
86 fixString(path);
87 // printf("Setting path to %s\n", token);
88 return;
89 }
90 }
91
92 printf("%cError! Incomplete PATH statement on line %d.\n", 7, line);
93 }
94
95
96 /*--------------------------------------------------------------------------*
97 parseBeginStatement()
98
99 - check for end state
100 - parse for sound id
101 - set sound id in sound.c
102 - set state flag to begin
103 *--------------------------------------------------------------------------*/
parseBeginStatement(void)104 void parseBeginStatement(void)
105 {
106 char *token;
107
108 if (state == STATE_BEGIN)
109 printf("%cWarning! BEGIN statement made prior to END on line %d.\n", 7, line);
110
111 if (token = strtok(NULL, seps))
112 {
113 if (token[0] != ';')
114 {
115 soundInitParams();
116 soundSetIdString(token);
117 // printf("Begin sound id %s\n", token);
118 state = STATE_BEGIN;
119
120 return;
121 }
122 }
123
124 printf("%cError! Incomplete BEGIN statement on line %d.\n", 7, line);
125 }
126
127
128 /*--------------------------------------------------------------------------*
129 parseFileStatement()
130
131 - parse for sound file path
132 - set sound file path in sound.c
133 *--------------------------------------------------------------------------*/
parseFileStatement(void)134 void parseFileStatement(void)
135 {
136 char *token;
137
138 if (token = strtok(NULL, seps))
139 {
140 if (token[0] != ';')
141 {
142 fixString(token);
143
144 if (absolutePath(token))
145 {
146 soundSetSoundFile(token);
147 }
148 else if (path[0] != 0)
149 {
150 char ch[1024];
151
152 strcpy(ch, path);
153 strcat(ch, "\\");
154 strcat(ch, token);
155 soundSetSoundFile(ch);
156 }
157 else
158 {
159 soundSetSoundFile(token);
160 }
161
162 // printf("\tSound file %s\n", token);
163 return;
164 }
165 }
166
167 printf("%cError! Incomplete FILE statement on line %d.\n", 7, line);
168 }
169
170 /*--------------------------------------------------------------------------*
171 parseOutputStatment()
172
173 - parse output mode
174 - set flag in sound.c
175 *--------------------------------------------------------------------------*/
parseOutputStatement(void)176 void parseOutputStatement(void)
177 {
178 char *token;
179
180 if (token = strtok(NULL, seps))
181 {
182 if (token[0] != ';')
183 {
184 strupr(token);
185
186 if (strcmp(token, "ADPCM") == 0)
187 soundSetFormat(SOUND_FORMAT_ADPCM);
188 else if (strcmp(token, "8BIT") == 0)
189 soundSetFormat(SOUND_FORMAT_PCM8);
190 else if (strcmp(token, "16BIT") == 0)
191 soundSetFormat(SOUND_FORMAT_PCM16);
192 else
193 printf("%cWarning! Invalid token \"%s\" on line %d.\n", 7, token, line);
194
195 // printf("\tOutput %s\n", token);
196 return;
197 }
198 }
199
200 printf("%cError! Incomplete OUTPUT statement on line %d.\n", 7, line);
201 }
202
203
204 /*--------------------------------------------------------------------------*
205 parseSampleRateStatement()
206
207 - parse sample rate
208 - set sample rate in sound.c
209 *--------------------------------------------------------------------------*/
parseSamplerateStatement(void)210 void parseSamplerateStatement(void)
211 {
212 char *token;
213
214 if (token = strtok(NULL, seps))
215 {
216 if (token[0] != ';')
217 {
218 int i = atoi(token);
219
220 // printf("\tSample rate %d\n", i);
221
222 soundSetSampleRate(i);
223
224 return;
225 }
226 }
227
228 printf("%cError! Incomplete SAMPLERATE statement on line %d.\n", 7, line);
229 }
230
231
232 /*--------------------------------------------------------------------------*
233 parseLoopStatement()
234
235 - parse loop start and end
236 - set loop pointes in sound.c
237 *--------------------------------------------------------------------------*/
parseLoopStatement(void)238 void parseLoopStatement(void)
239 {
240 char *token;
241 int loopStart, loopEnd;
242
243 if (token = strtok(NULL, seps))
244 {
245 if (token[0] != ';')
246 {
247 loopStart = atoi(token);
248
249 if (token = strtok(NULL, seps))
250 {
251 if (token[0] != ';')
252 {
253 loopEnd = atoi(token);
254
255 // printf("\tLoop %d %d\n", loopStart, loopEnd);
256
257 soundSetLoopStart(loopStart);
258 soundSetLoopEnd(loopEnd);
259
260 return;
261 }
262 }
263 }
264 }
265
266 printf("%cError! Incomplete LOOP statement on line %d.\n", 7, line);
267 }
268
269
270 /*--------------------------------------------------------------------------*
271 parseMixStatement()
272
273 - parse mix
274 - set the output mix flag in sound.c
275 *--------------------------------------------------------------------------*/
parseMixStatement(void)276 void parseMixStatement(void)
277 {
278 char *token;
279
280 if (token = strtok(NULL, seps))
281 {
282 if (token[0] != ';')
283 {
284 strupr(token);
285
286 if (strcmp(token, "COMBINE") == 0)
287 soundSetMix(SOUND_STEREO_COMBINE);
288 else if (strcmp(token, "LEFT") == 0)
289 soundSetMix(SOUND_STEREO_LEFT);
290 else if (strcmp(token, "RIGHT") == 0)
291 soundSetMix(SOUND_STEREO_RIGHT);
292 else
293 printf("%cWarning! Invalid token \"%s\" on line %d.\n", 7, token, line);
294
295 // printf("\tMix %s\n", token);
296 return;
297 }
298 }
299
300 printf("%cError! Incomplete MIX statement on line %d.\n", 7, line);
301 }
302
303
304 /*--------------------------------------------------------------------------*
305 parseCommentStatement()
306
307 - parse comment
308 - write comment to output header file
309 *--------------------------------------------------------------------------*/
parseCommentStatement(void)310 void parseCommentStatement(void)
311 {
312 char *token;
313 char ch[1024];
314
315 sprintf(ch, "// ");
316
317 if (token = strtok(NULL, ""))
318 strcat(ch, token);
319
320 if (ch[strlen(ch) - 1] != '\n')
321 strcat(ch, "\n");
322
323 soundOutputComment(ch);
324 // printf("%s", ch);
325 }
326
327
328 /*--------------------------------------------------------------------------*
329 parseEndStatement()
330
331 - print sound to data file
332 - add entry for sound
333 - set state falg to end
334 *--------------------------------------------------------------------------*/
parseEndStatement(void)335 void parseEndStatement(void)
336 {
337 char *token;
338
339 if (state == STATE_END)
340 printf("%cWarning! END statement made prior to BEGIN on line %d.\n", 7, line);
341
342 if (soundPrintSound() != STATUS_SUCCESS)
343 printf("%cError! Sound not converted on line %d.\n", 7, line);
344
345 // printf("End sound\n");
346 printf(".");
347
348 if (token = strtok(NULL, seps))
349 {
350 if (token[0] == ';')
351 return;
352
353 printf("%cWarning! Unexpected token \"%s\" after END statement on line %d.\n", 7, token, line);
354 }
355
356 state = STATE_END;
357 }
358
359
360 /*--------------------------------------------------------------------------*
361 parseIncludeStatement()
362
363 - get file path to include
364 - eat the file
365 *--------------------------------------------------------------------------*/
366 int eatFile(char *ch);
parseIncludeStatement(void)367 void parseIncludeStatement(void)
368 {
369 char *token;
370
371 if (token = strtok(NULL, seps))
372 {
373 if (token[0] != ';')
374 {
375 int tempLine = line;
376 line = 1;
377
378 fixString(token);
379
380 if (absolutePath(token))
381 {
382 eatFile(token);
383 }
384 else if (path[0] != 0)
385 {
386 char ch[1024];
387
388 strcpy(ch, path);
389 strcat(ch, "\\");
390 strcat(ch, token);
391 eatFile(ch);
392 }
393 else
394 {
395 eatFile(token);
396 }
397
398 line = tempLine;
399
400 // printf("\tInclude file %s\n", token);
401
402 return;
403 }
404 }
405
406 printf("%cError! Incomplete INCLUDE statement on line %d.\n", 7, line);
407 }
408
409
410 /*--------------------------------------------------------------------------*
411 parseLine()
412
413 - parse line for command
414 *--------------------------------------------------------------------------*/
parseLine(char * ch)415 void parseLine(char *ch)
416 {
417 char *token = strtok(ch, seps);
418
419 while(token != NULL)
420 {
421 if (token[0] == ';')
422 break;
423
424 // convert string to all upper case
425 _strupr(token);
426
427 // see what the command is
428 if (strcmp(token, "PATH") == 0)
429 {
430 parsePathStatement();
431 }
432 else if (strcmp(token, "BEGIN") == 0)
433 {
434 parseBeginStatement();
435 }
436 else if (strcmp(token, "FILE") == 0)
437 {
438 parseFileStatement();
439 }
440 else if (strcmp(token, "OUTPUT") == 0)
441 {
442 parseOutputStatement();
443 }
444 else if (strcmp(token, "SAMPLERATE") == 0)
445 {
446 parseSamplerateStatement();
447 }
448 else if (strcmp(token, "LOOP") == 0)
449 {
450 parseLoopStatement();
451 }
452 else if (strcmp(token, "MIX") == 0)
453 {
454 parseMixStatement();
455 }
456 else if (strcmp(token, "END") == 0)
457 {
458 parseEndStatement();
459 }
460 else if (strcmp(token, "COMMENT") == 0)
461 {
462 parseCommentStatement();
463 }
464 else if (strcmp(token, "INCLUDE") == 0)
465 {
466 parseIncludeStatement();
467
468 // since this cause other strtok() to be called
469 // for other instances of file reads... we better reset
470 // the strtok by doing a read from the start of this string
471 strtok(ch, seps);
472 strtok(NULL, seps);
473 }
474 else
475 {
476 printf("%c\nWarning unknown token \"%s\" on line number %d!!!\n",
477 7, token, line);
478 }
479
480 token = strtok(NULL, seps);
481 }
482 }
483
484
485 /*--------------------------------------------------------------------------*
486 nextLine()
487
488 - get next line from file
489 - parse line
490 *--------------------------------------------------------------------------*/
nextLine(FILE * file)491 int nextLine(FILE *file)
492 {
493 int status;
494 char ch[256];
495
496 if (fgets(ch, 256, file))
497 {
498 parseLine(ch);
499 status = STATUS_SUCCESS;
500 }
501 else
502 {
503 if (feof(file) == 0)
504 status = STATUS_ERROR;
505 else
506 status = STATUS_EOF;
507 }
508
509 return status;
510 }
511
512
513 /*--------------------------------------------------------------------------*
514 satFile()
515
516 - read file by line
517 - send lines to parsor
518 *--------------------------------------------------------------------------*/
eatFile(char * s)519 int eatFile(char *s)
520 {
521 FILE *file;
522 int status = STATUS_ERROR;
523
524 if (file = fopen(s, "r"))
525 {
526 do
527 {
528 status = nextLine(file);
529 line++;
530 }
531 while (status == STATUS_SUCCESS);
532
533 switch (status)
534 {
535 case STATUS_EOF:
536
537 // printf("\n\nEnd of script file %s reached.\n", s);
538
539 break;
540
541 case STATUS_ERROR:
542
543 printf("%c\n\nError encountered while reading file, line %d!\n", 7, line);
544
545 break;
546 }
547
548 fclose(file);
549 }
550 else
551 {
552 printf("%c\nError, cannot open %s for reading!!!\n", 7, s);
553 }
554
555 return status;
556 }
557
558
559 /*--------------------------------------------------------------------------*
560 printBanner()
561 *--------------------------------------------------------------------------*/
printBanner(void)562 void printBanner(void)
563 {
564 printf("\n");
565 printf("sndconv.exe v1.2\n");
566 printf("Sound converter for Dolphin AX sound player.\n");
567 printf("Copyright 2001 Nintendo Technology Development, Inc.\n");
568 printf("\n");
569 }
570
571
572 /*--------------------------------------------------------------------------*
573 printUsage()
574 *--------------------------------------------------------------------------*/
printUsage(void)575 void printUsage(void)
576 {
577 printf("Usage:\n\n");
578 printf("SNDCONV <inputfile> [-option]\n");
579 printf("Where:\n");
580 printf(" <scriptfile>.......Script file (required)\n\n");
581
582 printf("Options are:\n");
583 printf(" -a.................Default output to ADPCM\n");
584 printf(" -w.................Default output to 16bit PCM\n");
585 printf(" -b.................Default output to 8bit PCM\n");
586 printf(" -h.................This help text.\n");
587 // printf(" -v.................Verbose mode.\n");
588 printf("\n\n");
589 printf("This tool generates data files for AX SP library.\n");
590
591 printf("\n");
592 }
593
594
595 /*--------------------------------------------------------------------------*
596 cleanup()
597
598 - quit output code module
599 - free libraries
600 *--------------------------------------------------------------------------*/
cleanup(void)601 void cleanup(void)
602 {
603 soundOutputQuit();
604
605 if (hDllSoundfile) FreeLibrary(hDllSoundfile);
606 if (hDllDsptool) FreeLibrary(hDllDsptool);
607 }
608
609
610 /*--------------------------------------------------------------------------*
611 loadDlls()
612
613 - load soundfile.dll and dsptool.dll
614 - get export function pointers
615 *--------------------------------------------------------------------------*/
616 // soundfile.dll exports
617 typedef int (*FUNCTION1)(u8 *path, SOUNDINFO *soundinfo);
618 typedef int (*FUNCTION2)(u8 *path, SOUNDINFO *soundinfo, void *dest);
619 FUNCTION1 getSoundInfo;
620 FUNCTION2 getSoundSamples;
621
622 // dsptool.dll exports
623 typedef void (*FUNCTION3)(u16*, u8*, ADPCMINFO*, int);
624 typedef int (*FUNCTION4)(int);
625 typedef void (*FUNCTION5)(u8 *src, ADPCMINFO *cxt, u32 samples);
626 FUNCTION3 encode;
627 FUNCTION4 getBytesForAdpcmBuffer;
628 FUNCTION4 getBytesForAdpcmSamples;
629 FUNCTION4 getNibbleAddress;
630 FUNCTION5 getLoopContext;
631
632
loadDlls(void)633 int loadDlls(void)
634 {
635 getSoundInfo = NULL;
636 getSoundSamples = NULL;
637 encode = NULL;
638 getBytesForAdpcmBuffer = NULL;
639
640 if (hDllSoundfile = LoadLibrary("soundfile.dll"))
641 {
642 getSoundInfo = (FUNCTION1)GetProcAddress(hDllSoundfile, "getSoundInfo");
643 getSoundSamples = (FUNCTION2)GetProcAddress(hDllSoundfile, "getSoundSamples");
644 }
645
646 if (hDllDsptool = LoadLibrary("dsptool.dll"))
647 {
648 encode = (FUNCTION3)GetProcAddress(hDllDsptool, "encode");
649 getBytesForAdpcmBuffer = (FUNCTION4)GetProcAddress(hDllDsptool, "getBytesForAdpcmBuffer");
650 getBytesForAdpcmSamples = (FUNCTION4)GetProcAddress(hDllDsptool, "getBytesForAdpcmSamples");
651 getNibbleAddress = (FUNCTION4)GetProcAddress(hDllDsptool, "getNibbleAddress");
652 getLoopContext = (FUNCTION5)GetProcAddress(hDllDsptool, "getLoopContext");
653 }
654
655 if (getSoundInfo && getSoundSamples && encode && getBytesForAdpcmBuffer && getNibbleAddress)
656 return STATUS_SUCCESS;
657
658 printf("\n%cError loading DLL\n", 7);
659
660 return STATUS_ERROR;
661 }
662
663
664 /*--------------------------------------------------------------------------*
665 init()
666
667 - initialize output code module
668 *--------------------------------------------------------------------------*/
init(char * s)669 int init(char *s)
670 {
671 char ch[1024];
672 char *dot;
673
674 // make a copy of the path
675 strcpy(ch, s);
676
677 // check string for forward slash
678 fixString(ch);
679
680 // terminate the path at the last dot
681 if (dot = strrchr(ch, '.'))
682 *dot = 0;
683
684 // initialize the output module with the new path
685 if (soundOutputInit(ch) == STATUS_ERROR)
686 {
687 return STATUS_ERROR;
688 }
689 else
690 {
691 // some local variables
692 line = 1;
693 state = STATE_END;
694 path[0] = 0;
695
696 return loadDlls();
697 }
698 }
699
700
701 /*--------------------------------------------------------------------------*
702 parseArgs()
703
704 - check number of arguments
705 - parse commandline
706 - set default output format and input file path
707 *--------------------------------------------------------------------------*/
parseArgs(int argc,char * argv[])708 int parseArgs(int argc, char *argv[])
709 {
710 int i;
711
712 if (argc < 2)
713 {
714 printf("\nERROR: Missing parameter\n\n");
715
716 return FALSE;
717 }
718
719 for (i = 1; i < argc; i++)
720 {
721 switch (argv[i][0])
722 {
723 case '?':
724
725 return FALSE;
726
727 break;
728
729 case '-':
730 case '/':
731 case '\\':
732
733 switch (argv[i][1])
734 {
735 case 'a':
736 case 'A':
737
738 soundSetDefaultFormat(SOUND_FORMAT_ADPCM);
739
740 break;
741
742 case 'w':
743 case 'W':
744
745 soundSetDefaultFormat(SOUND_FORMAT_PCM16);
746
747 break;
748
749 case 'b':
750 case 'B':
751
752 soundSetDefaultFormat(SOUND_FORMAT_PCM8);
753
754 break;
755
756 case 'h':
757 case 'H':
758
759 return FALSE;
760
761 break;
762
763 default:
764
765 printf("\n%cERROR: Unknown switch '%c'\n\n", 7, argv[i][1]);
766 return FALSE;
767
768 break;
769 }
770
771 break;
772
773 default:
774
775 inputPath = argv[i];
776
777 break;
778 }
779 }
780
781 if (inputPath == NULL)
782 {
783 printf("\nERROR: No input file specified!\n\n");
784 return FALSE;
785 }
786
787 return TRUE;
788 }
789
790 /*--------------------------------------------------------------------------*
791 main()
792
793 - print program banner
794 - check arguments
795 - eat script file
796 *--------------------------------------------------------------------------*/
main(int argc,char * argv[])797 int main(int argc, char *argv[])
798 {
799 int status;
800
801 printBanner();
802
803 soundSetDefaultFormat(SOUND_FORMAT_ADPCM);
804
805 if (parseArgs(argc, argv))
806 {
807 if (init(inputPath) == STATUS_SUCCESS)
808 {
809 path[0] = 0;
810 status = eatFile(inputPath);
811 printf("\n");
812 }
813 else
814 {
815 status = STATUS_ERROR;
816 }
817 }
818 else
819 {
820 printUsage();
821 status = STATUS_ERROR;
822 }
823
824 cleanup();
825
826 if (status == STATUS_ERROR)
827 return 1;
828
829 exit(0);
830 }
831