2015-11-27 10:02:41 +00:00
|
|
|
|
// 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;
|
2016-07-02 09:57:36 +00:00
|
|
|
|
extern UINT8 HardStopOldVGMs;
|
2015-11-27 10:02:41 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
2016-07-02 09:57:36 +00:00
|
|
|
|
else if (! stricmp_u(LStr, "HardStopOld"))
|
|
|
|
|
{
|
|
|
|
|
HardStopOldVGMs = (UINT8)strtoul(RStr, &TempPnt, 0);
|
|
|
|
|
if (TempPnt == RStr)
|
|
|
|
|
HardStopOldVGMs = GetBoolFromStr(RStr) ? 0x01 : 0x00;
|
|
|
|
|
}
|
2015-11-27 10:02:41 +00:00
|
|
|
|
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
|
|
|
|
|
{
|
2016-07-02 09:57:36 +00:00
|
|
|
|
#if (defined(_MSC_VER) && _MSC_VER < 1400)// || defined(__MINGW32__)
|
2015-11-27 10:02:41 +00:00
|
|
|
|
swprintf(TitleStr, L"%.*ls", 0x70, TitleTag);
|
|
|
|
|
#else
|
|
|
|
|
swprintf(TitleStr, 0x80, L"%.*ls", 0x70, TitleTag);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
StrLen = wcslen(TitleStr);
|
|
|
|
|
|
|
|
|
|
if (wcslen(GameTag) && StrLen < 0x6C)
|
|
|
|
|
{
|
2016-07-02 09:57:36 +00:00
|
|
|
|
#if (defined(_MSC_VER) && _MSC_VER < 1400)// || defined(__MINGW32__)
|
2015-11-27 10:02:41 +00:00
|
|
|
|
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;
|
|
|
|
|
}
|