1 // VGMPlayUI.c: C Source File for the Console User Interface
2
3 // Note: In order to make MS VC6 NOT crash when using fprintf with stdout, stderr, etc.
4 // if linked to msvcrt.lib, the following project setting is important:
5 // C/C++ -> Code Generation -> Runtime libraries: Multithreaded DLL
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <stdarg.h>
11 #include <wchar.h>
12 #include <ctype.h> // for toupper
13 #include <locale.h> // for setlocale
14 #include "stdbool.h"
15 #include <math.h>
16
17 #ifdef WIN32
18 #include <conio.h>
19 #include <windows.h>
20 #else
21 #include <limits.h> // for PATH_MAX
22 #include <termios.h>
23 #include <unistd.h> // for STDIN_FILENO and usleep()
24 #include <sys/time.h> // for struct timeval in _kbhit()
25 #include <signal.h> // for signal()
26
27 #define Sleep(msec) usleep(msec * 1000)
28 #define _vsnwprintf vswprintf
29 #endif
30
31 #define printerr(x) fprintf(stderr, x)
32
33 #include "chips/mamedef.h"
34
35 #include "Stream.h"
36 #include "VGMPlay.h"
37 #include "VGMPlay_Intf.h"
38
39 #ifdef XMAS_EXTRA
40 #include "XMasFiles/XMasBonus.h"
41 #endif
42 #ifdef WS_DEMO
43 #include "XMasFiles/SWJ-SQRC01_1C.h"
44 #endif
45
46 #ifndef WIN32
47 void WaveOutLinuxCallBack(void);
48 #endif
49
50 #ifdef WIN32
51 #define DIR_CHR '\\'
52 #define DIR_STR "\\"
53 #define QMARK_CHR '\"'
54 #else
55 #define DIR_CHR '/'
56 #define DIR_STR "/"
57 #define QMARK_CHR '\''
58
59 #ifndef SHARE_PREFIX
60 #define SHARE_PREFIX "/usr/local"
61 #endif
62
63 #endif
64
65 #define APP_NAME "VGM Player"
66 #define APP_NAME_L L"VGM Player"
67
68
69 int main(int argc, char* argv[]);
70 static void RemoveNewLines(char* String);
71 static void RemoveQuotationMarks(char* String);
72 static char* GetLastDirSeparator(const char* FilePath);
73 static bool IsAbsolutePath(const char* FilePath);
74 static char* GetFileExtension(const char* FilePath);
75 static void StandardizeDirSeparators(char* FilePath);
76 #ifdef WIN32
77 static void WinNT_Check(void);
78 #endif
79 static char* GetAppFileName(void);
80 static void cls(void);
81 #ifndef WIN32
82 static void changemode(bool);
83 static int _kbhit(void);
84 static int _getch(void);
85 #endif
86 static INT8 stricmp_u(const char *string1, const char *string2);
87 static INT8 strnicmp_u(const char *string1, const char *string2, size_t count);
88 static void ReadOptions(const char* AppName);
89 static bool GetBoolFromStr(const char* TextStr);
90 #if defined(XMAS_EXTRA) || defined(WS_DEMO)
91 static bool XMas_Extra(char* FileName, bool Mode);
92 #endif
93 #ifndef WIN32
94 static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr);
95 #endif
96 static bool OpenPlayListFile(const char* FileName);
97 static bool OpenMusicFile(const char* FileName);
98 extern bool OpenVGMFile(const char* FileName);
99 extern bool OpenOtherFile(const char* FileName);
100
101 static void wprintc(const wchar_t* format, ...);
102 static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock);
103 static const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag);
104 static void ShowVGMTag(void);
105
106 static void PlayVGM_UI(void);
107 INLINE INT8 sign(double Value);
108 INLINE long int Round(double Value);
109 INLINE double RoundSpecial(double Value, double RoundTo);
110 static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate);
111
112
113 // Options Variables
114 extern UINT32 SampleRate; // Note: also used by some sound cores to
115 // determinate the chip sample rate
116
117 extern UINT32 VGMPbRate;
118 extern UINT32 VGMMaxLoop;
119 extern UINT32 CMFMaxLoop;
120 UINT32 FadeTimeN; // normal fade time
121 UINT32 FadeTimePL; // in-playlist fade time
122 extern UINT32 FadeTime;
123 UINT32 PauseTimeJ; // Pause Time for Jingles
124 UINT32 PauseTimeL; // Pause Time for Looping Songs
125 extern UINT32 PauseTime;
126 static UINT8 Show95Cmds;
127
128 extern float VolumeLevel;
129 extern bool SurroundSound;
130 extern UINT8 HardStopOldVGMs;
131 extern bool FadeRAWLog;
132 static UINT8 LogToWave;
133 //extern bool FullBufFill;
134 extern bool PauseEmulate;
135 extern bool DoubleSSGVol;
136 static UINT16 ForceAudioBuf;
137 static UINT8 OutputDevID;
138
139 extern UINT8 ResampleMode; // 00 - HQ both, 01 - LQ downsampling, 02 - LQ both
140 extern UINT8 CHIP_SAMPLING_MODE;
141 extern INT32 CHIP_SAMPLE_RATE;
142
143 extern UINT16 FMPort;
144 extern bool UseFM;
145 extern bool FMForce;
146 //extern bool FMAccurate;
147 extern bool FMBreakFade;
148 extern float FMVol;
149 extern bool FMOPL2Pan;
150
151 extern CHIPS_OPTION ChipOpts[0x02];
152
153
154 extern bool ThreadPauseEnable;
155 extern volatile bool ThreadPauseConfrm;
156 extern bool ThreadNoWait; // don't reset the timer
157 extern UINT16 AUDIOBUFFERU;
158 extern UINT32 SMPL_P_BUFFER;
159 extern char SoundLogFile[MAX_PATH];
160
161 extern UINT8 OPL_MODE;
162 extern UINT8 OPL_CHIPS;
163 //extern bool WINNT_MODE;
164 UINT8 NEED_LARGE_AUDIOBUFS;
165
166 extern char* AppPaths[8];
167 static char AppPathBuffer[MAX_PATH * 2];
168
169 static char PLFileBase[MAX_PATH];
170 static char PLFileName[MAX_PATH];
171 static UINT32 PLFileCount;
172 static char** PlayListFile;
173 static UINT32 CurPLFile;
174 static UINT8 NextPLCmd;
175 static UINT8 PLMode; // set to 1 to show Playlist text
176 static bool FirstInit;
177 extern bool AutoStopSkip;
178
179 static char VgmFileName[MAX_PATH];
180 static UINT8 FileMode;
181 extern VGM_HEADER VGMHead;
182 extern UINT32 VGMDataLen;
183 extern UINT8* VGMData;
184 extern GD3_TAG VGMTag;
185 static bool PreferJapTag;
186
187 extern volatile bool PauseThread;
188 static bool StreamStarted;
189
190 extern float MasterVol;
191
192 extern UINT32 VGMPos;
193 extern INT32 VGMSmplPos;
194 extern INT32 VGMSmplPlayed;
195 extern INT32 VGMSampleRate;
196 extern UINT32 BlocksSent;
197 extern UINT32 BlocksPlayed;
198 static bool IsRAWLog;
199 extern bool EndPlay;
200 extern bool PausePlay;
201 extern bool FadePlay;
202 extern bool ForceVGMExec;
203 extern UINT8 PlayingMode;
204
205 extern UINT32 PlayingTime;
206
207 extern UINT32 FadeStart;
208 extern UINT32 VGMMaxLoopM;
209 extern UINT32 VGMCurLoop;
210 extern float VolumeLevelM;
211 bool ErrorHappened; // used by VGMPlay.c and VGMPlay_AddFmts.c
212 extern float FinalVol;
213 extern bool ResetPBTimer;
214
215 #ifndef WIN32
216 static struct termios oldterm;
217 static bool termmode;
218 #endif
219 static volatile bool sigint = false;
220
221 UINT8 CmdList[0x100];
222
223 //extern UINT8 DISABLE_YMZ_FIX;
224 extern UINT8 IsVGMInit;
225 extern UINT16 Last95Drum; // for optvgm debugging
226 extern UINT16 Last95Max; // for optvgm debugging
227 extern UINT32 Last95Freq; // for optvgm debugging
228
229 static bool PrintMSHours;
230
231 #ifdef WIN32
signal_handler(DWORD dwCtrlType)232 static BOOL WINAPI signal_handler(DWORD dwCtrlType)
233 {
234 switch(dwCtrlType)
235 {
236 case CTRL_C_EVENT: // Ctrl + C
237 case CTRL_CLOSE_EVENT: // close console window via X button
238 case CTRL_BREAK_EVENT: // Ctrl + Break
239 sigint = true;
240 return TRUE;
241 case CTRL_LOGOFF_EVENT:
242 case CTRL_SHUTDOWN_EVENT:
243 return FALSE;
244 }
245 return FALSE;
246 }
247 #else
signal_handler(int signal)248 static void signal_handler(int signal)
249 {
250 if(signal == SIGINT)
251 sigint = true;
252 }
253 #endif
254
main(int argc,char * argv[])255 int main(int argc, char* argv[])
256 {
257 int argbase;
258 int ErrRet;
259 char* AppName;
260 #if defined(XMAS_EXTRA) || defined(WS_DEMO)
261 bool XMasEnable;
262 #endif
263 char* AppPathPtr;
264 const char* StrPtr;
265 const char* FileExt;
266 UINT8 CurPath;
267 UINT32 ChrPos;
268
269 // set locale to "current system locale"
270 // (makes Unicode characters (like umlauts) work under Linux and fixes some
271 // Unicode -> ANSI conversions)
272 setlocale(LC_CTYPE, "");
273
274 #ifndef WIN32
275 tcgetattr(STDIN_FILENO, &oldterm);
276 termmode = false;
277 signal(SIGINT, signal_handler);
278 #else
279 SetConsoleCtrlHandler(signal_handler, TRUE);
280 #endif
281
282 if (argc > 1)
283 {
284 if (! stricmp_u(argv[1], "-v") || ! stricmp_u(argv[1], "--version"))
285 {
286 printf("VGMPlay %s"
287 #if defined(APLHA)
288 " alpha"
289 #elif defined(BETA)
290 " beta"
291 #endif
292 ", supports VGM %s\n", VGMPLAY_VER_STR, VGM_VER_STR);
293 return 0;
294 }
295 }
296
297 #ifdef SET_CONSOLE_TITLE
298 #ifdef WIN32
299 SetConsoleTitle(APP_NAME); // Set Windows Console Title
300 #else
301 printf("\x1B]0;%s\x07", APP_NAME); // Set xterm/rxvt Terminal Title
302 #endif
303 #endif
304
305 printf(APP_NAME);
306 #ifdef XMAS_EXTRA
307 printf(" - XMas Release");
308 #endif
309 printf("\n----------\n");
310
311 //if (argv[0x00] == NULL)
312 // printf("Argument \"Application-Name\" is NULL!\n");
313
314 // Warning! It's dangerous to use Argument 0!
315 // AppName may be "vgmplay" instead of "vgmplay.exe"
316
317 VGMPlay_Init();
318
319 // Note: Paths are checked from last to first.
320 CurPath = 0x00;
321 AppPathPtr = AppPathBuffer;
322 #ifndef WIN32
323 // Path 1: global share directory
324 AppPaths[CurPath] = SHARE_PREFIX "/share/vgmplay/";
325 CurPath ++;
326 #endif
327
328 // Path 2: exe's directory
329 AppName = GetAppFileName(); // "C:\VGMPlay\VGMPlay.exe"
330 // Note: GetAppFileName always returns native directory separators.
331 StrPtr = strrchr(AppName, DIR_CHR);
332 if (StrPtr != NULL)
333 {
334 ChrPos = StrPtr + 1 - AppName;
335 strncpy(AppPathPtr, AppName, ChrPos);
336 AppPathPtr[ChrPos] = 0x00; // "C:\VGMPlay\"
337 AppPaths[CurPath] = AppPathPtr;
338 CurPath ++;
339 AppPathPtr += ChrPos + 1;
340 }
341
342 #ifndef WIN32
343 // Path 3: home directory
344 StrPtr = getenv("XDG_CONFIG_HOME");
345 if (StrPtr != NULL && StrPtr[0] == '\0')
346 {
347 strcpy(AppPathPtr, StrPtr);
348 }
349 else
350 {
351 StrPtr = getenv("HOME");
352 if (StrPtr != NULL)
353 strcpy(AppPathPtr, StrPtr);
354 else
355 strcpy(AppPathPtr, "");
356 strcat(AppPathPtr, "/.config");
357 }
358 strcat(AppPathPtr, "/vgmplay/");
359 AppPaths[CurPath] = AppPathPtr;
360 CurPath ++;
361 AppPathPtr += strlen(AppPathPtr) + 1;
362 #endif
363
364 // Path 4: working directory ("\0")
365 AppPathPtr[0] = '\0';
366 AppPaths[CurPath] = AppPathPtr;
367 CurPath ++;
368
369 #if 0 // set to 1 to print all selected search paths
370 CurPath = 0;
371 while(AppPaths[CurPath] != NULL)
372 {
373 printf("Path %u: %s\n", CurPath + 1, AppPaths[CurPath]);
374 CurPath ++;
375 }
376 #endif
377
378 ReadOptions(AppName);
379 VGMPlay_Init2();
380
381 ErrRet = 0;
382 argbase = 0x01;
383 while(argbase < argc)
384 {
385 if (! strnicmp_u(argv[argbase], "-LogSound:", 10))
386 {
387 LogToWave = (UINT8)strtoul(argv[argbase] + 10, NULL, 0);
388 argbase ++;
389 }
390 else if (! strnicmp_u(argv[argbase], "-DeviceId:", 10))
391 {
392 OutputDevID = (UINT8)strtoul(argv[argbase] + 10, NULL, 0);
393 argbase ++;
394 }
395 else
396 {
397 break;
398 }
399 }
400
401 printf("\nFile Name:\t");
402 if (argc <= argbase)
403 {
404 #ifdef WIN32
405 INT32 OldCP;
406
407 OldCP = GetConsoleCP();
408
409 // Set the Console Input Codepage to ANSI.
410 // The Output Codepage must be left at OEM, else the displayed characters are wrong.
411 ChrPos = GetACP();
412 ErrRet = SetConsoleCP(ChrPos); // set input codepage
413 //ErrRet = SetConsoleOutputCP(ChrPos); // set output codepage (would be a bad idea)
414
415 StrPtr = fgets(VgmFileName, MAX_PATH, stdin);
416 if (StrPtr == NULL)
417 VgmFileName[0] = '\0';
418
419 // Playing with the console font resets the Console Codepage to OEM, so I have to
420 // convert the file name in this case.
421 if (GetConsoleCP() == GetOEMCP())
422 OemToChar(VgmFileName, VgmFileName); // OEM -> ANSI conversion
423
424 // This fixes the display of non-ANSI characters.
425 ErrRet = SetConsoleCP(OldCP);
426
427 // This codepage stuff drives me insane.
428 // Debug and Release build behave differently - WHAT??
429 //
430 // There a list of behaviours.
431 // Debug and Release were tested by dropping a file on it and via Visual Studio.
432 //
433 // Input CP 850, Output CP 850
434 // Debug build: Dynamite D�x
435 // Release build: Dynamite D�x
436 // Input CP 1252, Output CP 850
437 // Debug build: Dynamite D�x
438 // Release build: Dynamite D�x
439 // Input CP 850, Output CP 1252
440 // Debug build: Dynamite D�x [tag display wrong]
441 // Release build: Dynamite D�x [tag display wrong]
442 // Input CP 1252, Output CP 1252
443 // Debug build: Dynamite D�x [tag display wrong]
444 // Release build: Dynamite D�x [tag display wrong]
445 #else
446 StrPtr = fgets(VgmFileName, MAX_PATH, stdin);
447 if (StrPtr == NULL)
448 VgmFileName[0] = '\0';
449 #endif
450
451 RemoveNewLines(VgmFileName);
452 RemoveQuotationMarks(VgmFileName);
453 }
454 else
455 {
456 // The argument should already use the ANSI codepage.
457 strcpy(VgmFileName, argv[argbase]);
458 printf("%s\n", VgmFileName);
459 }
460 if (! strlen(VgmFileName))
461 goto ExitProgram;
462 StandardizeDirSeparators(VgmFileName);
463
464 #if defined(XMAS_EXTRA) || defined(WS_DEMO)
465 XMasEnable = XMas_Extra(VgmFileName, 0x00);
466 #endif
467
468 #if 0
469 { // Print hex characters of file name (for vgm-player script debugging)
470 const char* CurChr;
471
472 #ifdef WIN32
473 printf("Input CP: %d, Output CP: %d\n", GetConsoleCP(), GetConsoleOutputCP());
474 #endif
475 printf("VgmFileName: ");
476
477 CurChr = VgmFileName;
478 while(*CurChr != '\0')
479 {
480 printf("%02X ", (UINT8)*CurChr);
481 CurChr ++;
482 }
483 printf("%02X\n", (UINT8)*CurChr);
484 _getch();
485 }
486 #endif
487 #if 0
488 { // strip spaces and \n (fixed bugs with vgm-player script with un-7z)
489 char* CurChr;
490
491 // trim \n and spaces off
492 CurChr = strchr(VgmFileName, '\n');
493 if (CurChr != NULL)
494 *CurChr = '\0';
495 CurChr = VgmFileName + strlen(VgmFileName) - 1;
496 while(CurChr > VgmFileName && *CurChr == ' ')
497 *(CurChr --) = '\0';
498 }
499 #endif
500
501 FirstInit = true;
502 StreamStarted = false;
503 FileExt = GetFileExtension(VgmFileName);
504 if (FileExt == NULL || stricmp_u(FileExt, "m3u"))
505 PLMode = 0x00;
506 else
507 PLMode = 0x01;
508
509 if (! PLMode)
510 {
511 PLFileCount = 0x00;
512 CurPLFile = 0x00;
513 // no Play List File
514 if (! OpenMusicFile(VgmFileName))
515 {
516 printerr("Error opening the file!\n");
517 if (argv[0][1] == ':')
518 _getch();
519 ErrRet = 1;
520 goto ExitProgram;
521 }
522 printf("\n");
523
524 ErrorHappened = false;
525 FadeTime = FadeTimeN;
526 PauseTime = PauseTimeL;
527 PrintMSHours = (VGMHead.lngTotalSamples >= 158760000); // 44100 smpl * 60 sec * 60 min
528 ShowVGMTag();
529 NextPLCmd = 0x80;
530 PlayVGM_UI();
531
532 CloseVGMFile();
533 }
534 else
535 {
536 strcpy(PLFileName, VgmFileName);
537 if (! OpenPlayListFile(PLFileName))
538 {
539 printerr("Error opening the playlist!\n");
540 if (argv[0][1] == ':')
541 _getch();
542 ErrRet = 1;
543 goto ExitProgram;
544 }
545
546 for (CurPLFile = 0x00; CurPLFile < PLFileCount; CurPLFile ++)
547 {
548 cls();
549 printf(APP_NAME);
550 printf("\n----------\n");
551 printf("\nPlaylist File:\t%s\n", PLFileName);
552 printf("Playlist Entry:\t%u / %u\n", CurPLFile + 1, PLFileCount);
553 printf("File Name:\t%s\n", PlayListFile[CurPLFile]);
554
555 if (IsAbsolutePath(PlayListFile[CurPLFile]))
556 {
557 strcpy(VgmFileName, PlayListFile[CurPLFile]);
558 }
559 else
560 {
561 strcpy(VgmFileName, PLFileBase);
562 strcat(VgmFileName, PlayListFile[CurPLFile]);
563 }
564
565 if (! OpenMusicFile(VgmFileName))
566 {
567 printf("Error opening the file!\n");
568 _getch();
569 while(_kbhit())
570 _getch();
571 continue;
572 }
573 printf("\n");
574
575 ErrorHappened = false;
576 if (CurPLFile < PLFileCount - 1)
577 FadeTime = FadeTimePL;
578 else
579 FadeTime = FadeTimeN;
580 PauseTime = VGMHead.lngLoopOffset ? PauseTimeL : PauseTimeJ;
581 PrintMSHours = (VGMHead.lngTotalSamples >= 158760000);
582 ShowVGMTag();
583 NextPLCmd = 0x00;
584 PlayVGM_UI();
585 CloseVGMFile();
586
587 if (ErrorHappened)
588 {
589 if (_kbhit())
590 _getch();
591 _getch();
592 ErrorHappened = false;
593 }
594 if (NextPLCmd == 0xFF)
595 break;
596 else if (NextPLCmd == 0x01)
597 CurPLFile -= 0x02; // Jump to last File (-2 + 1 = -1)
598 }
599 }
600
601 if (ErrorHappened && argv[0][1] == ':')
602 {
603 if (_kbhit())
604 _getch();
605 _getch();
606 }
607
608 #ifdef _DEBUG
609 printf("Press any key ...");
610 _getch();
611 #endif
612
613 ExitProgram:
614 #if defined(XMAS_EXTRA) || defined(WS_DEMO)
615 if (XMasEnable)
616 XMas_Extra(VgmFileName, 0x01);
617 #endif
618 #ifndef WIN32
619 changemode(false);
620 #ifdef SET_CONSOLE_TITLE
621 // printf("\x1B]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}\x07", APP_NAME); // Reset xterm/rxvt Terminal Title
622 #endif
623 #endif
624 VGMPlay_Deinit();
625 free(AppName);
626
627 return ErrRet;
628 }
629
RemoveNewLines(char * String)630 static void RemoveNewLines(char* String)
631 {
632 char* StrPtr;
633
634 StrPtr = String + strlen(String) - 1;
635 while(StrPtr >= String && (UINT8)*StrPtr < 0x20)
636 {
637 *StrPtr = '\0';
638 StrPtr --;
639 }
640
641 return;
642 }
643
RemoveQuotationMarks(char * String)644 static void RemoveQuotationMarks(char* String)
645 {
646 UINT32 StrLen;
647 char* EndQMark;
648
649 if (String[0x00] != QMARK_CHR)
650 return;
651
652 StrLen = strlen(String);
653 memmove(String, String + 0x01, StrLen); // Remove first char
654 EndQMark = strrchr(String, QMARK_CHR);
655 if (EndQMark != NULL)
656 *EndQMark = 0x00; // Remove last Quot.-Mark
657
658 return;
659 }
660
GetLastDirSeparator(const char * FilePath)661 static char* GetLastDirSeparator(const char* FilePath)
662 {
663 char* SepPos1;
664 char* SepPos2;
665
666 SepPos1 = strrchr(FilePath, '/');
667 SepPos2 = strrchr(FilePath, '\\');
668 if (SepPos1 < SepPos2)
669 return SepPos2;
670 else
671 return SepPos1;
672 }
673
IsAbsolutePath(const char * FilePath)674 static bool IsAbsolutePath(const char* FilePath)
675 {
676 #ifdef WIN32
677 if (FilePath[0] == '\0')
678 return false; // empty string
679 if (FilePath[1] == ':')
680 return true; // Device Path: C:\path
681 if (! strncmp(FilePath, "\\\\", 2))
682 return true; // Network Path: \\computername\path
683 #else
684 if (FilePath[0] == '/')
685 return true; // absolute UNIX path
686 #endif
687 return false;
688 }
689
GetFileExtension(const char * FilePath)690 static char* GetFileExtension(const char* FilePath)
691 {
692 char* DirSepPos;
693 char* ExtDotPos;
694
695 DirSepPos = GetLastDirSeparator(FilePath);
696 if (DirSepPos == NULL)
697 DirSepPos = (char*)FilePath;
698 ExtDotPos = strrchr(DirSepPos, '.');
699 if (ExtDotPos == NULL)
700 return NULL;
701 else
702 return ExtDotPos + 1;
703 }
704
StandardizeDirSeparators(char * FilePath)705 static void StandardizeDirSeparators(char* FilePath)
706 {
707 char* CurChr;
708
709 CurChr = FilePath;
710 while(*CurChr != '\0')
711 {
712 if (*CurChr == '\\' || *CurChr == '/')
713 *CurChr = DIR_CHR;
714 CurChr ++;
715 }
716
717 return;
718 }
719
720 #ifdef WIN32
WinNT_Check(void)721 static void WinNT_Check(void)
722 {
723 OSVERSIONINFO VerInf;
724
725 VerInf.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
726 GetVersionEx(&VerInf);
727 //WINNT_MODE = (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT);
728
729 /* Following Systems need larger Audio Buffers:
730 - Windows 95 (500+ ms)
731 - Windows Vista (200+ ms)
732 Tested Systems:
733 - Windows 95B
734 - Windows 98 SE
735 - Windows 2000
736 - Windows XP (32-bit)
737 - Windows Vista (32-bit)
738 - Windows 7 (64-bit)
739 */
740
741 NEED_LARGE_AUDIOBUFS = 0;
742 if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
743 {
744 if (VerInf.dwMajorVersion == 4 && VerInf.dwMinorVersion == 0)
745 NEED_LARGE_AUDIOBUFS = 50; // Windows 95
746 }
747 else if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT)
748 {
749 if (VerInf.dwMajorVersion == 6 && VerInf.dwMinorVersion == 0)
750 NEED_LARGE_AUDIOBUFS = 20; // Windows Vista
751 }
752
753 return;
754 }
755 #endif
756
GetAppFileName(void)757 static char* GetAppFileName(void)
758 {
759 char* AppPath;
760 int RetVal;
761
762 AppPath = (char*)malloc(MAX_PATH * sizeof(char));
763 #ifdef WIN32
764 RetVal = GetModuleFileName(NULL, AppPath, MAX_PATH);
765 if (! RetVal)
766 AppPath[0] = '\0';
767 #else
768 RetVal = readlink("/proc/self/exe", AppPath, MAX_PATH);
769 if (RetVal == -1)
770 AppPath[0] = '\0';
771 #endif
772
773 return AppPath;
774 }
775
cls(void)776 static void cls(void)
777 {
778 #ifdef WIN32
779 // CLS-Function from the MSDN Help
780 HANDLE hConsole;
781 COORD coordScreen = {0, 0};
782 BOOL bSuccess;
783 DWORD cCharsWritten;
784 CONSOLE_SCREEN_BUFFER_INFO csbi;
785 DWORD dwConSize;
786
787 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
788
789 // get the number of character cells in the current buffer
790 bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
791
792 // fill the entire screen with blanks
793 dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
794 bSuccess = FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen,
795 &cCharsWritten);
796
797 // get the current text attribute
798 //bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
799
800 // now set the buffer's attributes accordingly
801 //bSuccess = FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen,
802 // &cCharsWritten);
803
804 // put the cursor at (0, 0)
805 bSuccess = SetConsoleCursorPosition(hConsole, coordScreen);
806 #else
807 int retVal;
808
809 retVal = system("clear");
810 #endif
811
812 return;
813 }
814
815 #ifndef WIN32
816
changemode(bool dir)817 static void changemode(bool dir)
818 {
819 static struct termios newterm;
820
821 if (termmode == dir)
822 return;
823
824 if (dir)
825 {
826 newterm = oldterm;
827 newterm.c_lflag &= ~(ICANON | ECHO);
828 tcsetattr(STDIN_FILENO, TCSANOW, &newterm);
829 }
830 else
831 {
832 tcsetattr(STDIN_FILENO, TCSANOW, &oldterm);
833 }
834 termmode = dir;
835
836 return;
837 }
838
_kbhit(void)839 static int _kbhit(void)
840 {
841 struct timeval tv;
842 fd_set rdfs;
843 int kbret;
844 bool needchg;
845
846 needchg = (! termmode);
847 if (needchg)
848 changemode(true);
849 tv.tv_sec = 0;
850 tv.tv_usec = 0;
851
852 FD_ZERO(&rdfs);
853 FD_SET(STDIN_FILENO, &rdfs);
854
855 select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
856 kbret = FD_ISSET(STDIN_FILENO, &rdfs);
857 if (needchg)
858 changemode(false);
859
860 return kbret;
861 }
862
_getch(void)863 static int _getch(void)
864 {
865 int ch;
866 bool needchg;
867
868 needchg = (! termmode);
869 if (needchg)
870 changemode(true);
871 ch = getchar();
872 if (needchg)
873 changemode(false);
874
875 return ch;
876 }
877 #endif
878
stricmp_u(const char * string1,const char * string2)879 static INT8 stricmp_u(const char *string1, const char *string2)
880 {
881 // my own stricmp, because VC++6 doesn't find _stricmp when compiling without
882 // standard libraries
883 const char* StrPnt1;
884 const char* StrPnt2;
885 char StrChr1;
886 char StrChr2;
887
888 StrPnt1 = string1;
889 StrPnt2 = string2;
890 while(true)
891 {
892 StrChr1 = toupper(*StrPnt1);
893 StrChr2 = toupper(*StrPnt2);
894
895 if (StrChr1 < StrChr2)
896 return -1;
897 else if (StrChr1 > StrChr2)
898 return +1;
899 if (StrChr1 == 0x00)
900 return 0;
901
902 StrPnt1 ++;
903 StrPnt2 ++;
904 }
905
906 return 0;
907 }
908
strnicmp_u(const char * string1,const char * string2,size_t count)909 static INT8 strnicmp_u(const char *string1, const char *string2, size_t count)
910 {
911 // my own strnicmp, because GCC doesn't seem to have _strnicmp
912 const char* StrPnt1;
913 const char* StrPnt2;
914 char StrChr1;
915 char StrChr2;
916 size_t CurChr;
917
918 StrPnt1 = string1;
919 StrPnt2 = string2;
920 CurChr = 0x00;
921 while(CurChr < count)
922 {
923 StrChr1 = toupper(*StrPnt1);
924 StrChr2 = toupper(*StrPnt2);
925
926 if (StrChr1 < StrChr2)
927 return -1;
928 else if (StrChr1 > StrChr2)
929 return +1;
930 if (StrChr1 == 0x00)
931 return 0;
932
933 StrPnt1 ++;
934 StrPnt2 ++;
935 CurChr ++;
936 }
937
938 return 0;
939 }
940
ReadOptions(const char * AppName)941 static void ReadOptions(const char* AppName)
942 {
943 const UINT8 CHN_COUNT[CHIP_COUNT] =
944 { 0x04, 0x09, 0x06, 0x08, 0x10, 0x08, 0x03, 0x00,
945 0x00, 0x09, 0x09, 0x09, 0x12, 0x00, 0x0C, 0x08,
946 0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00,
947 0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10,
948 0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20,
949 0x04
950 };
951 const UINT8 CHN_MASK_CNT[CHIP_COUNT] =
952 { 0x04, 0x0E, 0x07, 0x08, 0x10, 0x08, 0x03, 0x06,
953 0x06, 0x0E, 0x0E, 0x0E, 0x17, 0x18, 0x0C, 0x08,
954 0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00,
955 0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10,
956 0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20,
957 0x04
958 };
959 const char* FNList[3];
960 char* FileName;
961 FILE* hFile;
962 char TempStr[0x40];
963 UINT32 StrLen;
964 UINT32 TempLng;
965 char* LStr;
966 char* RStr;
967 UINT8 IniSection;
968 UINT8 CurChip;
969 CHIP_OPTS* TempCOpt;
970 CHIP_OPTS* TempCOpt2;
971 UINT8 CurChn;
972 char* TempPnt;
973 bool TempFlag;
974
975 // most defaults are set by VGMPlay_Init()
976 FadeTimeN = FadeTime;
977 PauseTimeJ = PauseTime;
978 PauseTimeL = 0;
979 Show95Cmds = 0x00;
980 LogToWave = 0x00;
981 OutputDevID = 0;
982 ForceAudioBuf = 0x00;
983 PreferJapTag = false;
984
985 if (AppName == NULL)
986 {
987 printerr("Argument \"Application-Path\" is NULL!\nSkip loading INI.\n");
988 return;
989 }
990
991 // AppName: "C:\VGMPlay\VGMPlay.exe"
992 RStr = strrchr(AppName, DIR_CHR);
993 if (RStr != NULL)
994 RStr ++;
995 else
996 RStr = (char*)AppName;
997 FileName = (char*)malloc(strlen(RStr) + 0x05); // ".ini" + 00
998 strcpy(FileName, RStr);
999 // FileName: "VGMPlay.exe"
1000
1001 RStr = GetFileExtension(FileName);
1002 if (RStr == NULL)
1003 {
1004 RStr = FileName + strlen(FileName);
1005 *RStr = '.';
1006 RStr ++;
1007 }
1008 strcpy(RStr, "ini");
1009 // FileName: "VGMPlay.ini" or "vgmplay.ini"
1010
1011 // on Linux platforms, it searches for "vgmplay.ini" first and
1012 // file names are case sensitive
1013 FNList[0] = FileName;
1014 FNList[1] = "VGMPlay.ini";
1015 FNList[2] = NULL;
1016 LStr = FileName;
1017 FileName = FindFile_List(FNList);
1018 free(LStr);
1019 if (FileName == NULL)
1020 {
1021 printerr("Failed to load INI.\n");
1022 return;
1023 }
1024 hFile = fopen(FileName, "rt");
1025 free(FileName);
1026 if (hFile == NULL)
1027 {
1028 printerr("Failed to load INI.\n");
1029 return;
1030 }
1031
1032 IniSection = 0x00;
1033 while(! feof(hFile))
1034 {
1035 LStr = fgets(TempStr, 0x40, hFile);
1036 if (LStr == NULL)
1037 break;
1038 if (TempStr[0x00] == ';') // Comment line
1039 continue;
1040
1041 StrLen = strlen(TempStr) - 0x01;
1042 //if (TempStr[StrLen] == '\n')
1043 // TempStr[StrLen] = '\0';
1044 while(TempStr[StrLen] < 0x20)
1045 {
1046 TempStr[StrLen] = '\0';
1047 if (! StrLen)
1048 break;
1049 StrLen --;
1050 }
1051 if (! StrLen)
1052 continue;
1053 StrLen ++;
1054
1055 LStr = &TempStr[0x00];
1056 while(*LStr == ' ')
1057 LStr ++;
1058 if (LStr[0x00] == ';') // Comment line
1059 continue;
1060
1061 if (LStr[0x00] == '[')
1062 RStr = strchr(TempStr, ']');
1063 else
1064 RStr = strchr(TempStr, '=');
1065 if (RStr == NULL)
1066 continue;
1067
1068 if (LStr[0x00] == '[')
1069 {
1070 // Line pattern: [Group]
1071 LStr ++;
1072 RStr = strchr(TempStr, ']');
1073 if (RStr != NULL)
1074 RStr[0x00] = '\0';
1075
1076 if (! stricmp_u(LStr, "General"))
1077 {
1078 IniSection = 0x00;
1079 }
1080 else
1081 {
1082 IniSection = 0xFF;
1083 for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++)
1084 {
1085 if (! stricmp_u(LStr, GetChipName(CurChip)))
1086 {
1087 IniSection = 0x80 | CurChip;
1088 break;
1089 }
1090 }
1091 if (IniSection == 0xFF)
1092 continue;
1093 }
1094 }
1095 else
1096 {
1097 // Line pattern: Option = Value
1098 TempLng = RStr - TempStr;
1099 TempStr[TempLng] = '\0';
1100
1101 // Prepare Strings (trim the spaces)
1102 RStr = &TempStr[TempLng - 0x01];
1103 while(*RStr == ' ')
1104 *(RStr --) = '\0';
1105
1106 RStr = &TempStr[StrLen - 0x01];
1107 while(*RStr == ' ')
1108 *(RStr --) = '\0';
1109 RStr = &TempStr[TempLng + 0x01];
1110 while(*RStr == ' ')
1111 RStr ++;
1112
1113 switch(IniSection)
1114 {
1115 case 0x00: // General Sction
1116 if (! stricmp_u(LStr, "SampleRate"))
1117 {
1118 SampleRate = strtoul(RStr, NULL, 0);
1119 }
1120 else if (! stricmp_u(LStr, "PlaybackRate"))
1121 {
1122 VGMPbRate = strtoul(RStr, NULL, 0);
1123 }
1124 else if (! stricmp_u(LStr, "DoubleSSGVol"))
1125 {
1126 DoubleSSGVol = GetBoolFromStr(RStr);
1127 }
1128 else if (! stricmp_u(LStr, "PreferJapTag"))
1129 {
1130 PreferJapTag = GetBoolFromStr(RStr);
1131 }
1132 else if (! stricmp_u(LStr, "FadeTime"))
1133 {
1134 FadeTimeN = strtoul(RStr, NULL, 0);
1135 }
1136 else if (! stricmp_u(LStr, "FadeTimePL"))
1137 {
1138 FadeTimePL = strtoul(RStr, NULL, 0);
1139 }
1140 else if (! stricmp_u(LStr, "JinglePause"))
1141 {
1142 PauseTimeJ = strtoul(RStr, NULL, 0);
1143 }
1144 else if (! stricmp_u(LStr, "HardStopOld"))
1145 {
1146 HardStopOldVGMs = (UINT8)strtoul(RStr, &TempPnt, 0);
1147 if (TempPnt == RStr)
1148 HardStopOldVGMs = GetBoolFromStr(RStr) ? 0x01 : 0x00;
1149 }
1150 else if (! stricmp_u(LStr, "FadeRAWLogs"))
1151 {
1152 FadeRAWLog = GetBoolFromStr(RStr);
1153 }
1154 else if (! stricmp_u(LStr, "Volume"))
1155 {
1156 VolumeLevel = (float)strtod(RStr, NULL);
1157 }
1158 else if (! stricmp_u(LStr, "LogSound"))
1159 {
1160 //LogToWave = GetBoolFromStr(RStr);
1161 LogToWave = (UINT8)strtoul(RStr, NULL, 0);
1162 }
1163 else if (! stricmp_u(LStr, "MaxLoops"))
1164 {
1165 VGMMaxLoop = strtoul(RStr, NULL, 0);
1166 }
1167 else if (! stricmp_u(LStr, "MaxLoopsCMF"))
1168 {
1169 CMFMaxLoop = strtoul(RStr, NULL, 0);
1170 }
1171 else if (! stricmp_u(LStr, "ResamplingMode"))
1172 {
1173 ResampleMode = (UINT8)strtol(RStr, NULL, 0);
1174 }
1175 else if (! stricmp_u(LStr, "ChipSmplMode"))
1176 {
1177 CHIP_SAMPLING_MODE = (UINT8)strtol(RStr, NULL, 0);
1178 }
1179 else if (! stricmp_u(LStr, "ChipSmplRate"))
1180 {
1181 CHIP_SAMPLE_RATE = strtol(RStr, NULL, 0);
1182 }
1183 else if (! stricmp_u(LStr, "OutputDevice"))
1184 {
1185 OutputDevID = (UINT8)strtol(RStr, NULL, 0);
1186 }
1187 else if (! stricmp_u(LStr, "AudioBuffers"))
1188 {
1189 ForceAudioBuf = (UINT16)strtol(RStr, NULL, 0);
1190 if (ForceAudioBuf < 0x04)
1191 ForceAudioBuf = 0x00;
1192 }
1193 else if (! stricmp_u(LStr, "SurroundSound"))
1194 {
1195 SurroundSound = GetBoolFromStr(RStr);
1196 }
1197 else if (! stricmp_u(LStr, "EmulatePause"))
1198 {
1199 PauseEmulate = GetBoolFromStr(RStr);
1200 }
1201 else if (! stricmp_u(LStr, "ShowStreamCmds"))
1202 {
1203 Show95Cmds = (UINT8)strtol(RStr, NULL, 0);
1204 }
1205 else if (! stricmp_u(LStr, "FMPort"))
1206 {
1207 FMPort = (UINT16)strtoul(RStr, NULL, 16);
1208 }
1209 else if (! stricmp_u(LStr, "FMForce"))
1210 {
1211 FMForce = GetBoolFromStr(RStr);
1212 }
1213 else if (! stricmp_u(LStr, "FMVolume"))
1214 {
1215 FMVol = (float)strtod(RStr, NULL);
1216 }
1217 else if (! stricmp_u(LStr, "FMOPL2Pan"))
1218 {
1219 FMOPL2Pan = GetBoolFromStr(RStr);
1220 }
1221 /*else if (! stricmp_u(LStr, "AccurateFM"))
1222 {
1223 FMAccurate = GetBoolFromStr(RStr);
1224 }*/
1225 else if (! stricmp_u(LStr, "FMSoftStop"))
1226 {
1227 FMBreakFade = GetBoolFromStr(RStr);
1228 }
1229 break;
1230 case 0x80: // SN76496
1231 case 0x81: // YM2413
1232 case 0x82: // YM2612
1233 case 0x83: // YM2151
1234 case 0x84: // SegaPCM
1235 case 0x85: // RF5C68
1236 case 0x86: // YM2203
1237 case 0x87: // YM2608
1238 case 0x88: // YM2610
1239 case 0x89: // YM3812
1240 case 0x8A: // YM3526
1241 case 0x8B: // Y8950
1242 case 0x8C: // YMF262
1243 case 0x8D: // YMF278B
1244 case 0x8E: // YMF271
1245 case 0x8F: // YMZ280B
1246 case 0x90: // RF5C164
1247 case 0x91: // PWM
1248 case 0x92: // AY8910
1249 case 0x93: // GameBoy
1250 case 0x94: // NES
1251 case 0x95: // MultiPCM
1252 case 0x96: // UPD7759
1253 case 0x97: // OKIM6258
1254 case 0x98: // OKIM6295
1255 case 0x99: // K051649
1256 case 0x9A: // K054539
1257 case 0x9B: // HuC6280
1258 case 0x9C: // C140
1259 case 0x9D: // K053260
1260 case 0x9E: // Pokey
1261 case 0x9F: // QSound
1262 case 0xA0: // SCSP
1263 case 0xA1: // WonderSwan
1264 case 0xA2: // VSU
1265 case 0xA3: // SAA1099
1266 case 0xA4: // ES5503
1267 case 0xA5: // ES5506
1268 case 0xA6: // X1_010
1269 case 0xA7: // C352
1270 case 0xA8: // GA20
1271 CurChip = IniSection & 0x7F;
1272 TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00] + CurChip;
1273
1274 if (! stricmp_u(LStr, "Disabled"))
1275 {
1276 TempCOpt->Disabled = GetBoolFromStr(RStr);
1277 }
1278 else if (! stricmp_u(LStr, "EmulatorType"))
1279 {
1280 TempCOpt->EmuCore = (UINT8)strtol(RStr, NULL, 0);
1281 }
1282 else if (! stricmp_u(LStr, "MuteMask"))
1283 {
1284 if (! CHN_COUNT[CurChip])
1285 break; // must use MuteMaskFM and MuteMask???
1286 TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0);
1287 if (CHN_MASK_CNT[CurChip] < 0x20)
1288 TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1;
1289 }
1290 else if (! strnicmp_u(LStr, "MuteCh", 0x06))
1291 {
1292 if (! CHN_COUNT[CurChip])
1293 break; // must use MuteFM and Mute???
1294 CurChn = (UINT8)strtol(LStr + 0x06, &TempPnt, 0);
1295 if (TempPnt == NULL || *TempPnt)
1296 break;
1297 if (CurChn >= CHN_COUNT[CurChip])
1298 break;
1299 TempFlag = GetBoolFromStr(RStr);
1300 TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
1301 TempCOpt->ChnMute1 |= TempFlag << CurChn;
1302 }
1303 else
1304 {
1305 switch(CurChip)
1306 {
1307 //case 0x00: // SN76496
1308 case 0x02: // YM2612
1309 if (! stricmp_u(LStr, "MuteDAC"))
1310 {
1311 CurChn = 0x06;
1312 TempFlag = GetBoolFromStr(RStr);
1313 TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
1314 TempCOpt->ChnMute1 |= TempFlag << CurChn;
1315 }
1316 else if (! stricmp_u(LStr, "DACHighpass"))
1317 {
1318 TempFlag = GetBoolFromStr(RStr);
1319 TempCOpt->SpecialFlags &= ~(0x01 << 0);
1320 TempCOpt->SpecialFlags |= TempFlag << 0;
1321 }
1322 else if (! stricmp_u(LStr, "SSG-EG"))
1323 {
1324 TempFlag = GetBoolFromStr(RStr);
1325 TempCOpt->SpecialFlags &= ~(0x01 << 1);
1326 TempCOpt->SpecialFlags |= TempFlag << 1;
1327 }
1328 else if (! stricmp_u(LStr, "PseudoStereo"))
1329 {
1330 TempFlag = GetBoolFromStr(RStr);
1331 TempCOpt->SpecialFlags &= ~(0x01 << 2);
1332 TempCOpt->SpecialFlags |= TempFlag << 2;
1333 }
1334 else if (! stricmp_u(LStr, "NukedType"))
1335 {
1336 TempLng = (UINT32)strtoul(RStr, NULL, 0) & 0x03;
1337 TempCOpt->SpecialFlags &= ~(0x03 << 3);
1338 TempCOpt->SpecialFlags |= TempLng << 3;
1339 }
1340 break;
1341 //case 0x03: // YM2151
1342 //case 0x04: // SegaPCM
1343 //case 0x05: // RF5C68
1344 case 0x06: // YM2203
1345 if (! stricmp_u(LStr, "DisableAY"))
1346 {
1347 TempFlag = GetBoolFromStr(RStr);
1348 TempCOpt->SpecialFlags &= ~(0x01 << 0);
1349 TempCOpt->SpecialFlags |= TempFlag << 0;
1350 }
1351 break;
1352 case 0x07: // YM2608
1353 case 0x08: // YM2610
1354 if (! stricmp_u(LStr, "DisableAY"))
1355 {
1356 TempFlag = GetBoolFromStr(RStr);
1357 TempCOpt->SpecialFlags &= ~(0x01 << 0);
1358 TempCOpt->SpecialFlags |= TempFlag << 0;
1359 }
1360 else if (! stricmp_u(LStr, "MuteMask_FM"))
1361 {
1362 TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0);
1363 TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1;
1364 }
1365 else if (! stricmp_u(LStr, "MuteMask_PCM"))
1366 {
1367 TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0);
1368 TempCOpt->ChnMute2 &= (1 << (CHN_MASK_CNT[CurChip] + 1)) - 1;
1369 }
1370 else if (! strnicmp_u(LStr, "MuteFMCh", 0x08))
1371 {
1372 CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0);
1373 if (TempPnt == NULL || *TempPnt)
1374 break;
1375 if (CurChn >= CHN_MASK_CNT[CurChip])
1376 break;
1377 TempFlag = GetBoolFromStr(RStr);
1378 TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
1379 TempCOpt->ChnMute1 |= TempFlag << CurChn;
1380 }
1381 else if (! strnicmp_u(LStr, "MutePCMCh", 0x09))
1382 {
1383 CurChn = (UINT8)strtol(LStr + 0x09, &TempPnt, 0);
1384 if (TempPnt == NULL || *TempPnt)
1385 break;
1386 if (CurChn >= CHN_MASK_CNT[CurChip])
1387 break;
1388 TempFlag = GetBoolFromStr(RStr);
1389 TempCOpt->ChnMute2 &= ~(0x01 << CurChn);
1390 TempCOpt->ChnMute2 |= TempFlag << CurChn;
1391 }
1392 else if (! stricmp_u(LStr, "MuteDT"))
1393 {
1394 CurChn = 0x06;
1395 TempFlag = GetBoolFromStr(RStr);
1396 TempCOpt->ChnMute2 &= ~(0x01 << CurChn);
1397 TempCOpt->ChnMute2 |= TempFlag << CurChn;
1398 }
1399 break;
1400 case 0x01: // YM2413
1401 case 0x09: // YM3812
1402 case 0x0A: // YM3526
1403 case 0x0B: // Y8950
1404 case 0x0C: // YMF262
1405 CurChn = 0xFF;
1406 if (! stricmp_u(LStr, "MuteBD"))
1407 CurChn = 0x00;
1408 else if (! stricmp_u(LStr, "MuteSD"))
1409 CurChn = 0x01;
1410 else if (! stricmp_u(LStr, "MuteTOM"))
1411 CurChn = 0x02;
1412 else if (! stricmp_u(LStr, "MuteTC"))
1413 CurChn = 0x03;
1414 else if (! stricmp_u(LStr, "MuteHH"))
1415 CurChn = 0x04;
1416 else if (CurChip == 0x0B && ! stricmp_u(LStr, "MuteDT"))
1417 CurChn = 0x05;
1418 if (CurChn != 0xFF)
1419 {
1420 if (CurChip < 0x0C)
1421 CurChn += 9;
1422 else
1423 CurChn += 18;
1424 TempFlag = GetBoolFromStr(RStr);
1425 TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
1426 TempCOpt->ChnMute1 |= TempFlag << CurChn;
1427 }
1428 break;
1429 case 0x0D: // YMF278B
1430 if (! stricmp_u(LStr, "MuteMask_FM"))
1431 {
1432 TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0);
1433 TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip - 0x01]) - 1;
1434 }
1435 else if (! stricmp_u(LStr, "MuteMask_WT"))
1436 {
1437 TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0);
1438 TempCOpt->ChnMute2 &= (1 << CHN_MASK_CNT[CurChip]) - 1;
1439 }
1440 else if (! strnicmp_u(LStr, "MuteFMCh", 0x08))
1441 {
1442 CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0);
1443 if (TempPnt == NULL || *TempPnt)
1444 break;
1445 if (CurChn >= CHN_COUNT[CurChip - 0x01])
1446 break;
1447 TempFlag = GetBoolFromStr(RStr);
1448 TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
1449 TempCOpt->ChnMute1 |= TempFlag << CurChn;
1450 }
1451 else if (! strnicmp_u(LStr, "MuteFM", 0x06))
1452 {
1453 CurChn = 0xFF;
1454 if (! stricmp_u(LStr + 6, "BD"))
1455 CurChn = 0x00;
1456 else if (! stricmp_u(LStr + 6, "SD"))
1457 CurChn = 0x01;
1458 else if (! stricmp_u(LStr + 6, "TOM"))
1459 CurChn = 0x02;
1460 else if (! stricmp_u(LStr + 6, "TC"))
1461 CurChn = 0x03;
1462 else if (! stricmp_u(LStr + 6, "HH"))
1463 CurChn = 0x04;
1464 if (CurChn != 0xFF)
1465 {
1466 CurChn += 18;
1467 TempFlag = GetBoolFromStr(RStr);
1468 TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
1469 TempCOpt->ChnMute1 |= TempFlag << CurChn;
1470 }
1471 }
1472 else if (! strnicmp_u(LStr, "MuteWTCh", 0x08))
1473 {
1474 CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0);
1475 if (TempPnt == NULL || *TempPnt)
1476 break;
1477 if (CurChn >= CHN_MASK_CNT[CurChip])
1478 break;
1479 TempFlag = GetBoolFromStr(RStr);
1480 TempCOpt->ChnMute2 &= ~(0x01 << CurChn);
1481 TempCOpt->ChnMute2 |= TempFlag << CurChn;
1482 }
1483 break;
1484 //case 0x0E: // YMF271
1485 //case 0x0F: // YMZ280B
1486 /*if (! stricmp_u(LStr, "DisableFix"))
1487 {
1488 DISABLE_YMZ_FIX = GetBoolFromStr(RStr);
1489 }
1490 break;*/
1491 //case 0x10: // RF5C164
1492 //case 0x11: // PWM
1493 //case 0x12: // AY8910
1494 case 0x13: // GameBoy
1495 if (! stricmp_u(LStr, "BoostWaveChn"))
1496 {
1497 TempFlag = GetBoolFromStr(RStr);
1498 TempCOpt->SpecialFlags &= ~(0x01 << 0);
1499 TempCOpt->SpecialFlags |= TempFlag << 0;
1500 }
1501 break;
1502 case 0x14: // NES
1503 if (! stricmp_u(LStr, "SharedOpts"))
1504 {
1505 // 2 bits
1506 TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03;
1507 TempCOpt->SpecialFlags &= ~(0x03 << 0) & 0x7FFF;
1508 TempCOpt->SpecialFlags |= TempLng << 0;
1509 }
1510 else if (! stricmp_u(LStr, "APUOpts"))
1511 {
1512 // 2 bits
1513 TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03;
1514 TempCOpt->SpecialFlags &= ~(0x03 << 2) & 0x7FFF;
1515 TempCOpt->SpecialFlags |= TempLng << 2;
1516 }
1517 else if (! stricmp_u(LStr, "DMCOpts"))
1518 {
1519 // 8 bits (6 bits used)
1520 TempLng = (UINT32)strtol(RStr, NULL, 0) & 0xFF;
1521 TempCOpt->SpecialFlags &= ~(0xFF << 4) & 0x7FFF;
1522 TempCOpt->SpecialFlags |= TempLng << 4;
1523 }
1524 else if (! stricmp_u(LStr, "FDSOpts"))
1525 {
1526 // 1 bit
1527 TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x01;
1528 TempCOpt->SpecialFlags &= ~(0x01 << 12) & 0x7FFF;
1529 TempCOpt->SpecialFlags |= TempLng << 12;
1530 }
1531 break;
1532 case 0x17: // OKIM6258
1533 if (! stricmp_u(LStr, "Enable10Bit"))
1534 {
1535 TempFlag = GetBoolFromStr(RStr);
1536 TempCOpt->SpecialFlags &= ~(0x01 << 0);
1537 TempCOpt->SpecialFlags |= TempFlag << 0;
1538 }
1539 break;
1540 case 0x20: // SCSP
1541 if (! stricmp_u(LStr, "BypassDSP"))
1542 {
1543 TempFlag = GetBoolFromStr(RStr);
1544 TempCOpt->SpecialFlags &= ~(0x01 << 0);
1545 TempCOpt->SpecialFlags |= TempFlag << 0;
1546 }
1547 break;
1548 case 0x27: // C352
1549 if (! stricmp_u(LStr, "DisableRear"))
1550 {
1551 TempFlag = GetBoolFromStr(RStr);
1552 TempCOpt->SpecialFlags &= ~(0x01 << 0);
1553 TempCOpt->SpecialFlags |= TempFlag << 0;
1554 }
1555 break;
1556 }
1557 }
1558 break;
1559 case 0xFF: // Dummy Section
1560 break;
1561 }
1562 }
1563 }
1564
1565 TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00];
1566 TempCOpt2 = (CHIP_OPTS*)&ChipOpts[0x01];
1567 for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++, TempCOpt2 ++)
1568 {
1569 TempCOpt2->Disabled = TempCOpt->Disabled;
1570 TempCOpt2->EmuCore = TempCOpt->EmuCore;
1571 TempCOpt2->SpecialFlags = TempCOpt->SpecialFlags;
1572 TempCOpt2->ChnMute1 = TempCOpt->ChnMute1;
1573 TempCOpt2->ChnMute2 = TempCOpt->ChnMute2;
1574 TempCOpt2->ChnMute3 = TempCOpt->ChnMute3;
1575 }
1576
1577 fclose(hFile);
1578
1579 #ifdef WIN32
1580 WinNT_Check();
1581 #endif
1582 if (CHIP_SAMPLE_RATE <= 0)
1583 CHIP_SAMPLE_RATE = SampleRate;
1584
1585 return;
1586 }
1587
GetBoolFromStr(const char * TextStr)1588 static bool GetBoolFromStr(const char* TextStr)
1589 {
1590 if (! stricmp_u(TextStr, "True"))
1591 return true;
1592 else if (! stricmp_u(TextStr, "False"))
1593 return false;
1594 else
1595 return strtol(TextStr, NULL, 0) ? true : false;
1596 }
1597
1598 #if defined(XMAS_EXTRA) || defined(WS_DEMO)
XMas_Extra(char * FileName,bool Mode)1599 static bool XMas_Extra(char* FileName, bool Mode)
1600 {
1601 char* FileTitle;
1602 const UINT8* XMasData;
1603 UINT32 XMasSize;
1604 FILE* hFile;
1605
1606 if (! Mode)
1607 { // Prepare Mode
1608 FileTitle = NULL;
1609 XMasData = NULL;
1610 #ifdef XMAS_EXTRA
1611 if (! stricmp_u(FileName, "WEWISH")
1612 {
1613 FileTitle = "WEWISH.CMF";
1614 XMasSize = sizeof(WEWISH_CMF);
1615 XMasData = WEWISH_CMF;
1616 }
1617 else if (! stricmp_u(FileName, "tim7")
1618 {
1619 FileTitle = "lem_tim7.vgz";
1620 XMasSize = sizeof(TIM7_VGZ);
1621 XMasData = TIM7_VGZ;
1622 }
1623 else if (! stricmp_u(FileName, "jingleb")
1624 {
1625 FileTitle = "lxmas_jb.dro";
1626 XMasSize = sizeof(JB_DRO);
1627 XMasData = JB_DRO;
1628 }
1629 else if (! stricmp_u(FileName, "rudolph")
1630 {
1631 FileTitle = "rudolph.dro";
1632 XMasSize = sizeof(RODOLPH_DRO);
1633 XMasData = RODOLPH_DRO;
1634 }
1635 else if (! stricmp_u(FileName, "clyde"))
1636 {
1637 FileTitle = "clyde1_1.dro";
1638 XMasSize = sizeof(clyde1_1_dro);
1639 XMasData = clyde1_1_dro;
1640 }
1641 #elif defined(WS_DEMO)
1642 if (! stricmp_u(FileName, "wswan"))
1643 {
1644 FileTitle = "SWJ-SQRC01_1C.vgz";
1645 XMasSize = sizeof(FF1ws_1C);
1646 XMasData = FF1ws_1C;
1647 }
1648 #endif
1649
1650 if (XMasData)
1651 {
1652 #ifdef WIN32
1653 GetEnvironmentVariable("Temp", FileName, MAX_PATH);
1654 #else
1655 strcpy(FileName, "/tmp");
1656 #endif
1657 strcat(FileName, DIR_STR);
1658 if (FileTitle == NULL)
1659 FileTitle = "XMas.dat";
1660 strcat(FileName, FileTitle);
1661
1662 hFile = fopen(FileName, "wb");
1663 if (hFile == NULL)
1664 {
1665 FileName[0x00] = '\0';
1666 printerr("Critical XMas-Error!\n");
1667 return false;
1668 }
1669 fwrite(XMasData, 0x01, XMasSize, hFile);
1670 fclose(hFile);
1671 }
1672 else
1673 {
1674 FileName = NULL;
1675 return false;
1676 }
1677 }
1678 else
1679 { // Unprepare Mode
1680 if (! remove(FileName))
1681 return false;
1682 // btw: it's intentional that the user can grab the file from the temp-folder
1683 }
1684
1685 return true;
1686 }
1687 #endif
1688
1689 #ifndef WIN32
1690 static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr)
1691 {
1692 const UINT16 CONV_TBL[0x20] =
1693 { 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87
1694 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, // 88-8F
1695 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97
1696 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178}; // 98-9F
1697 UINT32 StrLen;
1698 UINT16 UnicodeChr;
1699 char* DstPtr;
1700 const unsigned char* SrcPtr;
1701
1702 SrcPtr = (const unsigned char*)SrcStr;
1703 StrLen = 0x00;
1704 while(*SrcPtr != '\0')
1705 {
1706 if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0)
1707 UnicodeChr = *SrcPtr;
1708 else
1709 UnicodeChr = CONV_TBL[*SrcPtr - 0x80];
1710 if (UnicodeChr < 0x0080)
1711 StrLen ++;
1712 else if (UnicodeChr < 0x0800)
1713 StrLen += 2;
1714 else
1715 StrLen += 3;
1716 SrcPtr ++;
1717 }
1718
1719 *DstStr = (char*)malloc((StrLen + 0x01) * sizeof(char));
1720 SrcPtr = (const unsigned char*)SrcStr;
1721 DstPtr = *DstStr;
1722 while(*SrcPtr != '\0')
1723 {
1724 if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0)
1725 UnicodeChr = (unsigned char)*SrcPtr;
1726 else
1727 UnicodeChr = CONV_TBL[*SrcPtr - 0x80];
1728 if (UnicodeChr < 0x0080)
1729 {
1730 *DstPtr = UnicodeChr & 0xFF;
1731 DstPtr ++;
1732 }
1733 else if (UnicodeChr < 0x0800)
1734 {
1735 DstPtr[0x00] = 0xC0 | ((UnicodeChr >> 6) & 0x1F);
1736 DstPtr[0x01] = 0x80 | ((UnicodeChr >> 0) & 0x3F);
1737 DstPtr += 0x02;
1738 }
1739 else
1740 {
1741 DstPtr[0x00] = 0xE0 | ((UnicodeChr >> 12) & 0x0F);
1742 DstPtr[0x01] = 0x80 | ((UnicodeChr >> 6) & 0x3F);
1743 DstPtr[0x02] = 0x80 | ((UnicodeChr >> 0) & 0x3F);
1744 DstPtr += 0x03;
1745 }
1746 SrcPtr ++;
1747 }
1748 *DstPtr = '\0';
1749
1750 return;
1751 }
1752 #endif
1753
1754 static bool OpenPlayListFile(const char* FileName)
1755 {
1756 const char M3UV2_HEAD[] = "#EXTM3U";
1757 const char M3UV2_META[] = "#EXTINF:";
1758 const UINT8 UTF8_SIG[] = {0xEF, 0xBB, 0xBF};
1759 UINT32 METASTR_LEN;
1760 size_t RetVal;
1761
1762 FILE* hFile;
1763 UINT32 LineNo;
1764 bool IsV2Fmt;
1765 UINT32 PLAlloc;
1766 char TempStr[0x1000]; // 4096 chars should be enough
1767 char* RetStr;
1768 bool IsUTF8;
1769
1770 hFile = fopen(FileName, "rt");
1771 if (hFile == NULL)
1772 return false;
1773
1774 RetVal = fread(TempStr, 0x01, 0x03, hFile);
1775 if (RetVal >= 0x03)
1776 IsUTF8 = ! memcmp(TempStr, UTF8_SIG, 0x03);
1777 else
1778 IsUTF8 = false;
1779
1780 rewind(hFile);
1781
1782 PLAlloc = 0x0100;
1783 PLFileCount = 0x00;
1784 LineNo = 0x00;
1785 IsV2Fmt = false;
1786 METASTR_LEN = strlen(M3UV2_META);
1787 PlayListFile = (char**)malloc(PLAlloc * sizeof(char*));
1788 while(! feof(hFile))
1789 {
1790 RetStr = fgets(TempStr, 0x1000, hFile);
1791 if (RetStr == NULL)
1792 break;
1793 //RetStr = strchr(TempStr, 0x0D);
1794 //if (RetStr)
1795 // *RetStr = 0x00; // remove NewLine-Character
1796 RetStr = TempStr + strlen(TempStr) - 0x01;
1797 while(RetStr >= TempStr && *RetStr < 0x20)
1798 {
1799 *RetStr = '\0'; // remove NewLine-Characters
1800 RetStr --;
1801 }
1802 if (! strlen(TempStr))
1803 continue;
1804
1805 if (! LineNo)
1806 {
1807 if (! strcmp(TempStr, M3UV2_HEAD))
1808 {
1809 IsV2Fmt = true;
1810 LineNo ++;
1811 continue;
1812 }
1813 }
1814 if (IsV2Fmt)
1815 {
1816 if (! strncmp(TempStr, M3UV2_META, METASTR_LEN))
1817 {
1818 // Ignore Metadata of m3u Version 2
1819 LineNo ++;
1820 continue;
1821 }
1822 }
1823
1824 if (PLFileCount >= PLAlloc)
1825 {
1826 PLAlloc += 0x0100;
1827 PlayListFile = (char**)realloc(PlayListFile, PLAlloc * sizeof(char*));
1828 }
1829
1830 // TODO:
1831 // - supprt UTF-8 m3us under Windows
1832 // - force IsUTF8 via Commandline
1833 #ifdef WIN32
1834 // Windows uses the 1252 Codepage by default
1835 PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char));
1836 strcpy(PlayListFile[PLFileCount], TempStr);
1837 #else
1838 if (! IsUTF8)
1839 {
1840 // Most recent Linux versions use UTF-8, so I need to convert all strings.
1841 ConvertCP1252toUTF8(&PlayListFile[PLFileCount], TempStr);
1842 }
1843 else
1844 {
1845 PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char));
1846 strcpy(PlayListFile[PLFileCount], TempStr);
1847 }
1848 #endif
1849 StandardizeDirSeparators(PlayListFile[PLFileCount]);
1850 PLFileCount ++;
1851 LineNo ++;
1852 }
1853
1854 fclose(hFile);
1855
1856 RetStr = GetLastDirSeparator(FileName);
1857 if (RetStr != NULL)
1858 {
1859 RetStr ++;
1860 strncpy(PLFileBase, FileName, RetStr - FileName);
1861 PLFileBase[RetStr - FileName] = '\0';
1862 StandardizeDirSeparators(PLFileBase);
1863 }
1864 else
1865 {
1866 strcpy(PLFileBase, "");
1867 }
1868
1869 return true;
1870 }
1871
1872 static bool OpenMusicFile(const char* FileName)
1873 {
1874 if (OpenVGMFile(FileName))
1875 return true;
1876 else if (OpenOtherFile(FileName))
1877 return true;
1878
1879 return false;
1880 }
1881
1882 static void wprintc(const wchar_t* format, ...)
1883 {
1884 va_list arg_list;
1885 int RetVal;
1886 UINT32 BufSize;
1887 wchar_t* printbuf;
1888 #ifdef WIN32
1889 UINT32 StrLen;
1890 char* oembuf;
1891 DWORD CPMode;
1892 #endif
1893
1894 BufSize = 0x00;
1895 printbuf = NULL;
1896 do
1897 {
1898 BufSize += 0x100;
1899 printbuf = (wchar_t*)realloc(printbuf, BufSize * sizeof(wchar_t));
1900
1901 // Note: On Linux every vprintf call needs its own set of va_start/va_end commands.
1902 // Under Windows (with VC6) one only one block for all calls works, too.
1903 va_start(arg_list, format);
1904 RetVal = _vsnwprintf(printbuf, BufSize - 0x01, format, arg_list);
1905 va_end(arg_list);
1906 } while(RetVal == -1 && BufSize < 0x1000);
1907 #ifdef WIN32
1908 StrLen = wcslen(printbuf);
1909
1910 // This is the only way to print Unicode stuff to the Windows console.
1911 // No, wprintf doesn't work.
1912 RetVal = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), printbuf, StrLen, &CPMode, NULL);
1913 if (! RetVal) // call failed (e.g. with ERROR_CALL_NOT_IMPLEMENTED on Win95)
1914 {
1915 // fallback to printf with OEM codepage
1916 oembuf = (char*)malloc(BufSize);
1917 /*if (GetConsoleOutputCP() == GetOEMCP())
1918 CPMode = CP_OEMCP;
1919 else
1920 CPMode = CP_ACP;*/
1921 CPMode = GetConsoleOutputCP();
1922 WideCharToMultiByte(CPMode, 0x00, printbuf, StrLen + 1, oembuf, BufSize, NULL, NULL);
1923
1924 printf("%s", oembuf);
1925 free(oembuf);
1926 }
1927 #else
1928 // on Linux, it's easy
1929 printf("%ls", printbuf);
1930 #endif
1931
1932 free(printbuf);
1933
1934 return;
1935 }
1936
1937 static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock)
1938 {
1939 if (! Clock)
1940 return;
1941
1942 if (ChipID == 0x00 && (Clock & 0x80000000))
1943 Clock &= ~0x40000000;
1944 if (Clock & 0x80000000)
1945 {
1946 Clock &= ~0x80000000;
1947 ChipID |= 0x80;
1948 }
1949
1950 if (Clock & 0x40000000)
1951 printf("2x");
1952 printf("%s, ", GetAccurateChipName(ChipID, SubType));
1953
1954 return;
1955 }
1956
1957 static const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag)
1958 {
1959 const wchar_t* RetTag;
1960
1961 if (EngTag == NULL || ! wcslen(EngTag))
1962 {
1963 RetTag = JapTag;
1964 }
1965 else if (JapTag == NULL || ! wcslen(JapTag))
1966 {
1967 RetTag = EngTag;
1968 }
1969 else
1970 {
1971 if (! PreferJapTag)
1972 RetTag = EngTag;
1973 else
1974 RetTag = JapTag;
1975 }
1976
1977 if (RetTag == NULL)
1978 return L"";
1979 else
1980 return RetTag;
1981 }
1982
1983 static void ShowVGMTag(void)
1984 {
1985 const wchar_t* TitleTag;
1986 const wchar_t* GameTag;
1987 const wchar_t* AuthorTag;
1988 const wchar_t* SystemTag;
1989 UINT8 CurChip;
1990 UINT32 ChpClk;
1991 UINT8 ChpType;
1992 INT16 VolMod;
1993 #ifdef SET_CONSOLE_TITLE
1994 wchar_t TitleStr[0x80];
1995 UINT32 StrLen;
1996 #endif
1997
1998 TitleTag = GetTagStrEJ(VGMTag.strTrackNameE, VGMTag.strTrackNameJ);
1999 GameTag = GetTagStrEJ(VGMTag.strGameNameE, VGMTag.strGameNameJ);
2000 AuthorTag = GetTagStrEJ(VGMTag.strAuthorNameE, VGMTag.strAuthorNameJ);
2001 SystemTag = GetTagStrEJ(VGMTag.strSystemNameE, VGMTag.strSystemNameJ);
2002
2003 #ifdef SET_CONSOLE_TITLE
2004 // --- Show "Song (Game) - VGM Player" as Console Title ---
2005 if (! wcslen(TitleTag))
2006 {
2007 char* TempPtr1;
2008 char* TempPtr2;
2009
2010 TempPtr1 = strrchr(VgmFileName, '\\');
2011 TempPtr2 = strrchr(VgmFileName, '/');
2012 if (TempPtr1 < TempPtr2)
2013 TempPtr1 = TempPtr2;
2014 if (TempPtr1 == NULL)
2015 TempPtr1 = VgmFileName;
2016 else
2017 TempPtr1 ++;
2018 //strncpy(TitleStr, TempPtr1, 0x70);
2019 mbstowcs(TitleStr, TempPtr1, 0x7F);
2020 TitleStr[0x70] = '\0';
2021 }
2022 else
2023 {
2024 #if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(OLD_SWPRINTF)
2025 swprintf(TitleStr, L"%.*ls", 0x70, TitleTag);
2026 #else
2027 swprintf(TitleStr, 0x80, L"%.*ls", 0x70, TitleTag);
2028 #endif
2029 }
2030 StrLen = wcslen(TitleStr);
2031
2032 if (wcslen(GameTag) && StrLen < 0x6C)
2033 {
2034 #if (defined(_MSC_VER) && _MSC_VER < 1400) || defined(OLD_SWPRINTF)
2035 swprintf(TitleStr + StrLen, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag);
2036 #else
2037 swprintf(TitleStr + StrLen, 0x80, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag);
2038 #endif
2039 StrLen = wcslen(TitleStr);
2040 }
2041
2042 wcscat(TitleStr, L" - " APP_NAME_L);
2043 #ifdef WIN32
2044 SetConsoleTitleW(TitleStr); // Set Windows Console Title
2045 #else
2046 printf("\x1B]0;%ls\x07", TitleStr); // Set xterm/rxvt Terminal Title
2047 #endif
2048 #endif
2049
2050 // --- Display Tag Data ---
2051 if (VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP)
2052 VolMod = VGMHead.bytVolumeModifier;
2053 else if (VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01))
2054 VolMod = VOLUME_MODIF_WRAP - 0x100;
2055 else
2056 VolMod = VGMHead.bytVolumeModifier - 0x100;
2057
2058 wprintc(L"Track Title:\t%ls\n", TitleTag);
2059 wprintc(L"Game Name:\t%ls\n", GameTag);
2060 wprintc(L"System:\t\t%ls\n", SystemTag);
2061 wprintc(L"Composer:\t%ls\n", AuthorTag);
2062 wprintc(L"Release:\t%ls\n", VGMTag.strReleaseDate);
2063 printf("Version:\t%X.%02X\t", VGMHead.lngVersion >> 8, VGMHead.lngVersion & 0xFF);
2064 printf(" Gain:%5.2f\t", pow(2.0, VolMod / (double)0x20));
2065 printf("Loop: ");
2066 if (VGMHead.lngLoopOffset)
2067 {
2068 UINT32 PbRateMul;
2069 UINT32 PbRateDiv;
2070 UINT32 PbSamples;
2071
2072 // calculate samples for correct display with changed playback rate
2073 if (! VGMPbRate || ! VGMHead.lngRate)
2074 {
2075 PbRateMul = 1;
2076 PbRateDiv = 1;
2077 }
2078 else
2079 {
2080 PbRateMul = VGMHead.lngRate;
2081 PbRateDiv = VGMPbRate;
2082 }
2083 PbSamples = (UINT32)((UINT64)VGMHead.lngLoopSamples * PbRateMul / PbRateDiv);
2084
2085 printf("Yes (");
2086 PrintMinSec(PbSamples, VGMSampleRate);
2087 printf(")\n");
2088 }
2089 else
2090 {
2091 printf("No\n");
2092 }
2093 wprintc(L"VGM by:\t\t%ls\n", VGMTag.strCreator);
2094 wprintc(L"Notes:\t\t%ls\n", VGMTag.strNotes);
2095 printf("\n");
2096
2097 printf("Used chips:\t");
2098 for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++)
2099 {
2100 ChpClk = GetChipClock(&VGMHead, CurChip, &ChpType);
2101 if (ChpClk && GetChipClock(&VGMHead, 0x80 | CurChip, NULL))
2102 ChpClk |= 0x40000000;
2103 PrintChipStr(CurChip, ChpType, ChpClk);
2104 }
2105 printf("\b\b \n");
2106 printf("\n");
2107
2108 return;
2109 }
2110
2111
2112 #define LOG_SAMPLES (SampleRate / 5)
2113 static void PlayVGM_UI(void)
2114 {
2115 INT32 VGMPbSmplCount;
2116 INT32 PlaySmpl;
2117 UINT8 KeyCode;
2118 UINT32 VGMPlaySt;
2119 UINT32 VGMPlayEnd;
2120 char WavFileName[MAX_PATH];
2121 char* TempStr;
2122 WAVE_16BS* TempBuf;
2123 UINT8 RetVal;
2124 UINT32 TempLng;
2125 bool PosPrint;
2126 bool LastUninit;
2127 bool QuitPlay;
2128 UINT32 PlayTimeEnd;
2129
2130 printf("Initializing ...\r");
2131
2132 PlayVGM();
2133
2134 /*switch(LogToWave)
2135 {
2136 case 0x00:
2137 break;
2138 case 0x01:
2139 // Currently there's no record for Hardware FM
2140 PlayingMode = 0x00; // Impossible to log at full speed AND use FMPort
2141 break;
2142 case 0x02:
2143 if (PlayingMode == 0x01)
2144 LogToWave = 0x00; // Output and log sound (FM isn't logged)
2145 break;
2146 }*/
2147 switch(PlayingMode)
2148 {
2149 case 0x00:
2150 AUDIOBUFFERU = 10;
2151 break;
2152 case 0x01:
2153 AUDIOBUFFERU = 0; // no AudioBuffers needed
2154 break;
2155 case 0x02:
2156 AUDIOBUFFERU = 5; // try to sync Hardware/Software Emulator as well as possible
2157 break;
2158 }
2159 if (AUDIOBUFFERU < NEED_LARGE_AUDIOBUFS)
2160 AUDIOBUFFERU = NEED_LARGE_AUDIOBUFS;
2161 if (ForceAudioBuf && AUDIOBUFFERU)
2162 AUDIOBUFFERU = ForceAudioBuf;
2163
2164 switch(FileMode)
2165 {
2166 case 0x00: // VGM
2167 // RAW Log: no loop, no Creator, System Name set
2168 IsRAWLog = (! VGMHead.lngLoopOffset && ! wcslen(VGMTag.strCreator) &&
2169 (wcslen(VGMTag.strSystemNameE) || wcslen(VGMTag.strSystemNameJ)));
2170 break;
2171 case 0x01: // CMF
2172 IsRAWLog = false;
2173 break;
2174 case 0x02: // DRO
2175 IsRAWLog = true;
2176 break;
2177 }
2178 if (! VGMHead.lngTotalSamples)
2179 IsRAWLog = false;
2180
2181 #ifndef WIN32
2182 changemode(true);
2183 #endif
2184
2185 switch(PlayingMode)
2186 {
2187 case 0x00:
2188 case 0x02:
2189 if (LogToWave)
2190 {
2191 strcpy(WavFileName, VgmFileName);
2192 TempStr = GetFileExtension(WavFileName);
2193 if (TempStr == NULL)
2194 TempStr = WavFileName + strlen(WavFileName);
2195 else
2196 TempStr --;
2197 strcpy(TempStr, ".wav");
2198
2199 strcpy(SoundLogFile, WavFileName);
2200 }
2201 //FullBufFill = ! LogToWave;
2202
2203 switch(LogToWave)
2204 {
2205 case 0x00:
2206 case 0x02:
2207 SoundLogging(LogToWave ? true : false);
2208 if (FirstInit || ! StreamStarted)
2209 {
2210 // support smooth transistions between songs
2211 RetVal = StartStream(OutputDevID);
2212 if (RetVal)
2213 {
2214 printf("Error openning Sound Device!\n");
2215 return;
2216 }
2217 StreamStarted = true;
2218 }
2219 PauseStream(PausePlay);
2220 break;
2221 case 0x01:
2222 TempBuf = (WAVE_16BS*)malloc(SAMPLESIZE * LOG_SAMPLES);
2223 if (TempBuf == NULL)
2224 {
2225 printf("Allocation Error!\n");
2226 return;
2227 }
2228
2229 StartStream(0xFF);
2230 RetVal = SaveFile(0x00000000, NULL);
2231 if (RetVal)
2232 {
2233 printf("Can't open %s!\n", SoundLogFile);
2234 return;
2235 }
2236 break;
2237 }
2238 break;
2239 case 0x01:
2240 // PlayVGM() does it all
2241 //FullBufFill = true;
2242 break;
2243 }
2244 FirstInit = false;
2245
2246 VGMPlaySt = VGMPos;
2247 if (VGMHead.lngGD3Offset)
2248 VGMPlayEnd = VGMHead.lngGD3Offset;
2249 else
2250 VGMPlayEnd = VGMHead.lngEOFOffset;
2251 VGMPlayEnd -= VGMPlaySt;
2252 if (! FileMode)
2253 VGMPlayEnd --; // EOF Command doesn't count
2254 PosPrint = true;
2255
2256 PlayTimeEnd = 0;
2257 QuitPlay = false;
2258 while(! QuitPlay)
2259 {
2260 if(sigint)
2261 {
2262 QuitPlay = true;
2263 NextPLCmd = 0xFF;
2264 }
2265
2266 if (! PausePlay || PosPrint)
2267 {
2268 PosPrint = false;
2269
2270 VGMPbSmplCount = SampleVGM2Playback(VGMHead.lngTotalSamples);
2271 PlaySmpl = VGMPos - VGMPlaySt;
2272 #ifdef WIN32
2273 printf("Playing %01.2f%%\t", 100.0 * PlaySmpl / VGMPlayEnd);
2274 #else
2275 // \t doesn't display correctly under Linux
2276 // but \b causes flickering under Windows
2277 printf("Playing %01.2f%% \b\b\b\t", 100.0 * PlaySmpl / VGMPlayEnd);
2278 #endif
2279 if (LogToWave != 0x01)
2280 {
2281 PlaySmpl = (BlocksSent - BlocksPlayed) * SMPL_P_BUFFER;
2282 PlaySmpl = VGMSmplPlayed - PlaySmpl;
2283 }
2284 else
2285 {
2286 PlaySmpl = VGMSmplPlayed;
2287 }
2288 if (! VGMCurLoop)
2289 {
2290 if (PlaySmpl < 0)
2291 PlaySmpl = 0;
2292 }
2293 else
2294 {
2295 while(PlaySmpl < SampleVGM2Playback(VGMHead.lngTotalSamples -
2296 VGMHead.lngLoopSamples))
2297 PlaySmpl += SampleVGM2Playback(VGMHead.lngLoopSamples);
2298 }
2299 //if (PlaySmpl > VGMPbSmplCount)
2300 // PlaySmpl = VGMPbSmplCount;
2301 PrintMinSec(PlaySmpl, SampleRate);
2302 printf(" / ");
2303 PrintMinSec(VGMPbSmplCount, SampleRate);
2304 printf(" seconds");
2305 if (Show95Cmds && Last95Max != 0xFFFF)
2306 {
2307 if (Show95Cmds == 0x01)
2308 printf(" %02X / %02hX", 1 + Last95Drum, Last95Max);
2309 else if (Show95Cmds == 0x02)
2310 printf(" %02X / %02hX at %5u Hz", 1 + Last95Drum, Last95Max, Last95Freq);
2311 else if (Show95Cmds == 0x03)
2312 printf(" %02X / %02hX at %4.1f KHz", 1 + Last95Drum, Last95Max,
2313 Last95Freq / 1000.0);
2314 }
2315 //printf(" %u / %u", multipcm_get_channels(0, NULL), 28);
2316 printf("\r");
2317 #ifndef WIN32
2318 fflush(stdout);
2319 #endif
2320
2321 if (LogToWave == 0x01 && ! PausePlay)
2322 {
2323 TempLng = FillBuffer(TempBuf, LOG_SAMPLES);
2324 if (TempLng)
2325 SaveFile(TempLng, TempBuf);
2326 if (EndPlay)
2327 break;
2328 }
2329 else
2330 {
2331 #ifdef WIN32
2332 Sleep(50);
2333 #endif
2334 }
2335 }
2336 else
2337 {
2338 #ifdef WIN32
2339 Sleep(1);
2340 #endif
2341 }
2342 #ifndef WIN32
2343 if (! PausePlay && PlayingMode != 0x01)
2344 WaveOutLinuxCallBack();
2345 else
2346 Sleep(100);
2347 #endif
2348
2349 if (EndPlay)
2350 {
2351 if (! PlayTimeEnd)
2352 {
2353 PlayTimeEnd = PlayingTime;
2354 // quitting now terminates the program, so I need some special
2355 // checks to make sure that the rest of the audio buffer is played
2356 if (! PLFileCount || CurPLFile >= PLFileCount - 0x01)
2357 {
2358 if (FileMode == 0x01)
2359 PlayTimeEnd += SampleRate << 1; // Add 2 secs
2360 PlayTimeEnd += AUDIOBUFFERU * SMPL_P_BUFFER;
2361 }
2362 }
2363
2364 if (PlayingTime >= PlayTimeEnd)
2365 QuitPlay = true;
2366 }
2367 if (_kbhit())
2368 {
2369 KeyCode = _getch();
2370 if (KeyCode < 0x80)
2371 KeyCode = toupper(KeyCode);
2372 switch(KeyCode)
2373 {
2374 #ifndef WIN32
2375 case 0x1B: // Special Key
2376 KeyCode = _getch();
2377 if (KeyCode == 0x1B || KeyCode == 0x00)
2378 {
2379 // ESC Key pressed
2380 QuitPlay = true;
2381 NextPLCmd = 0xFF;
2382 break;
2383 }
2384 switch(KeyCode)
2385 {
2386 case 0x5B:
2387 // Cursor-Key Table
2388 // Key KeyCode
2389 // Up 41
2390 // Down 42
2391 // Left 44
2392 // Right 43
2393 // Cursor only: CursorKey
2394 // Ctrl: 0x31 + 0x3B + 0x35 + CursorKey
2395 // Alt: 0x31 + 0x3B + 0x33 + CursorKey
2396
2397 // Page-Keys: PageKey + 0x7E
2398 // PageUp 35
2399 // PageDown 36
2400 KeyCode = _getch(); // Get 2nd Key
2401 // Convert Cursor Key Code from Linux to Windows
2402 switch(KeyCode)
2403 {
2404 case 0x31: // Ctrl or Alt key
2405 KeyCode = _getch();
2406 if (KeyCode == 0x3B)
2407 {
2408 KeyCode = _getch();
2409 if (KeyCode == 0x35)
2410 {
2411 KeyCode = _getch();
2412 switch(KeyCode)
2413 {
2414 case 0x41:
2415 KeyCode = 0x8D;
2416 break;
2417 case 0x42:
2418 KeyCode = 0x91;
2419 break;
2420 case 0x43:
2421 KeyCode = 0x74;
2422 break;
2423 case 0x44:
2424 KeyCode = 0x73;
2425 break;
2426 default:
2427 KeyCode = 0x00;
2428 break;
2429 }
2430 }
2431 }
2432
2433 if ((KeyCode & 0xF0) == 0x30)
2434 KeyCode = 0x00;
2435 break;
2436 case 0x35:
2437 KeyCode = 0x49;
2438 _getch();
2439 break;
2440 case 0x36:
2441 KeyCode = 0x51;
2442 _getch();
2443 break;
2444 case 0x41:
2445 KeyCode = 0x48;
2446 break;
2447 case 0x42:
2448 KeyCode = 0x50;
2449 break;
2450 case 0x43:
2451 KeyCode = 0x4D;
2452 break;
2453 case 0x44:
2454 KeyCode = 0x4B;
2455 break;
2456 default:
2457 KeyCode = 0x00;
2458 break;
2459 }
2460 }
2461 // At this point I have Windows-style keys.
2462 #else //#ifdef WIN32
2463 case 0xE0: // Special Key
2464 // Cursor-Key Table
2465 // Shift + Cursor results in the usual value for the Cursor Key
2466 // Alt + Cursor results in 0x00 + (0x50 + CursorKey) (0x00 instead of 0xE0)
2467 // Key None Ctrl
2468 // Up 48 8D
2469 // Down 50 91
2470 // Left 4B 73
2471 // Right 4D 74
2472 KeyCode = _getch(); // Get 2nd Key
2473 #endif
2474 switch(KeyCode)
2475 {
2476 case 0x4B: // Cursor Left
2477 PlaySmpl = -5;
2478 break;
2479 case 0x4D: // Cursor Right
2480 PlaySmpl = 5;
2481 break;
2482 case 0x73: // Ctrl + Cursor Left
2483 PlaySmpl = -60;
2484 break;
2485 case 0x74: // Ctrl + Cursor Right
2486 PlaySmpl = 60;
2487 break;
2488 case 0x49: // Page Up
2489 if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile)
2490 {
2491 NextPLCmd = 0x01;
2492 QuitPlay = true;
2493 }
2494 PlaySmpl = 0;
2495 break;
2496 case 0x51: // Page Down
2497 if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01)
2498 {
2499 NextPLCmd = 0x00;
2500 QuitPlay = true;
2501 }
2502 PlaySmpl = 0;
2503 break;
2504 default:
2505 PlaySmpl = 0;
2506 break;
2507 }
2508 if (PlaySmpl)
2509 {
2510 SeekVGM(true, PlaySmpl * SampleRate);
2511 PosPrint = true;
2512 }
2513 break;
2514 #ifdef WIN32
2515 case 0x1B: // ESC
2516 #endif
2517 case 'Q':
2518 QuitPlay = true;
2519 NextPLCmd = 0xFF;
2520 break;
2521 case ' ':
2522 PauseVGM(! PausePlay);
2523 PosPrint = true;
2524 break;
2525 case 'F': // Fading
2526 FadeTime = FadeTimeN;
2527 FadePlay = true;
2528 break;
2529 case 'R': // Restart
2530 RestartVGM();
2531 PosPrint = true;
2532 break;
2533 case 'B': // Previous file (Back)
2534 if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile)
2535 {
2536 NextPLCmd = 0x01;
2537 QuitPlay = true;
2538 }
2539 break;
2540 case 'N': // Next file
2541 if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01)
2542 {
2543 NextPLCmd = 0x00;
2544 QuitPlay = true;
2545 }
2546 break;
2547 }
2548 }
2549
2550 /*if (! PauseThread && FadePlay && (! FadeTime || MasterVol == 0.0f))
2551 {
2552 QuitPlay = true;
2553 }*/
2554 if (FadeRAWLog && IsRAWLog && ! PausePlay && ! FadePlay && FadeTimeN)
2555 {
2556 PlaySmpl = (INT32)VGMHead.lngTotalSamples -
2557 FadeTimeN * VGMSampleRate / 1500;
2558 if (VGMSmplPos >= PlaySmpl)
2559 {
2560 FadeTime = FadeTimeN;
2561 FadePlay = true; // (FadeTime / 1500) ends at 33%
2562 }
2563 }
2564 }
2565 ThreadNoWait = false;
2566
2567 // Last Uninit: ESC pressed, no playlist, last file in playlist
2568 LastUninit = (NextPLCmd & 0x80) || ! PLFileCount ||
2569 (NextPLCmd == 0x00 && CurPLFile >= PLFileCount - 0x01);
2570 switch(PlayingMode)
2571 {
2572 case 0x00:
2573 switch(LogToWave)
2574 {
2575 case 0x00:
2576 case 0x02:
2577 if (LastUninit)
2578 {
2579 StopStream();
2580 StreamStarted = false;
2581 }
2582 else
2583 {
2584 if (ThreadPauseEnable)
2585 {
2586 ThreadPauseConfrm = false;
2587 PauseThread = true;
2588 while(! ThreadPauseConfrm)
2589 Sleep(1); // Wait until the Thread is finished
2590 }
2591 else
2592 {
2593 PauseThread = true;
2594 }
2595 if (LogToWave)
2596 SaveFile(0xFFFFFFFF, NULL);
2597 }
2598 break;
2599 case 0x01:
2600 SaveFile(0xFFFFFFFF, NULL);
2601 break;
2602 }
2603 break;
2604 case 0x01:
2605 if (StreamStarted)
2606 {
2607 StopStream();
2608 StreamStarted = false;
2609 }
2610 break;
2611 case 0x02:
2612 if (LastUninit)
2613 {
2614 StopStream();
2615 StreamStarted = false;
2616 #ifdef MIXER_MUTING
2617 #ifdef WIN32
2618 mixerClose(hmixer);
2619 #else
2620 close(hmixer);
2621 #endif
2622 #endif
2623 }
2624 else
2625 {
2626 if (ThreadPauseEnable)
2627 {
2628 ThreadPauseConfrm = false;
2629 PauseThread = true;
2630 while(! ThreadPauseConfrm)
2631 Sleep(1); // Wait until the Thread is finished
2632 PauseStream(true);
2633 }
2634 else
2635 {
2636 PauseThread = true;
2637 }
2638 }
2639 break;
2640 }
2641 #ifndef WIN32
2642 changemode(false);
2643 #endif
2644
2645 StopVGM();
2646
2647 printf("\nPlaying finished.\n");
2648
2649 return;
2650 }
2651
2652 INLINE INT8 sign(double Value)
2653 {
2654 if (Value > 0.0)
2655 return 1;
2656 else if (Value < 0.0)
2657 return -1;
2658 else
2659 return 0;
2660 }
2661
2662 INLINE long int Round(double Value)
2663 {
2664 // Alternative: (fabs(Value) + 0.5) * sign(Value);
2665 return (long int)(Value + 0.5 * sign(Value));
2666 }
2667
2668 INLINE double RoundSpecial(double Value, double RoundTo)
2669 {
2670 return (long int)(Value / RoundTo + 0.5 * sign(Value)) * RoundTo;
2671 }
2672
2673 static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate)
2674 {
2675 float TimeSec;
2676 UINT16 TimeMin;
2677 UINT16 TimeHours;
2678
2679 TimeSec = (float)RoundSpecial(SamplePos / (double)SmplRate, 0.01);
2680 //TimeSec = SamplePos / (float)SmplRate;
2681 TimeMin = (UINT16)TimeSec / 60;
2682 TimeSec -= TimeMin * 60;
2683 if (! PrintMSHours)
2684 {
2685 printf("%02hu:%05.2f", TimeMin, TimeSec);
2686 }
2687 else
2688 {
2689 TimeHours = TimeMin / 60;
2690 TimeMin %= 60;
2691 printf("%hu:%02hu:%05.2f", TimeHours, TimeMin, TimeSec);
2692 }
2693
2694 return;
2695 }
2696