cog/Frameworks/GME/vgmplay/VGMPlayUI.c

2680 lines
60 KiB
C
Raw Blame History

// TODO: Check codepage stuff (SetConsoleCP) - it looks like I don't need printc anymore
// VGMPlayUI.c: C Source File for the Console User Interface
// Note: In order to make MS VC6 NOT crash when using fprintf with stdout, stderr, etc.
// if linked to msvcrt.lib, the following project setting is important:
// C/C++ -> Code Generation -> Runtime libraries: Multithreaded DLL
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <wchar.h>
#include <locale.h> // for setlocale
#include "stdbool.h"
#include <math.h>
#ifdef WIN32
#include <conio.h>
#include <windows.h>
#else
#include <limits.h> // for PATH_MAX
#include <termios.h>
#include <unistd.h> // for STDIN_FILENO and usleep()
#include <sys/time.h> // for struct timeval in _kbhit()
#define Sleep(msec) usleep(msec * 1000)
#define _vsnwprintf vswprintf
#endif
#define printerr(x) fprintf(stderr, x)
#include "chips/mamedef.h"
#include "Stream.h"
#include "VGMPlay.h"
#include "VGMPlay_Intf.h"
#ifdef XMAS_EXTRA
#include "XMasFiles/XMasBonus.h"
#endif
#ifdef WS_DEMO
#include "XMasFiles/SWJ-SQRC01_1C.h"
#endif
#ifndef WIN32
void WaveOutLinuxCallBack(void);
#endif
#ifdef WIN32
#define DIR_CHR '\\'
#define DIR_STR "\\"
#define QMARK_CHR '\"'
#else
#define DIR_CHR '/'
#define DIR_STR "/"
#define QMARK_CHR '\''
#ifndef SHARE_PREFIX
#define SHARE_PREFIX "/usr/local"
#endif
#endif
#define APP_NAME "VGM Player"
#define APP_NAME_L L"VGM Player"
int main(int argc, char* argv[]);
static void RemoveNewLines(char* String);
static void RemoveQuotationMarks(char* String);
static char* GetLastDirSeparator(const char* FilePath);
static bool IsAbsolutePath(const char* FilePath);
static char* GetFileExtention(const char* FilePath);
static void StandardizeDirSeparators(char* FilePath);
#ifdef WIN32
static void WinNT_Check(void);
#endif
static char* GetAppFileName(void);
static void cls(void);
#ifndef WIN32
static void changemode(bool);
static int _kbhit(void);
static int _getch(void);
#endif
static INT8 stricmp_u(const char *string1, const char *string2);
static INT8 strnicmp_u(const char *string1, const char *string2, size_t count);
static void ReadOptions(const char* AppName);
static bool GetBoolFromStr(const char* TextStr);
#if defined(XMAS_EXTRA) || defined(WS_DEMO)
static bool XMas_Extra(char* FileName, bool Mode);
#endif
#ifndef WIN32
static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr);
#endif
static bool OpenPlayListFile(const char* FileName);
static bool OpenMusicFile(const char* FileName);
extern bool OpenVGMFile(const char* FileName);
extern bool OpenOtherFile(const char* FileName);
//#ifdef WIN32
//static void printc(const char* format, ...);
//#else
#define printc printf
//#endif
static void wprintc(const wchar_t* format, ...);
static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock);
static const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag);
static void ShowVGMTag(void);
static void PlayVGM_UI(void);
INLINE INT8 sign(double Value);
INLINE long int Round(double Value);
INLINE double RoundSpecial(double Value, double RoundTo);
static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate);
// Options Variables
extern UINT32 SampleRate; // Note: also used by some sound cores to
// determinate the chip sample rate
extern UINT32 VGMPbRate;
extern UINT32 VGMMaxLoop;
extern UINT32 CMFMaxLoop;
UINT32 FadeTimeN; // normal fade time
UINT32 FadeTimePL; // in-playlist fade time
extern UINT32 FadeTime;
UINT32 PauseTimeJ; // Pause Time for Jingles
UINT32 PauseTimeL; // Pause Time for Looping Songs
extern UINT32 PauseTime;
static UINT8 Show95Cmds;
extern float VolumeLevel;
extern bool SurroundSound;
extern UINT8 HardStopOldVGMs;
extern bool FadeRAWLog;
static UINT8 LogToWave;
//extern bool FullBufFill;
extern bool PauseEmulate;
extern bool DoubleSSGVol;
static UINT16 ForceAudioBuf;
extern UINT8 ResampleMode; // 00 - HQ both, 01 - LQ downsampling, 02 - LQ both
extern UINT8 CHIP_SAMPLING_MODE;
extern INT32 CHIP_SAMPLE_RATE;
extern UINT16 FMPort;
extern bool UseFM;
extern bool FMForce;
//extern bool FMAccurate;
extern bool FMBreakFade;
extern float FMVol;
extern CHIPS_OPTION ChipOpts[0x02];
extern bool ThreadPauseEnable;
extern volatile bool ThreadPauseConfrm;
extern bool ThreadNoWait; // don't reset the timer
extern UINT16 AUDIOBUFFERU;
extern UINT32 SMPL_P_BUFFER;
extern char SoundLogFile[MAX_PATH];
extern UINT8 OPL_MODE;
extern UINT8 OPL_CHIPS;
//extern bool WINNT_MODE;
UINT8 NEED_LARGE_AUDIOBUFS;
extern char* AppPaths[8];
static char AppPathBuffer[MAX_PATH * 2];
static char PLFileBase[MAX_PATH];
static char PLFileName[MAX_PATH];
static UINT32 PLFileCount;
static char** PlayListFile;
static UINT32 CurPLFile;
static UINT8 NextPLCmd;
static UINT8 PLMode; // set to 1 to show Playlist text
static bool FirstInit;
extern bool AutoStopSkip;
static char VgmFileName[MAX_PATH];
static UINT8 FileMode;
extern VGM_HEADER VGMHead;
extern UINT32 VGMDataLen;
extern UINT8* VGMData;
extern GD3_TAG VGMTag;
static PreferJapTag;
extern volatile bool PauseThread;
static bool StreamStarted;
extern float MasterVol;
extern UINT32 VGMPos;
extern INT32 VGMSmplPos;
extern INT32 VGMSmplPlayed;
extern INT32 VGMSampleRate;
extern UINT32 BlocksSent;
extern UINT32 BlocksPlayed;
static bool IsRAWLog;
extern bool EndPlay;
extern bool PausePlay;
extern bool FadePlay;
extern bool ForceVGMExec;
extern UINT8 PlayingMode;
extern UINT32 PlayingTime;
extern UINT32 FadeStart;
extern UINT32 VGMMaxLoopM;
extern UINT32 VGMCurLoop;
extern float VolumeLevelM;
bool ErrorHappened; // used by VGMPlay.c and VGMPlay_AddFmts.c
extern float FinalVol;
extern bool ResetPBTimer;
#ifndef WIN32
static struct termios oldterm;
static bool termmode;
#endif
UINT8 CmdList[0x100];
//extern UINT8 DISABLE_YMZ_FIX;
extern UINT8 IsVGMInit;
extern UINT16 Last95Drum; // for optvgm debugging
extern UINT16 Last95Max; // for optvgm debugging
extern UINT32 Last95Freq; // for optvgm debugging
static bool PrintMSHours;
int main(int argc, char* argv[])
{
int argbase;
int ErrRet;
char* AppName;
#if defined(XMAS_EXTRA) || defined(WS_DEMO)
bool XMasEnable;
#endif
char* AppPathPtr;
const char* StrPtr;
const char* FileExt;
UINT8 CurPath;
UINT32 ChrPos;
INT32 OldCP;
// set locale to "current system locale"
// (makes Unicode characters (like umlauts) work under Linux and fixes some
// Unicode -> ANSI conversions)
setlocale(LC_CTYPE, "");
#ifndef WIN32
tcgetattr(STDIN_FILENO, &oldterm);
termmode = false;
#endif
if (argc > 1)
{
if (! stricmp_u(argv[1], "-v") || ! stricmp_u(argv[1], "--version"))
{
printf("VGMPlay %s"
#if defined(APLHA)
" alpha"
#elif defined(BETA)
" beta"
#endif
", supports VGM %s\n", VGMPLAY_VER_STR, VGM_VER_STR);
return 0;
}
}
#ifdef SET_CONSOLE_TITLE
#ifdef WIN32
SetConsoleTitle(APP_NAME); // Set Windows Console Title
#else
printf("\x1B]0;%s\x07", APP_NAME); // Set xterm/rxvt Terminal Title
#endif
#endif
printf(APP_NAME);
#ifdef XMAS_EXTRA
printf(" - XMas Release");
#endif
printf("\n----------\n");
//if (argv[0x00] == NULL)
// printf("Argument \"Application-Name\" is NULL!\n");
// Warning! It's dangerous to use Argument 0!
// AppName may be "vgmplay" instead of "vgmplay.exe"
VGMPlay_Init();
// Note: Paths are checked from last to first.
CurPath = 0x00;
AppPathPtr = AppPathBuffer;
#ifndef WIN32
// Path 1: global share directory
AppPaths[CurPath] = SHARE_PREFIX "/share/vgmplay/";
CurPath ++;
#endif
// Path 2: exe's directory
AppName = GetAppFileName(); // "C:\VGMPlay\VGMPlay.exe"
// Note: GetAppFileName always returns native directory separators.
StrPtr = strrchr(AppName, DIR_CHR);
if (StrPtr != NULL)
{
ChrPos = StrPtr + 1 - AppName;
strncpy(AppPathPtr, AppName, ChrPos);
AppPathPtr[ChrPos] = 0x00; // "C:\VGMPlay\"
AppPaths[CurPath] = AppPathPtr;
CurPath ++;
AppPathPtr += ChrPos + 1;
}
#ifndef WIN32
// Path 3: home directory
StrPtr = getenv("XDG_CONFIG_HOME");
if (StrPtr != NULL && StrPtr[0] == '\0')
{
strcpy(AppPathPtr, StrPtr);
}
else
{
StrPtr = getenv("HOME");
if (StrPtr != NULL)
strcpy(AppPathPtr, StrPtr);
else
strcpy(AppPathPtr, "");
strcat(AppPathPtr, "/.config");
}
strcat(AppPathPtr, "/vgmplay/");
AppPaths[CurPath] = AppPathPtr;
CurPath ++;
AppPathPtr += strlen(AppPathPtr) + 1;
#endif
// Path 4: working directory ("\0")
AppPathPtr[0] = '\0';
AppPaths[CurPath] = AppPathPtr;
CurPath ++;
#if 0 // set to 1 to print all selected search paths
CurPath = 0;
while(AppPaths[CurPath] != NULL)
{
printf("Path %u: %s\n", CurPath + 1, AppPaths[CurPath]);
CurPath ++;
}
#endif
ReadOptions(AppName);
VGMPlay_Init2();
ErrRet = 0;
argbase = 0x01;
if (argc >= argbase + 0x01)
{
if (! strnicmp_u(argv[argbase], "-LogSound:", 10))
{
LogToWave = (UINT8)strtoul(argv[argbase] + 10, NULL, 0);
argbase ++;
}
}
printf("\nFile Name:\t");
if (argc <= argbase)
{
#ifdef WIN32
OldCP = GetConsoleCP();
// Set the Console Input Codepage to ANSI.
// The Output Codepage must be left at OEM, else the displayed characters are wrong.
ChrPos = GetACP();
ErrRet = SetConsoleCP(ChrPos); // set input codepage
//ErrRet = SetConsoleOutputCP(ChrPos); // set output codepage (would be a bad idea)
StrPtr = fgets(VgmFileName, MAX_PATH, stdin);
if (StrPtr == NULL)
VgmFileName[0] = '\0';
// Playing with the console font resets the Console Codepage to OEM, so I have to
// convert the file name in this case.
if (GetConsoleCP() == GetOEMCP())
OemToChar(VgmFileName, VgmFileName); // OEM -> ANSI conversion
// This fixes the display of non-ANSI characters.
ErrRet = SetConsoleCP(OldCP);
// This codepage stuff drives me insane.
// Debug and Release build behave differently - WHAT??
//
// There a list of behaviours.
// Debug and Release were tested by dropping a file on it and via Visual Studio.
//
// Input CP 850, Output CP 850
// Debug build: Dynamite D<>x
// Release build: Dynamite D<>x
// Input CP 1252, Output CP 850
// Debug build: Dynamite D<>x
// Release build: Dynamite D<>x
// Input CP 850, Output CP 1252
// Debug build: Dynamite D<>x [tag display wrong]
// Release build: Dynamite D<>x [tag display wrong]
// Input CP 1252, Output CP 1252
// Debug build: Dynamite D<>x [tag display wrong]
// Release build: Dynamite D<>x [tag display wrong]
#else
StrPtr = fgets(VgmFileName, MAX_PATH, stdin);
if (StrPtr == NULL)
VgmFileName[0] = '\0';
#endif
RemoveNewLines(VgmFileName);
RemoveQuotationMarks(VgmFileName);
}
else
{
// The argument should already use the ANSI codepage.
strcpy(VgmFileName, argv[argbase]);
printc("%s\n", VgmFileName);
}
if (! strlen(VgmFileName))
goto ExitProgram;
StandardizeDirSeparators(VgmFileName);
#if defined(XMAS_EXTRA) || defined(WS_DEMO)
XMasEnable = XMas_Extra(VgmFileName, 0x00);
#endif
#if 0
{ // Print hex characters of file name (for vgm-player script debugging)
const char* CurChr;
#ifdef WIN32
printf("Input CP: %d, Output CP: %d\n", GetConsoleCP(), GetConsoleOutputCP());
#endif
printf("VgmFileName: ");
CurChr = VgmFileName;
while(*CurChr != '\0')
{
printf("%02X ", (UINT8)*CurChr);
CurChr ++;
}
printf("%02X\n", (UINT8)*CurChr);
_getch();
}
#endif
#if 0
{ // strip spaces and \n (fixed bugs with vgm-player script with un-7z)
char* CurChr;
// trim \n and spaces off
CurChr = strchr(VgmFileName, '\n');
if (CurChr != NULL)
*CurChr = '\0';
CurChr = VgmFileName + strlen(VgmFileName) - 1;
while(CurChr > VgmFileName && *CurChr == ' ')
*(CurChr --) = '\0';
}
#endif
FirstInit = true;
StreamStarted = false;
FileExt = GetFileExtention(VgmFileName);
if (FileExt == NULL || stricmp_u(FileExt, "m3u"))
PLMode = 0x00;
else
PLMode = 0x01;
if (! PLMode)
{
PLFileCount = 0x00;
CurPLFile = 0x00;
// no Play List File
if (! OpenMusicFile(VgmFileName))
{
printerr("Error opening the file!\n");
if (argv[0][1] == ':')
_getch();
ErrRet = 1;
goto ExitProgram;
}
printf("\n");
ErrorHappened = false;
FadeTime = FadeTimeN;
PauseTime = PauseTimeL;
PrintMSHours = (VGMHead.lngTotalSamples >= 158760000); // 44100 smpl * 60 sec * 60 min
ShowVGMTag();
NextPLCmd = 0x80;
PlayVGM_UI();
CloseVGMFile();
}
else
{
strcpy(PLFileName, VgmFileName);
if (! OpenPlayListFile(PLFileName))
{
printerr("Error opening the playlist!\n");
if (argv[0][1] == ':')
_getch();
ErrRet = 1;
goto ExitProgram;
}
for (CurPLFile = 0x00; CurPLFile < PLFileCount; CurPLFile ++)
{
if (PLMode)
{
cls();
printf(APP_NAME);
printf("\n----------\n");
printc("\nPlaylist File:\t%s\n", PLFileName);
printf("Playlist Entry:\t%u / %u\n", CurPLFile + 1, PLFileCount);
printc("File Name:\t%s\n", PlayListFile[CurPLFile]);
}
if (IsAbsolutePath(PlayListFile[CurPLFile]))
{
strcpy(VgmFileName, PlayListFile[CurPLFile]);
}
else
{
strcpy(VgmFileName, PLFileBase);
strcat(VgmFileName, PlayListFile[CurPLFile]);
}
if (! OpenMusicFile(VgmFileName))
{
printf("Error opening the file!\n");
_getch();
while(_kbhit())
_getch();
continue;
}
printf("\n");
ErrorHappened = false;
if (CurPLFile < PLFileCount - 1)
FadeTime = FadeTimePL;
else
FadeTime = FadeTimeN;
PauseTime = VGMHead.lngLoopOffset ? PauseTimeL : PauseTimeJ;
PrintMSHours = (VGMHead.lngTotalSamples >= 158760000);
ShowVGMTag();
NextPLCmd = 0x00;
PlayVGM_UI();
CloseVGMFile();
if (ErrorHappened)
{
if (_kbhit())
_getch();
_getch();
ErrorHappened = false;
}
if (NextPLCmd == 0xFF)
break;
else if (NextPLCmd == 0x01)
CurPLFile -= 0x02; // Jump to last File (-2 + 1 = -1)
}
}
if (ErrorHappened && argv[0][1] == ':')
{
if (_kbhit())
_getch();
_getch();
}
#ifdef _DEBUG
printf("Press any key ...");
_getch();
#endif
ExitProgram:
#if defined(XMAS_EXTRA) || defined(WS_DEMO)
if (XMasEnable)
XMas_Extra(VgmFileName, 0x01);
#endif
#ifndef WIN32
changemode(false);
#ifdef SET_CONSOLE_TITLE
// printf("\x1B]0;${USER}@${HOSTNAME}: ${PWD/$HOME/~}\x07", APP_NAME); // Reset xterm/rxvt Terminal Title
#endif
#endif
VGMPlay_Deinit();
free(AppName);
return ErrRet;
}
static void RemoveNewLines(char* String)
{
char* StrPtr;
StrPtr = String + strlen(String) - 1;
while(StrPtr >= String && (UINT8)*StrPtr < 0x20)
{
*StrPtr = '\0';
StrPtr --;
}
return;
}
static void RemoveQuotationMarks(char* String)
{
UINT32 StrLen;
char* EndQMark;
if (String[0x00] != QMARK_CHR)
return;
StrLen = strlen(String);
memmove(String, String + 0x01, StrLen); // Remove first char
EndQMark = strrchr(String, QMARK_CHR);
if (EndQMark != NULL)
*EndQMark = 0x00; // Remove last Quot.-Mark
return;
}
static char* GetLastDirSeparator(const char* FilePath)
{
char* SepPos1;
char* SepPos2;
SepPos1 = strrchr(FilePath, '/');
SepPos2 = strrchr(FilePath, '\\');
if (SepPos1 < SepPos2)
return SepPos2;
else
return SepPos1;
}
static bool IsAbsolutePath(const char* FilePath)
{
#ifdef WIN32
if (FilePath[0] == '\0')
return false; // empty string
if (FilePath[1] == ':')
return true; // Device Path: C:\path
if (! strncmp(FilePath, "\\\\", 2))
return true; // Network Path: \\computername\path
#else
if (FilePath[0] == '/')
return true; // absolute UNIX path
#endif
return false;
}
static char* GetFileExtention(const char* FilePath)
{
char* DirSepPos;
char* ExtDotPos;
DirSepPos = GetLastDirSeparator(FilePath);
if (DirSepPos == NULL)
DirSepPos = (char*)FilePath;
ExtDotPos = strrchr(DirSepPos, '.');
if (ExtDotPos == NULL)
return NULL;
else
return ExtDotPos + 1;
}
static void StandardizeDirSeparators(char* FilePath)
{
char* CurChr;
CurChr = FilePath;
while(*CurChr != '\0')
{
if (*CurChr == '\\' || *CurChr == '/')
*CurChr = DIR_CHR;
CurChr ++;
}
return;
}
#ifdef WIN32
static void WinNT_Check(void)
{
OSVERSIONINFO VerInf;
VerInf.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&VerInf);
//WINNT_MODE = (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT);
/* Following Systems need larger Audio Buffers:
- Windows 95 (500+ ms)
- Windows Vista (200+ ms)
Tested Systems:
- Windows 95B
- Windows 98 SE
- Windows 2000
- Windows XP (32-bit)
- Windows Vista (32-bit)
- Windows 7 (64-bit)
*/
NEED_LARGE_AUDIOBUFS = 0;
if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{
if (VerInf.dwMajorVersion == 4 && VerInf.dwMinorVersion == 0)
NEED_LARGE_AUDIOBUFS = 50; // Windows 95
}
else if (VerInf.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
if (VerInf.dwMajorVersion == 6 && VerInf.dwMinorVersion == 0)
NEED_LARGE_AUDIOBUFS = 20; // Windows Vista
}
return;
}
#endif
static char* GetAppFileName(void)
{
char* AppPath;
int RetVal;
AppPath = (char*)malloc(MAX_PATH * sizeof(char));
#ifdef WIN32
RetVal = GetModuleFileName(NULL, AppPath, MAX_PATH);
if (! RetVal)
AppPath[0] = '\0';
#else
RetVal = readlink("/proc/self/exe", AppPath, MAX_PATH);
if (RetVal == -1)
AppPath[0] = '\0';
#endif
return AppPath;
}
static void cls(void)
{
#ifdef WIN32
// CLS-Function from the MSDN Help
HANDLE hConsole;
COORD coordScreen = {0, 0};
BOOL bSuccess;
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// get the number of character cells in the current buffer
bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
// fill the entire screen with blanks
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
bSuccess = FillConsoleOutputCharacter(hConsole, (TCHAR)' ', dwConSize, coordScreen,
&cCharsWritten);
// get the current text attribute
//bSuccess = GetConsoleScreenBufferInfo(hConsole, &csbi);
// now set the buffer's attributes accordingly
//bSuccess = FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen,
// &cCharsWritten);
// put the cursor at (0, 0)
bSuccess = SetConsoleCursorPosition(hConsole, coordScreen);
#else
system("clear");
#endif
return;
}
#ifndef WIN32
static void changemode(bool dir)
{
static struct termios newterm;
if (termmode == dir)
return;
if (dir)
{
newterm = oldterm;
newterm.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newterm);
}
else
{
tcsetattr(STDIN_FILENO, TCSANOW, &oldterm);
}
termmode = dir;
return;
}
static int _kbhit(void)
{
struct timeval tv;
fd_set rdfs;
int kbret;
bool needchg;
needchg = (! termmode);
if (needchg)
changemode(true);
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET(STDIN_FILENO, &rdfs);
select(STDIN_FILENO + 1, &rdfs, NULL, NULL, &tv);
kbret = FD_ISSET(STDIN_FILENO, &rdfs);
if (needchg)
changemode(false);
return kbret;
}
static int _getch(void)
{
int ch;
bool needchg;
needchg = (! termmode);
if (needchg)
changemode(true);
ch = getchar();
if (needchg)
changemode(false);
return ch;
}
#endif
static INT8 stricmp_u(const char *string1, const char *string2)
{
// my own stricmp, because VC++6 doesn't find _stricmp when compiling without
// standard libraries
const char* StrPnt1;
const char* StrPnt2;
char StrChr1;
char StrChr2;
StrPnt1 = string1;
StrPnt2 = string2;
while(true)
{
StrChr1 = toupper(*StrPnt1);
StrChr2 = toupper(*StrPnt2);
if (StrChr1 < StrChr2)
return -1;
else if (StrChr1 > StrChr2)
return +1;
if (StrChr1 == 0x00)
return 0;
StrPnt1 ++;
StrPnt2 ++;
}
return 0;
}
static INT8 strnicmp_u(const char *string1, const char *string2, size_t count)
{
// my own strnicmp, because GCC doesn't seem to have _strnicmp
const char* StrPnt1;
const char* StrPnt2;
char StrChr1;
char StrChr2;
size_t CurChr;
StrPnt1 = string1;
StrPnt2 = string2;
CurChr = 0x00;
while(CurChr < count)
{
StrChr1 = toupper(*StrPnt1);
StrChr2 = toupper(*StrPnt2);
if (StrChr1 < StrChr2)
return -1;
else if (StrChr1 > StrChr2)
return +1;
if (StrChr1 == 0x00)
return 0;
StrPnt1 ++;
StrPnt2 ++;
CurChr ++;
}
return 0;
}
static void ReadOptions(const char* AppName)
{
const UINT8 CHN_COUNT[CHIP_COUNT] =
{ 0x04, 0x09, 0x06, 0x08, 0x10, 0x08, 0x03, 0x00,
0x00, 0x09, 0x09, 0x09, 0x12, 0x00, 0x0C, 0x08,
0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00,
0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10,
0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20,
0x04
};
const UINT8 CHN_MASK_CNT[CHIP_COUNT] =
{ 0x04, 0x0E, 0x07, 0x08, 0x10, 0x08, 0x03, 0x06,
0x06, 0x0E, 0x0E, 0x0E, 0x17, 0x18, 0x0C, 0x08,
0x08, 0x00, 0x03, 0x04, 0x05, 0x1C, 0x00, 0x00,
0x04, 0x05, 0x08, 0x08, 0x18, 0x04, 0x04, 0x10,
0x20, 0x04, 0x06, 0x06, 0x20, 0x20, 0x10, 0x20,
0x04
};
char* FileName;
FILE* hFile;
char TempStr[0x40];
UINT32 StrLen;
UINT32 TempLng;
char* LStr;
char* RStr;
UINT8 IniSection;
UINT8 CurChip;
CHIP_OPTS* TempCOpt;
CHIP_OPTS* TempCOpt2;
UINT8 CurChn;
char* TempPnt;
bool TempFlag;
// most defaults are set by VGMPlay_Init()
FadeTimeN = FadeTime;
PauseTimeJ = PauseTime;
PauseTimeL = 0;
Show95Cmds = 0x00;
LogToWave = 0x00;
ForceAudioBuf = 0x00;
PreferJapTag = false;
if (AppName == NULL)
{
printerr("Argument \"Application-Path\" is NULL!\nSkip loading INI.\n");
return;
}
// AppName: "C:\VGMPlay\VGMPlay.exe"
RStr = strrchr(AppName, DIR_CHR);
if (RStr != NULL)
RStr ++;
else
RStr = (char*)AppName;
FileName = (char*)malloc(strlen(RStr) + 0x05); // ".ini" + 00
strcpy(FileName, RStr);
// FileName: "VGMPlay.exe"
RStr = GetFileExtention(FileName);
if (RStr == NULL)
{
RStr = FileName + strlen(FileName);
*RStr = '.';
RStr ++;
}
strcpy(RStr, "ini");
// FileName: "VGMPlay.ini"
LStr = FileName;
FileName = FindFile(LStr);
free(LStr);
if (FileName == NULL)
{
// on Linux platforms, it searches for "vgmplay.ini" first and
// file names are case sensitive
FileName = FindFile("VGMPlay.ini");
}
if (FileName == NULL)
{
printerr("Failed to load INI.\n");
return;
}
hFile = fopen(FileName, "rt");
free(FileName);
if (hFile == NULL)
{
printerr("Failed to load INI.\n");
return;
}
IniSection = 0x00;
while(! feof(hFile))
{
LStr = fgets(TempStr, 0x40, hFile);
if (LStr == NULL)
break;
if (TempStr[0x00] == ';') // Comment line
continue;
StrLen = strlen(TempStr) - 0x01;
//if (TempStr[StrLen] == '\n')
// TempStr[StrLen] = 0x00;
while(TempStr[StrLen] < 0x20)
{
TempStr[StrLen] = 0x00;
if (! StrLen)
break;
StrLen --;
}
if (! StrLen)
continue;
StrLen ++;
LStr = &TempStr[0x00];
while(*LStr == ' ')
LStr ++;
if (LStr[0x00] == ';') // Comment line
continue;
if (LStr[0x00] == '[')
RStr = strchr(TempStr, ']');
else
RStr = strchr(TempStr, '=');
if (RStr == NULL)
continue;
if (LStr[0x00] == '[')
{
// Line pattern: [Group]
LStr ++;
RStr = strchr(TempStr, ']');
if (RStr != NULL)
RStr[0x00] = 0x00;
if (! stricmp_u(LStr, "General"))
{
IniSection = 0x00;
}
else
{
IniSection = 0xFF;
for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++)
{
if (! stricmp_u(LStr, GetChipName(CurChip)))
{
IniSection = 0x80 | CurChip;
break;
}
}
if (IniSection == 0xFF)
continue;
}
}
else
{
// Line pattern: Option = Value
TempLng = RStr - TempStr;
TempStr[TempLng] = 0x00;
// Prepare Strings (trim the spaces)
RStr = &TempStr[TempLng - 0x01];
while(*RStr == ' ')
*(RStr --) = 0x00;
RStr = &TempStr[StrLen - 0x01];
while(*RStr == ' ')
*(RStr --) = 0x00;
RStr = &TempStr[TempLng + 0x01];
while(*RStr == ' ')
RStr ++;
switch(IniSection)
{
case 0x00: // General Sction
if (! stricmp_u(LStr, "SampleRate"))
{
SampleRate = strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "PlaybackRate"))
{
VGMPbRate = strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "DoubleSSGVol"))
{
DoubleSSGVol = GetBoolFromStr(RStr);
}
else if (! stricmp_u(LStr, "PreferJapTag"))
{
PreferJapTag = GetBoolFromStr(RStr);
}
else if (! stricmp_u(LStr, "FadeTime"))
{
FadeTimeN = strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "FadeTimePL"))
{
FadeTimePL = strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "JinglePause"))
{
PauseTimeJ = strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "HardStopOld"))
{
HardStopOldVGMs = (UINT8)strtoul(RStr, &TempPnt, 0);
if (TempPnt == RStr)
HardStopOldVGMs = GetBoolFromStr(RStr) ? 0x01 : 0x00;
}
else if (! stricmp_u(LStr, "FadeRAWLogs"))
{
FadeRAWLog = GetBoolFromStr(RStr);
}
else if (! stricmp_u(LStr, "Volume"))
{
VolumeLevel = (float)strtod(RStr, NULL);
}
else if (! stricmp_u(LStr, "LogSound"))
{
//LogToWave = GetBoolFromStr(RStr);
LogToWave = (UINT8)strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "MaxLoops"))
{
VGMMaxLoop = strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "MaxLoopsCMF"))
{
CMFMaxLoop = strtoul(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "ResamplingMode"))
{
ResampleMode = (UINT8)strtol(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "ChipSmplMode"))
{
CHIP_SAMPLING_MODE = (UINT8)strtol(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "ChipSmplRate"))
{
CHIP_SAMPLE_RATE = strtol(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "AudioBuffers"))
{
ForceAudioBuf = (UINT16)strtol(RStr, NULL, 0);
if (ForceAudioBuf < 0x04)
ForceAudioBuf = 0x00;
}
else if (! stricmp_u(LStr, "SurroundSound"))
{
SurroundSound = GetBoolFromStr(RStr);
}
else if (! stricmp_u(LStr, "EmulatePause"))
{
PauseEmulate = GetBoolFromStr(RStr);
}
else if (! stricmp_u(LStr, "ShowStreamCmds"))
{
Show95Cmds = (UINT8)strtol(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "FMPort"))
{
FMPort = (UINT16)strtoul(RStr, NULL, 16);
}
else if (! stricmp_u(LStr, "FMForce"))
{
FMForce = GetBoolFromStr(RStr);
}
else if (! stricmp_u(LStr, "FMVolume"))
{
FMVol = (float)strtod(RStr, NULL);
}
/*else if (! stricmp_u(LStr, "AccurateFM"))
{
FMAccurate = GetBoolFromStr(RStr);
}*/
else if (! stricmp_u(LStr, "FMSoftStop"))
{
FMBreakFade = GetBoolFromStr(RStr);
}
break;
case 0x80: // SN76496
case 0x81: // YM2413
case 0x82: // YM2612
case 0x83: // YM2151
case 0x84: // SegaPCM
case 0x85: // RF5C68
case 0x86: // YM2203
case 0x87: // YM2608
case 0x88: // YM2610
case 0x89: // YM3812
case 0x8A: // YM3526
case 0x8B: // Y8950
case 0x8C: // YMF262
case 0x8D: // YMF278B
case 0x8E: // YMF271
case 0x8F: // YMZ280B
case 0x90: // RF5C164
case 0x91: // PWM
case 0x92: // AY8910
case 0x93: // GameBoy
case 0x94: // NES
case 0x95: // MultiPCM
case 0x96: // UPD7759
case 0x97: // OKIM6258
case 0x98: // OKIM6295
case 0x99: // K051649
case 0x9A: // K054539
case 0x9B: // HuC6280
case 0x9C: // C140
case 0x9D: // K053260
case 0x9E: // Pokey
case 0x9F: // QSound
case 0xA0: // SCSP
case 0xA1: // WonderSwan
case 0xA2: // VSU
case 0xA3: // SAA1099
case 0xA4: // ES5503
case 0xA5: // ES5506
case 0xA6: // X1_010
case 0xA7: // C352
case 0xA8: // GA20
CurChip = IniSection & 0x7F;
TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00] + CurChip;
if (! stricmp_u(LStr, "Disabled"))
{
TempCOpt->Disabled = GetBoolFromStr(RStr);
}
else if (! stricmp_u(LStr, "EmulatorType"))
{
TempCOpt->EmuCore = (UINT8)strtol(RStr, NULL, 0);
}
else if (! stricmp_u(LStr, "MuteMask"))
{
if (! CHN_COUNT[CurChip])
break; // must use MuteMaskFM and MuteMask???
TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0);
if (CHN_MASK_CNT[CurChip] < 0x20)
TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1;
}
else if (! strnicmp_u(LStr, "MuteCh", 0x06))
{
if (! CHN_COUNT[CurChip])
break; // must use MuteFM and Mute???
CurChn = (UINT8)strtol(LStr + 0x06, &TempPnt, 0);
if (TempPnt == NULL || *TempPnt)
break;
if (CurChn >= CHN_COUNT[CurChip])
break;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
TempCOpt->ChnMute1 |= TempFlag << CurChn;
}
else
{
switch(CurChip)
{
//case 0x00: // SN76496
case 0x02: // YM2612
if (! stricmp_u(LStr, "MuteDAC"))
{
CurChn = 0x06;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
TempCOpt->ChnMute1 |= TempFlag << CurChn;
}
else if (! stricmp_u(LStr, "DACHighpass"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 0);
TempCOpt->SpecialFlags |= TempFlag << 0;
}
else if (! stricmp_u(LStr, "SSG-EG"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 1);
TempCOpt->SpecialFlags |= TempFlag << 1;
}
else if (! stricmp_u(LStr, "PseudoStereo"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 2);
TempCOpt->SpecialFlags |= TempFlag << 2;
}
break;
//case 0x03: // YM2151
//case 0x04: // SegaPCM
//case 0x05: // RF5C68
case 0x06: // YM2203
if (! stricmp_u(LStr, "DisableAY"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 0);
TempCOpt->SpecialFlags |= TempFlag << 0;
}
break;
case 0x07: // YM2608
case 0x08: // YM2610
if (! stricmp_u(LStr, "DisableAY"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 0);
TempCOpt->SpecialFlags |= TempFlag << 0;
}
else if (! stricmp_u(LStr, "MuteMask_FM"))
{
TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0);
TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip]) - 1;
}
else if (! stricmp_u(LStr, "MuteMask_PCM"))
{
TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0);
TempCOpt->ChnMute2 &= (1 << (CHN_MASK_CNT[CurChip] + 1)) - 1;
}
else if (! strnicmp_u(LStr, "MuteFMCh", 0x08))
{
CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0);
if (TempPnt == NULL || *TempPnt)
break;
if (CurChn >= CHN_COUNT[CurChip])
break;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
TempCOpt->ChnMute1 |= TempFlag << CurChn;
}
else if (! strnicmp_u(LStr, "MutePCMCh", 0x08))
{
CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0);
if (TempPnt == NULL || *TempPnt)
break;
if (CurChn >= CHN_COUNT[CurChip])
break;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute2 &= ~(0x01 << CurChn);
TempCOpt->ChnMute2 |= TempFlag << CurChn;
}
else if (! stricmp_u(LStr, "MuteDT"))
{
CurChn = 0x06;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute2 &= ~(0x01 << CurChn);
TempCOpt->ChnMute2 |= TempFlag << CurChn;
}
break;
case 0x01: // YM2413
case 0x09: // YM3812
case 0x0A: // YM3526
case 0x0B: // Y8950
case 0x0C: // YMF262
CurChn = 0xFF;
if (! stricmp_u(LStr, "MuteBD"))
CurChn = 0x00;
else if (! stricmp_u(LStr, "MuteSD"))
CurChn = 0x01;
else if (! stricmp_u(LStr, "MuteTOM"))
CurChn = 0x02;
else if (! stricmp_u(LStr, "MuteTC"))
CurChn = 0x03;
else if (! stricmp_u(LStr, "MuteHH"))
CurChn = 0x04;
else if (CurChip == 0x0B && ! stricmp_u(LStr, "MuteDT"))
CurChn = 0x05;
if (CurChn != 0xFF)
{
if (CurChip < 0x0C)
CurChn += 9;
else
CurChn += 18;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
TempCOpt->ChnMute1 |= TempFlag << CurChn;
}
break;
case 0x0D: // YMF278B
if (! stricmp_u(LStr, "MuteMask_FM"))
{
TempCOpt->ChnMute1 = strtoul(RStr, NULL, 0);
TempCOpt->ChnMute1 &= (1 << CHN_MASK_CNT[CurChip - 0x01]) - 1;
}
else if (! stricmp_u(LStr, "MuteMask_WT"))
{
TempCOpt->ChnMute2 = strtoul(RStr, NULL, 0);
TempCOpt->ChnMute2 &= (1 << CHN_MASK_CNT[CurChip]) - 1;
}
else if (! strnicmp_u(LStr, "MuteFMCh", 0x08))
{
CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0);
if (TempPnt == NULL || *TempPnt)
break;
if (CurChn >= CHN_COUNT[CurChip - 0x01])
break;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
TempCOpt->ChnMute1 |= TempFlag << CurChn;
}
else if (! strnicmp_u(LStr, "MuteFM", 0x06))
{
CurChn = 0xFF;
if (! stricmp_u(LStr + 6, "BD"))
CurChn = 0x00;
else if (! stricmp_u(LStr + 6, "SD"))
CurChn = 0x01;
else if (! stricmp_u(LStr + 6, "TOM"))
CurChn = 0x02;
else if (! stricmp_u(LStr + 6, "TC"))
CurChn = 0x03;
else if (! stricmp_u(LStr + 6, "HH"))
CurChn = 0x04;
if (CurChn != 0xFF)
{
CurChn += 18;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute1 &= ~(0x01 << CurChn);
TempCOpt->ChnMute1 |= TempFlag << CurChn;
}
}
else if (! strnicmp_u(LStr, "MuteWTCh", 0x08))
{
CurChn = (UINT8)strtol(LStr + 0x08, &TempPnt, 0);
if (TempPnt == NULL || *TempPnt)
break;
if (CurChn >= CHN_MASK_CNT[CurChip])
break;
TempFlag = GetBoolFromStr(RStr);
TempCOpt->ChnMute2 &= ~(0x01 << CurChn);
TempCOpt->ChnMute2 |= TempFlag << CurChn;
}
break;
//case 0x0E: // YMF271
//case 0x0F: // YMZ280B
/*if (! stricmp_u(LStr, "DisableFix"))
{
DISABLE_YMZ_FIX = GetBoolFromStr(RStr);
}
break;*/
//case 0x10: // RF5C164
//case 0x11: // PWM
//case 0x12: // AY8910
case 0x13: // GameBoy
if (! stricmp_u(LStr, "BoostWaveChn"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 0);
TempCOpt->SpecialFlags |= TempFlag << 0;
}
else if (! stricmp_u(LStr, "LowerNoiseChn"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 1);
TempCOpt->SpecialFlags |= TempFlag << 1;
}
else if (! stricmp_u(LStr, "Inaccurate"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 2);
TempCOpt->SpecialFlags |= TempFlag << 2;
}
break;
case 0x14: // NES
if (! stricmp_u(LStr, "SharedOpts"))
{
// 2 bits
TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03;
TempCOpt->SpecialFlags &= ~(0x03 << 0) & 0x7FFF;
TempCOpt->SpecialFlags |= TempLng << 0;
}
else if (! stricmp_u(LStr, "APUOpts"))
{
// 2 bits
TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x03;
TempCOpt->SpecialFlags &= ~(0x03 << 2) & 0x7FFF;
TempCOpt->SpecialFlags |= TempLng << 2;
}
else if (! stricmp_u(LStr, "DMCOpts"))
{
// 8 bits (6 bits used)
TempLng = (UINT32)strtol(RStr, NULL, 0) & 0xFF;
TempCOpt->SpecialFlags &= ~(0xFF << 4) & 0x7FFF;
TempCOpt->SpecialFlags |= TempLng << 4;
}
else if (! stricmp_u(LStr, "FDSOpts"))
{
// 1 bit
TempLng = (UINT32)strtol(RStr, NULL, 0) & 0x01;
TempCOpt->SpecialFlags &= ~(0x01 << 12) & 0x7FFF;
TempCOpt->SpecialFlags |= TempLng << 12;
}
break;
case 0x17: // OKIM6258
if (! stricmp_u(LStr, "Enable10Bit"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 0);
TempCOpt->SpecialFlags |= TempFlag << 0;
}
else if (! stricmp_u(LStr, "RemoveDCOfs"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 1);
TempCOpt->SpecialFlags |= TempFlag << 1;
}
break;
case 0x20: // SCSP
if (! stricmp_u(LStr, "BypassDSP"))
{
TempFlag = GetBoolFromStr(RStr);
TempCOpt->SpecialFlags &= ~(0x01 << 0);
TempCOpt->SpecialFlags |= TempFlag << 0;
}
break;
}
}
break;
case 0xFF: // Dummy Section
break;
}
}
}
TempCOpt = (CHIP_OPTS*)&ChipOpts[0x00];
TempCOpt2 = (CHIP_OPTS*)&ChipOpts[0x01];
for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++, TempCOpt ++, TempCOpt2 ++)
{
TempCOpt2->Disabled = TempCOpt->Disabled;
TempCOpt2->EmuCore = TempCOpt->EmuCore;
TempCOpt2->SpecialFlags = TempCOpt->SpecialFlags;
TempCOpt2->ChnMute1 = TempCOpt->ChnMute1;
TempCOpt2->ChnMute2 = TempCOpt->ChnMute2;
TempCOpt2->ChnMute3 = TempCOpt->ChnMute3;
}
fclose(hFile);
#ifdef WIN32
WinNT_Check();
#endif
if (CHIP_SAMPLE_RATE <= 0)
CHIP_SAMPLE_RATE = SampleRate;
return;
}
static bool GetBoolFromStr(const char* TextStr)
{
if (! stricmp_u(TextStr, "True"))
return true;
else if (! stricmp_u(TextStr, "False"))
return false;
else
return strtol(TextStr, NULL, 0) ? true : false;
}
#if defined(XMAS_EXTRA) || defined(WS_DEMO)
static bool XMas_Extra(char* FileName, bool Mode)
{
char* FileTitle;
const UINT8* XMasData;
UINT32 XMasSize;
FILE* hFile;
if (! Mode)
{ // Prepare Mode
FileTitle = NULL;
XMasData = NULL;
#ifdef XMAS_EXTRA
if (! stricmp_u(FileName, "WEWISH")
{
FileTitle = "WEWISH.CMF";
XMasSize = sizeof(WEWISH_CMF);
XMasData = WEWISH_CMF;
}
else if (! stricmp_u(FileName, "tim7")
{
FileTitle = "lem_tim7.vgz";
XMasSize = sizeof(TIM7_VGZ);
XMasData = TIM7_VGZ;
}
else if (! stricmp_u(FileName, "jingleb")
{
FileTitle = "lxmas_jb.dro";
XMasSize = sizeof(JB_DRO);
XMasData = JB_DRO;
}
else if (! stricmp_u(FileName, "rudolph")
{
FileTitle = "rudolph.dro";
XMasSize = sizeof(RODOLPH_DRO);
XMasData = RODOLPH_DRO;
}
else if (! stricmp_u(FileName, "clyde"))
{
FileTitle = "clyde1_1.dro";
XMasSize = sizeof(clyde1_1_dro);
XMasData = clyde1_1_dro;
}
#elif defined(WS_DEMO)
if (! stricmp_u(FileName, "wswan"))
{
FileTitle = "SWJ-SQRC01_1C.vgz";
XMasSize = sizeof(FF1ws_1C);
XMasData = FF1ws_1C;
}
#endif
if (XMasData)
{
#ifdef WIN32
GetEnvironmentVariable("Temp", FileName, MAX_PATH);
#else
strcpy(FileName, "/tmp");
#endif
strcat(FileName, DIR_STR);
if (FileTitle == NULL)
FileTitle = "XMas.dat";
strcat(FileName, FileTitle);
hFile = fopen(FileName, "wb");
if (hFile == NULL)
{
FileName[0x00] = 0x00;
printerr("Critical XMas-Error!\n");
return false;
}
fwrite(XMasData, 0x01, XMasSize, hFile);
fclose(hFile);
}
else
{
FileName = NULL;
return false;
}
}
else
{ // Unprepare Mode
if (! remove(FileName))
return false;
// btw: it's intentional that the user can grab the file from the temp-folder
}
return true;
}
#endif
#ifndef WIN32
static void ConvertCP1252toUTF8(char** DstStr, const char* SrcStr)
{
const UINT16 CONV_TBL[0x20] =
{ 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87
0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, // 88-8F
0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97
0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178}; // 98-9F
UINT32 StrLen;
UINT16 UnicodeChr;
char* DstPtr;
const char* SrcPtr;
SrcPtr = SrcStr;
StrLen = 0x00;
while(*SrcPtr != '\0')
{
if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0)
UnicodeChr = *SrcPtr;
else
UnicodeChr = CONV_TBL[*SrcPtr - 0x80];
if (UnicodeChr < 0x0080)
StrLen ++;
else if (UnicodeChr < 0x0800)
StrLen += 2;
else
StrLen += 3;
SrcPtr ++;
}
*DstStr = (char*)malloc((StrLen + 0x01) * sizeof(char));
SrcPtr = SrcStr;
DstPtr = *DstStr;
while(*SrcPtr != '\0')
{
if (*SrcPtr < 0x80 || *SrcPtr >= 0xA0)
UnicodeChr = (unsigned char)*SrcPtr;
else
UnicodeChr = CONV_TBL[*SrcPtr - 0x80];
if (UnicodeChr < 0x0080)
{
*DstPtr = UnicodeChr & 0xFF;
DstPtr ++;
}
else if (UnicodeChr < 0x0800)
{
DstPtr[0x00] = 0xC0 | ((UnicodeChr >> 6) & 0x1F);
DstPtr[0x01] = 0x80 | ((UnicodeChr >> 0) & 0x3F);
DstPtr += 0x02;
}
else
{
DstPtr[0x00] = 0xE0 | ((UnicodeChr >> 12) & 0x0F);
DstPtr[0x01] = 0x80 | ((UnicodeChr >> 6) & 0x3F);
DstPtr[0x02] = 0x80 | ((UnicodeChr >> 0) & 0x3F);
DstPtr += 0x03;
}
SrcPtr ++;
}
*DstPtr = '\0';
return;
}
#endif
static bool OpenPlayListFile(const char* FileName)
{
const char M3UV2_HEAD[] = "#EXTM3U";
const char M3UV2_META[] = "#EXTINF:";
const UINT8 UTF8_SIG[] = {0xEF, 0xBB, 0xBF};
UINT32 METASTR_LEN;
size_t RetVal;
FILE* hFile;
UINT32 LineNo;
bool IsV2Fmt;
UINT32 PLAlloc;
char TempStr[0x1000]; // 4096 chars should be enough
char* RetStr;
bool IsUTF8;
hFile = fopen(FileName, "rt");
if (hFile == NULL)
return false;
RetVal = fread(TempStr, 0x01, 0x03, hFile);
if (RetVal >= 0x03)
IsUTF8 = ! memcmp(TempStr, UTF8_SIG, 0x03);
else
IsUTF8 = false;
rewind(hFile);
PLAlloc = 0x0100;
PLFileCount = 0x00;
LineNo = 0x00;
IsV2Fmt = false;
METASTR_LEN = strlen(M3UV2_META);
PlayListFile = (char**)malloc(PLAlloc * sizeof(char*));
while(! feof(hFile))
{
RetStr = fgets(TempStr, 0x1000, hFile);
if (RetStr == NULL)
break;
//RetStr = strchr(TempStr, 0x0D);
//if (RetStr)
// *RetStr = 0x00; // remove NewLine-Character
RetStr = TempStr + strlen(TempStr) - 0x01;
while(RetStr >= TempStr && *RetStr < 0x20)
{
*RetStr = 0x00; // remove NewLine-Characters
RetStr --;
}
if (! strlen(TempStr))
continue;
if (! LineNo)
{
if (! strcmp(TempStr, M3UV2_HEAD))
{
IsV2Fmt = true;
LineNo ++;
continue;
}
}
if (IsV2Fmt)
{
if (! strncmp(TempStr, M3UV2_META, METASTR_LEN))
{
// Ignore Metadata of m3u Version 2
LineNo ++;
continue;
}
}
if (PLFileCount >= PLAlloc)
{
PLAlloc += 0x0100;
PlayListFile = (char**)realloc(PlayListFile, PLAlloc * sizeof(char*));
}
// TODO:
// - supprt UTF-8 m3us under Windows
// - force IsUTF8 via Commandline
#ifdef WIN32
// Windows uses the 1252 Codepage by default
PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char));
strcpy(PlayListFile[PLFileCount], TempStr);
#else
if (! IsUTF8)
{
// Most recent Linux versions use UTF-8, so I need to convert all strings.
ConvertCP1252toUTF8(&PlayListFile[PLFileCount], TempStr);
}
else
{
PlayListFile[PLFileCount] = (char*)malloc((strlen(TempStr) + 0x01) * sizeof(char));
strcpy(PlayListFile[PLFileCount], TempStr);
}
#endif
StandardizeDirSeparators(PlayListFile[PLFileCount]);
PLFileCount ++;
LineNo ++;
}
fclose(hFile);
RetStr = GetLastDirSeparator(FileName);
if (RetStr != NULL)
{
RetStr ++;
strncpy(PLFileBase, FileName, RetStr - FileName);
PLFileBase[RetStr - FileName] = '\0';
StandardizeDirSeparators(PLFileBase);
}
else
{
strcpy(PLFileBase, "");
}
return true;
}
static bool OpenMusicFile(const char* FileName)
{
if (OpenVGMFile(FileName))
return true;
else if (OpenOtherFile(FileName))
return true;
return false;
}
/*#ifdef WIN32
// "printc" initially meant "print correct, though "print console" would also make sense ;)
static void printc(const char* format, ...)
{
int RetVal;
UINT32 BufSize;
char* printbuf;
va_list arg_list;
BufSize = 0x00;
printbuf = NULL;
do
{
BufSize += 0x100;
printbuf = (char*)realloc(printbuf, BufSize);
va_start(arg_list, format);
RetVal = _vsnprintf(printbuf, BufSize - 0x01, format, arg_list);
va_end(arg_list);
} while(RetVal == -1 && BufSize < 0x1000);
CharToOem(printbuf, printbuf);
printf("%s", printbuf);
free(printbuf);
return;
}
#endif*/
static void wprintc(const wchar_t* format, ...)
{
va_list arg_list;
int RetVal;
UINT32 BufSize;
wchar_t* printbuf;
#ifdef WIN32
UINT32 StrLen;
char* oembuf;
DWORD CPMode;
#endif
BufSize = 0x00;
printbuf = NULL;
do
{
BufSize += 0x100;
printbuf = (wchar_t*)realloc(printbuf, BufSize * sizeof(wchar_t));
// Note: On Linux every vprintf call needs its own set of va_start/va_end commands.
// Under Windows (with VC6) one only one block for all calls works, too.
va_start(arg_list, format);
RetVal = _vsnwprintf(printbuf, BufSize - 0x01, format, arg_list);
va_end(arg_list);
} while(RetVal == -1 && BufSize < 0x1000);
#ifdef WIN32
StrLen = wcslen(printbuf);
// This is the only way to print Unicode stuff to the Windows console.
// No, wprintf doesn't work.
RetVal = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), printbuf, StrLen, &CPMode, NULL);
if (! RetVal) // call failed (e.g. with ERROR_CALL_NOT_IMPLEMENTED on Win95)
{
// fallback to printf with OEM codepage
oembuf = (char*)malloc(BufSize);
/*if (GetConsoleOutputCP() == GetOEMCP())
CPMode = CP_OEMCP;
else
CPMode = CP_ACP;*/
CPMode = GetConsoleOutputCP();
WideCharToMultiByte(CPMode, 0x00, printbuf, StrLen + 1, oembuf, BufSize, NULL, NULL);
printf("%s", oembuf);
free(oembuf);
}
#else
printf("%ls", printbuf);
#endif
free(printbuf);
return;
}
static void PrintChipStr(UINT8 ChipID, UINT8 SubType, UINT32 Clock)
{
if (! Clock)
return;
if (ChipID == 0x00 && (Clock & 0x80000000))
Clock &= ~0x40000000;
if (Clock & 0x80000000)
{
Clock &= ~0x80000000;
ChipID |= 0x80;
}
if (Clock & 0x40000000)
printf("2x");
printf("%s, ", GetAccurateChipName(ChipID, SubType));
return;
}
static const wchar_t* GetTagStrEJ(const wchar_t* EngTag, const wchar_t* JapTag)
{
const wchar_t* RetTag;
if (EngTag == NULL || ! wcslen(EngTag))
{
RetTag = JapTag;
}
else if (JapTag == NULL || ! wcslen(JapTag))
{
RetTag = EngTag;
}
else
{
if (! PreferJapTag)
RetTag = EngTag;
else
RetTag = JapTag;
}
if (RetTag == NULL)
return L"";
else
return RetTag;
}
static void ShowVGMTag(void)
{
const wchar_t* TitleTag;
const wchar_t* GameTag;
const wchar_t* AuthorTag;
const wchar_t* SystemTag;
UINT8 CurChip;
UINT32 ChpClk;
UINT8 ChpType;
INT16 VolMod;
#ifdef SET_CONSOLE_TITLE
wchar_t TitleStr[0x80];
UINT32 StrLen;
#endif
TitleTag = GetTagStrEJ(VGMTag.strTrackNameE, VGMTag.strTrackNameJ);
GameTag = GetTagStrEJ(VGMTag.strGameNameE, VGMTag.strGameNameJ);
AuthorTag = GetTagStrEJ(VGMTag.strAuthorNameE, VGMTag.strAuthorNameJ);
SystemTag = GetTagStrEJ(VGMTag.strSystemNameE, VGMTag.strSystemNameJ);
#ifdef SET_CONSOLE_TITLE
// --- Show "Song (Game) - VGM Player" as Console Title ---
if (! wcslen(TitleTag))
{
char* TempPtr1;
char* TempPtr2;
TempPtr1 = strrchr(VgmFileName, '\\');
TempPtr2 = strrchr(VgmFileName, '/');
if (TempPtr1 < TempPtr2)
TempPtr1 = TempPtr2;
if (TempPtr1 == NULL)
TempPtr1 = VgmFileName;
else
TempPtr1 ++;
//strncpy(TitleStr, TempPtr1, 0x70);
mbstowcs(TitleStr, TempPtr1, 0x7F);
TitleStr[0x70] = '\0';
}
else
{
#if (defined(_MSC_VER) && _MSC_VER < 1400)// || defined(__MINGW32__)
swprintf(TitleStr, L"%.*ls", 0x70, TitleTag);
#else
swprintf(TitleStr, 0x80, L"%.*ls", 0x70, TitleTag);
#endif
}
StrLen = wcslen(TitleStr);
if (wcslen(GameTag) && StrLen < 0x6C)
{
#if (defined(_MSC_VER) && _MSC_VER < 1400)// || defined(__MINGW32__)
swprintf(TitleStr + StrLen, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag);
#else
swprintf(TitleStr + StrLen, 0x80, L" (%.*ls)", 0x70 - 3 - StrLen, GameTag);
#endif
StrLen = wcslen(TitleStr);
}
wcscat(TitleStr, L" - " APP_NAME_L);
#ifdef WIN32
SetConsoleTitleW(TitleStr); // Set Windows Console Title
#else
printf("\x1B]0;%ls\x07", TitleStr); // Set xterm/rxvt Terminal Title
#endif
#endif
// --- Display Tag Data ---
if (VGMHead.bytVolumeModifier <= VOLUME_MODIF_WRAP)
VolMod = VGMHead.bytVolumeModifier;
else if (VGMHead.bytVolumeModifier == (VOLUME_MODIF_WRAP + 0x01))
VolMod = VOLUME_MODIF_WRAP - 0x100;
else
VolMod = VGMHead.bytVolumeModifier - 0x100;
wprintc(L"Track Title:\t%ls\n", TitleTag);
wprintc(L"Game Name:\t%ls\n", GameTag);
wprintc(L"System:\t\t%ls\n", SystemTag);
wprintc(L"Composer:\t%ls\n", AuthorTag);
wprintc(L"Release:\t%ls\n", VGMTag.strReleaseDate);
printf("Version:\t%X.%02X\t", VGMHead.lngVersion >> 8, VGMHead.lngVersion & 0xFF);
printf(" Gain:%5.2f\t", pow(2.0, VolMod / (double)0x20));
printf("Loop: ");
if (VGMHead.lngLoopOffset)
{
UINT32 PbRateMul;
UINT32 PbRateDiv;
UINT32 PbSamples;
// calculate samples for correct display with changed playback rate
if (! VGMPbRate || ! VGMHead.lngRate)
{
PbRateMul = 1;
PbRateDiv = 1;
}
else
{
PbRateMul = VGMHead.lngRate;
PbRateDiv = VGMPbRate;
}
PbSamples = (UINT32)((UINT64)VGMHead.lngLoopSamples * PbRateMul / PbRateDiv);
printf("Yes (");
PrintMinSec(PbSamples, VGMSampleRate);
printf(")\n");
}
else
{
printf("No\n");
}
wprintc(L"VGM by:\t\t%ls\n", VGMTag.strCreator);
wprintc(L"Notes:\t\t%ls\n", VGMTag.strNotes);
printf("\n");
printf("Used chips:\t");
for (CurChip = 0x00; CurChip < CHIP_COUNT; CurChip ++)
{
ChpClk = GetChipClock(&VGMHead, CurChip, &ChpType);
if (ChpClk && GetChipClock(&VGMHead, 0x80 | CurChip, NULL))
ChpClk |= 0x40000000;
PrintChipStr(CurChip, ChpType, ChpClk);
}
printf("\b\b \n");
printf("\n");
return;
}
#define LOG_SAMPLES (SampleRate / 5)
static void PlayVGM_UI(void)
{
INT32 VGMPbSmplCount;
INT32 PlaySmpl;
UINT8 KeyCode;
UINT32 VGMPlaySt;
UINT32 VGMPlayEnd;
char WavFileName[MAX_PATH];
char* TempStr;
WAVE_16BS* TempBuf;
UINT8 RetVal;
UINT32 TempLng;
bool PosPrint;
bool LastUninit;
bool QuitPlay;
UINT32 PlayTimeEnd;
printf("Initializing ...\r");
PlayVGM();
/*switch(LogToWave)
{
case 0x00:
break;
case 0x01:
// Currently there's no record for Hardware FM
PlayingMode = 0x00; // Impossible to log at full speed AND use FMPort
break;
case 0x02:
if (PlayingMode == 0x01)
LogToWave = 0x00; // Output and log sound (FM isn't logged)
break;
}*/
switch(PlayingMode)
{
case 0x00:
AUDIOBUFFERU = 10;
break;
case 0x01:
AUDIOBUFFERU = 0; // no AudioBuffers needed
break;
case 0x02:
AUDIOBUFFERU = 5; // try to sync Hardware/Software Emulator as well as possible
break;
}
if (AUDIOBUFFERU < NEED_LARGE_AUDIOBUFS)
AUDIOBUFFERU = NEED_LARGE_AUDIOBUFS;
if (ForceAudioBuf && AUDIOBUFFERU)
AUDIOBUFFERU = ForceAudioBuf;
switch(FileMode)
{
case 0x00: // VGM
// RAW Log: no loop, no Creator, System Name set
IsRAWLog = (! VGMHead.lngLoopOffset && ! wcslen(VGMTag.strCreator) &&
(wcslen(VGMTag.strSystemNameE) || wcslen(VGMTag.strSystemNameJ)));
break;
case 0x01: // CMF
IsRAWLog = false;
break;
case 0x02: // DRO
IsRAWLog = true;
break;
}
if (! VGMHead.lngTotalSamples)
IsRAWLog = false;
#ifndef WIN32
changemode(true);
#endif
switch(PlayingMode)
{
case 0x00:
case 0x02:
if (LogToWave)
{
strcpy(WavFileName, VgmFileName);
TempStr = GetFileExtention(WavFileName);
if (TempStr == NULL)
TempStr = WavFileName + strlen(WavFileName);
else
TempStr --;
strcpy(TempStr, ".wav");
strcpy(SoundLogFile, WavFileName);
}
//FullBufFill = ! LogToWave;
switch(LogToWave)
{
case 0x00:
case 0x02:
SoundLogging(LogToWave ? true : false);
if (FirstInit || ! StreamStarted)
{
// support smooth transistions between songs
RetVal = StartStream(0x00);
if (RetVal)
{
printf("Error openning Sound Device!\n");
return;
}
StreamStarted = true;
}
PauseStream(PausePlay);
break;
case 0x01:
TempBuf = (WAVE_16BS*)malloc(SAMPLESIZE * LOG_SAMPLES);
if (TempBuf == NULL)
{
printf("Allocation Error!\n");
return;
}
StartStream(0xFF);
RetVal = SaveFile(0x00000000, NULL);
if (RetVal)
{
printf("Can't open %s!\n", SoundLogFile);
return;
}
break;
}
break;
case 0x01:
// PlayVGM() does it all
//FullBufFill = true;
break;
}
FirstInit = false;
VGMPlaySt = VGMPos;
if (VGMHead.lngGD3Offset)
VGMPlayEnd = VGMHead.lngGD3Offset;
else
VGMPlayEnd = VGMHead.lngEOFOffset;
VGMPlayEnd -= VGMPlaySt;
if (! FileMode)
VGMPlayEnd --; // EOF Command doesn't count
PosPrint = true;
PlayTimeEnd = 0;
QuitPlay = false;
while(! QuitPlay)
{
if (! PausePlay || PosPrint)
{
PosPrint = false;
VGMPbSmplCount = SampleVGM2Playback(VGMHead.lngTotalSamples);
PlaySmpl = VGMPos - VGMPlaySt;
#ifdef WIN32
printf("Playing %01.2f%%\t", 100.0 * PlaySmpl / VGMPlayEnd);
#else
// \t doesn't display correctly under Linux
// but \b causes flickering under Windows
printf("Playing %01.2f%% \b\b\b\t", 100.0 * PlaySmpl / VGMPlayEnd);
#endif
if (LogToWave != 0x01)
{
PlaySmpl = (BlocksSent - BlocksPlayed) * SMPL_P_BUFFER;
PlaySmpl = VGMSmplPlayed - PlaySmpl;
}
else
{
PlaySmpl = VGMSmplPlayed;
}
if (! VGMCurLoop)
{
if (PlaySmpl < 0)
PlaySmpl = 0;
}
else
{
while(PlaySmpl < SampleVGM2Playback(VGMHead.lngTotalSamples -
VGMHead.lngLoopSamples))
PlaySmpl += SampleVGM2Playback(VGMHead.lngLoopSamples);
}
//if (PlaySmpl > VGMPbSmplCount)
// PlaySmpl = VGMPbSmplCount;
PrintMinSec(PlaySmpl, SampleRate);
printf(" / ");
PrintMinSec(VGMPbSmplCount, SampleRate);
printf(" seconds");
if (Show95Cmds && Last95Max != 0xFFFF)
{
if (Show95Cmds == 0x01)
printf(" %02hX / %02hX", 1 + Last95Drum, Last95Max);
else if (Show95Cmds == 0x02)
printf(" %02hX / %02hX at %5u Hz", 1 + Last95Drum, Last95Max, Last95Freq);
else if (Show95Cmds == 0x03)
printf(" %02hX / %02hX at %4.1f KHz", 1 + Last95Drum, Last95Max,
Last95Freq / 1000.0);
}
//printf(" %u / %u", multipcm_get_channels(0, NULL), 28);
printf("\r");
#ifndef WIN32
fflush(stdout);
#endif
if (LogToWave == 0x01 && ! PausePlay)
{
TempLng = FillBuffer(TempBuf, LOG_SAMPLES);
if (TempLng)
SaveFile(TempLng, TempBuf);
if (EndPlay)
break;
}
else
{
#ifdef WIN32
Sleep(50);
#endif
}
}
else
{
#ifdef WIN32
Sleep(1);
#endif
}
#ifndef WIN32
if (! PausePlay)
WaveOutLinuxCallBack();
else
Sleep(100);
#endif
if (EndPlay)
{
if (! PlayTimeEnd)
{
PlayTimeEnd = PlayingTime;
// quitting now terminates the program, so I need some special
// checks to make sure that the rest of the audio buffer is played
if (! PLFileCount || CurPLFile >= PLFileCount - 0x01)
{
if (FileMode == 0x01)
PlayTimeEnd += SampleRate << 1; // Add 2 secs
PlayTimeEnd += AUDIOBUFFERU * SMPL_P_BUFFER;
}
}
if (PlayingTime >= PlayTimeEnd)
QuitPlay = true;
}
if (_kbhit())
{
KeyCode = _getch();
if (KeyCode < 0x80)
KeyCode = toupper(KeyCode);
switch(KeyCode)
{
#ifndef WIN32
case 0x1B: // Special Key
KeyCode = _getch();
if (KeyCode == 0x1B || KeyCode == 0x00)
{
// ESC Key pressed
QuitPlay = true;
NextPLCmd = 0xFF;
break;
}
switch(KeyCode)
{
case 0x5B:
// Cursor-Key Table
// Key KeyCode
// Up 41
// Down 42
// Left 44
// Right 43
// Cursor only: CursorKey
// Ctrl: 0x31 + 0x3B + 0x35 + CursorKey
// Alt: 0x31 + 0x3B + 0x33 + CursorKey
// Page-Keys: PageKey + 0x7E
// PageUp 35
// PageDown 36
KeyCode = _getch(); // Get 2nd Key
// Convert Cursor Key Code from Linux to Windows
switch(KeyCode)
{
case 0x31: // Ctrl or Alt key
KeyCode = _getch();
if (KeyCode == 0x3B)
{
KeyCode = _getch();
if (KeyCode == 0x35)
{
KeyCode = _getch();
switch(KeyCode)
{
case 0x41:
KeyCode = 0x8D;
break;
case 0x42:
KeyCode = 0x91;
break;
case 0x43:
KeyCode = 0x74;
break;
case 0x44:
KeyCode = 0x73;
break;
default:
KeyCode = 0x00;
break;
}
}
}
if ((KeyCode & 0xF0) == 0x30)
KeyCode = 0x00;
break;
case 0x35:
KeyCode = 0x49;
_getch();
break;
case 0x36:
KeyCode = 0x51;
_getch();
break;
case 0x41:
KeyCode = 0x48;
break;
case 0x42:
KeyCode = 0x50;
break;
case 0x43:
KeyCode = 0x4D;
break;
case 0x44:
KeyCode = 0x4B;
break;
default:
KeyCode = 0x00;
break;
}
}
// At this point I have Windows-style keys.
#else //#ifdef WIN32
case 0xE0: // Special Key
// Cursor-Key Table
// Shift + Cursor results in the usual value for the Cursor Key
// Alt + Cursor results in 0x00 + (0x50 + CursorKey) (0x00 instead of 0xE0)
// Key None Ctrl
// Up 48 8D
// Down 50 91
// Left 4B 73
// Right 4D 74
KeyCode = _getch(); // Get 2nd Key
#endif
switch(KeyCode)
{
case 0x4B: // Cursor Left
PlaySmpl = -5;
break;
case 0x4D: // Cursor Right
PlaySmpl = 5;
break;
case 0x73: // Ctrl + Cursor Left
PlaySmpl = -60;
break;
case 0x74: // Ctrl + Cursor Right
PlaySmpl = 60;
break;
case 0x49: // Page Up
if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile)
{
NextPLCmd = 0x01;
QuitPlay = true;
}
PlaySmpl = 0;
break;
case 0x51: // Page Down
if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01)
{
NextPLCmd = 0x00;
QuitPlay = true;
}
PlaySmpl = 0;
break;
default:
PlaySmpl = 0;
break;
}
if (PlaySmpl)
{
SeekVGM(true, PlaySmpl * SampleRate);
PosPrint = true;
}
break;
#ifdef WIN32
case 0x1B: // ESC
#endif
case 'Q':
QuitPlay = true;
NextPLCmd = 0xFF;
break;
case ' ':
PauseVGM(! PausePlay);
PosPrint = true;
break;
case 'F': // Fading
FadeTime = FadeTimeN;
FadePlay = true;
break;
case 'R': // Restart
RestartVGM();
PosPrint = true;
break;
case 'B': // Previous file (Back)
if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile)
{
NextPLCmd = 0x01;
QuitPlay = true;
}
break;
case 'N': // Next file
if (PLFileCount && /*! NextPLCmd &&*/ CurPLFile < PLFileCount - 0x01)
{
NextPLCmd = 0x00;
QuitPlay = true;
}
break;
}
}
/*if (! PauseThread && FadePlay && (! FadeTime || MasterVol == 0.0f))
{
QuitPlay = true;
}*/
if (FadeRAWLog && IsRAWLog && ! PausePlay && ! FadePlay && FadeTimeN)
{
PlaySmpl = (INT32)VGMHead.lngTotalSamples -
FadeTimeN * VGMSampleRate / 1500;
if (VGMSmplPos >= PlaySmpl)
{
FadeTime = FadeTimeN;
FadePlay = true; // (FadeTime / 1500) ends at 33%
}
}
}
ThreadNoWait = false;
// Last Uninit: ESC pressed, no playlist, last file in playlist
LastUninit = (NextPLCmd & 0x80) || ! PLFileCount ||
(NextPLCmd == 0x00 && CurPLFile >= PLFileCount - 0x01);
switch(PlayingMode)
{
case 0x00:
switch(LogToWave)
{
case 0x00:
case 0x02:
if (LastUninit)
{
StopStream();
StreamStarted = false;
}
else
{
if (ThreadPauseEnable)
{
ThreadPauseConfrm = false;
PauseThread = true;
while(! ThreadPauseConfrm)
Sleep(1); // Wait until the Thread is finished
}
else
{
PauseThread = true;
}
if (LogToWave)
SaveFile(0xFFFFFFFF, NULL);
}
break;
case 0x01:
SaveFile(0xFFFFFFFF, NULL);
break;
}
break;
case 0x01:
if (StreamStarted)
{
StopStream();
StreamStarted = false;
}
break;
case 0x02:
if (LastUninit)
{
StopStream();
StreamStarted = false;
#ifdef MIXER_MUTING
#ifdef WIN32
mixerClose(hmixer);
#else
close(hmixer);
#endif
#endif
}
else
{
if (ThreadPauseEnable)
{
ThreadPauseConfrm = false;
PauseThread = true;
while(! ThreadPauseConfrm)
Sleep(1); // Wait until the Thread is finished
PauseStream(true);
}
else
{
PauseThread = true;
}
}
break;
}
#ifndef WIN32
changemode(false);
#endif
StopVGM();
printf("\nPlaying finished.\n");
return;
}
INLINE INT8 sign(double Value)
{
if (Value > 0.0)
return 1;
else if (Value < 0.0)
return -1;
else
return 0;
}
INLINE long int Round(double Value)
{
// Alternative: (fabs(Value) + 0.5) * sign(Value);
return (long int)(Value + 0.5 * sign(Value));
}
INLINE double RoundSpecial(double Value, double RoundTo)
{
return (long int)(Value / RoundTo + 0.5 * sign(Value)) * RoundTo;
}
static void PrintMinSec(UINT32 SamplePos, UINT32 SmplRate)
{
float TimeSec;
UINT16 TimeMin;
UINT16 TimeHours;
TimeSec = (float)RoundSpecial(SamplePos / (double)SmplRate, 0.01);
//TimeSec = SamplePos / (float)SmplRate;
TimeMin = (UINT16)TimeSec / 60;
TimeSec -= TimeMin * 60;
if (! PrintMSHours)
{
printf("%02hu:%05.2f", TimeMin, TimeSec);
}
else
{
TimeHours = TimeMin / 60;
TimeMin %= 60;
printf("%hu:%02hu:%05.2f", TimeHours, TimeMin, TimeSec);
}
return;
}