winamp/Src/Plugins/Input/in_cdda/Main.cpp
2024-09-24 14:54:57 +02:00

598 lines
13 KiB
C++

//#define PLUGIN_NAME "Nullsoft CD Plug-in"
#define PLUGIN_VERSION L"4.7"
#include "main.h"
#include "cddb.h"
#include "CDPlay.h"
#include "DAEPlay.h"
#include "MCIPlay.h"
#include "WindacPlay.h"
#include "PlayStatus.h"
#include "../nu/AutoWide.h"
#include "../nu/AutoChar.h"
#include "../Winamp/strutil.h"
#include "../winamp/wa_ipc.h"
#include <shlwapi.h>
#include "api__in_cdda.h"
#include "workorder.h"
#include <strsafe.h>
using namespace Nullsoft::Utility;
Nullsoft::Utility::LockGuard *playDeviceGuard = 0;
char * s_last_error = NULL;
static wchar_t playDriveLetter;
//extern int config_maxextractspeed;
api_config *AGAVE_API_CONFIG=0;
#ifndef _DEBUG
BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
DisableThreadLibraryCalls(hInst);
return TRUE;
}
#endif
#ifdef IGNORE_API_GRACENOTE
static DINFO g_ps;
#endif
int g_ps_inuse = 0;
int g_playtrack, g_playlength;
wchar_t lastfn[1024] = {0};
int paused;
void _setvolume();
DWORD MainThreadId;
extern char INI_FILE[];
extern char app_name[];
int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
{
MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
msgbx.lpszText = message;
msgbx.lpszCaption = title;
msgbx.lpszIcon = MAKEINTRESOURCE(102);
msgbx.hInstance = GetModuleHandle(0);
msgbx.dwStyle = MB_USERICON;
msgbx.hwndOwner = parent;
return MessageBoxIndirect(&msgbx);
}
void about(HWND hwndParent)
{
wchar_t message[1024] = {0}, text[1024] = {0};
WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_CD_PLUGIN_OLD,text,1024);
StringCchPrintf(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
line.description, TEXT(__DATE__));
DoAboutMessageBox(hwndParent,text,message);
}
void quit();
int init();
int isourfile(const in_char *fn)
{
if (!_wcsnicmp(fn, L"cda://", 6)) return 1;
return 0;
}
volatile int done = 0;
int g_lastpos = 0;
C_CDPlay *g_cdplay = 0;
const wchar_t *filename(const wchar_t *fn)
{
const wchar_t *s = scanstr_backcW(fn, L"\\/", 0);
if (!s) return fn;
return (s + 1);
}
bool ParseName(const wchar_t *fn, wchar_t &device, int &trackNum)
{
wchar_t track[16] = L"1";
if (!_wcsnicmp(fn, L"cda://", 6))
{
fn += 6;
wchar_t d[16] = {0};
wchar_t *p = d;
while (fn && *fn && *fn != L',' && (p - d < 15)) *p++ = *fn++;
if (p) *p = 0;
device = toupper(d[0]);
if (*fn == L',') fn++;
lstrcpyn(track, fn, ARRAYSIZE(track));
trackNum = _wtoi(track);
return true;
}
else if (!_wcsicmp(extensionW(fn), L"cda"))
{
const wchar_t *f = filename(fn);
if (!_wcsnicmp(f, L"track", 5)) f += 5;
wchar_t t[16] = {0};
wchar_t *p = t;
while (f && *f && *f != L'.' && (p - t < 15)) *p++ = *f++;
lstrcpyn(track, t, ARRAYSIZE(track));
device = toupper(fn[0]);
trackNum = _wtoi(track);
return true;
}
return false;
}
WindacPlay *windacPlayer = 0;
DAEPlay *daePlayer = 0;
MciPlay *mciPlayer = 0;
int play(const in_char *fn)
{
done = 0;
lstrcpyn(lastfn, fn, 1024);
line.is_seekable = 0;
g_lastpos = 0;
int track = -1;
if (!ParseName(fn, playDriveLetter, track))
return 1;
if (playStatus[playDriveLetter].IsRipping() || (g_cdplay && g_cdplay->IsPlaying(playDriveLetter)))
{
wchar_t title[32] = {0};
MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CD_CURRENTLY_IN_USE),
WASABI_API_LNGSTRINGW_BUF(IDS_DRIVE_IN_USE,title,32), MB_OK);
return 1;
}
if (g_cdplay) delete g_cdplay; g_cdplay = NULL;
//first, try DAE
if (!daePlayer) daePlayer = new DAEPlay;
g_cdplay = daePlayer;
int ret = (g_cdplay ? g_cdplay->play(playDriveLetter, track) : 0);
if (ret != 0)
{
if (g_cdplay) delete g_cdplay; g_cdplay = daePlayer = NULL;
//second, try Windac
if (!windacPlayer) windacPlayer = new WindacPlay;
g_cdplay = windacPlayer;
ret = (g_cdplay ? g_cdplay->play(playDriveLetter, track) : 0);
if (ret != 0)
{
if (g_cdplay) delete g_cdplay; g_cdplay = windacPlayer = NULL;
//try MCI
if (!mciPlayer) mciPlayer = new MciPlay;
g_cdplay = mciPlayer;
int ret = (g_cdplay ? g_cdplay->play(playDriveLetter, track) : 1);
if (ret != 0)
{
//no luck
if (g_cdplay) delete g_cdplay; g_cdplay = mciPlayer = NULL;
return ret;
}
}
}
paused = 0;
return 0;
}
void pause()
{
if (g_cdplay) g_cdplay->pause();
paused = 1;
}
void unpause()
{
if (g_cdplay) g_cdplay->unpause();
paused = 0;
}
int ispaused()
{
return paused;
}
void stop()
{
if (g_cdplay)
{
g_cdplay->stop();
g_cdplay = NULL;
}
done = 0;
line.SAVSADeInit();
}
int getlength()
{
if (g_cdplay) return g_cdplay->getlength();
return -1000;
}
int getoutputtime()
{
if (g_cdplay) return g_cdplay->getoutputtime();
return 0;
//return audioGetPos();
}
void setoutputtime(int time_in_ms)
{
if (g_cdplay) g_cdplay->setoutputtime(time_in_ms);
}
void setvolume(int volume)
{
if (volume != -666)
{
a_v = volume;
}
if (g_cdplay) g_cdplay->setvolume(a_v, a_p);
}
void setpan(int pan)
{
a_p = pan;
if (g_cdplay) g_cdplay->setvolume(a_v, a_p);
}
int infoDlg(const in_char *fn, HWND hwnd)
{
return 0;
#if 0 // switched to unified file info dialog in 5.53
if (!_stricmp(extension(fn), "cda") || !_strnicmp(fn, "cda://", 6))
{
if (!g_ps_inuse)
{
char device;
int res=1;
MCIDEVICEID d = 0;
g_ps_inuse = 1;
device = fn[_strnicmp(fn, "cda://", 6) ? 0 : 6];
if (device >= 'a' && device <= 'z') device += 'A' -'a';
CDOpen(&d, device, L"infoDlg");
memset(&g_ps, 0, sizeof(g_ps));
res = GetDiscID(d, &g_ps);
CDClose(&d);
if (!res)
res = GetCDDBInfo(&g_ps, device);
//if (!res) DBEdit(&g_ps, hwnd, 0, device);
//if (!res)
{
if (CDEdit(device, &g_ps, hwnd))
{
g_ps_inuse = 0;
return INFOBOX_EDITED;
}
}
g_ps_inuse = 0;
}
}
return INFOBOX_UNCHANGED;
#endif
}
void getfileinfo(const in_char *filename, in_char *title, int *length_in_ms)
{
#if 0
int track;
char device;
MCIDEVICEID dev2 = 0;
if (length_in_ms) *length_in_ms = -1000;
if (!filename || !*filename) // currently playing
{
if (!_stricmp(extension(lastfn), "cda") || !_strnicmp(lastfn, "cda://", 6))
{
#ifdef IGNORE_API_GRACENOTE
if (title)
{
lstrcpynA(title, "CD Track", GETFILEINFO_TITLE_LENGTH);
if (!g_ps_inuse)
{
g_ps_inuse = 1;
memset(&g_ps, 0, sizeof(g_ps));
if (CDOpen(&dev2, playDriveLetter, L"getfileinfo"))
{
wchar_t wtitle[256] = {0};
int ret = GetDiscID(dev2, &g_ps);
CDClose(&dev2);
if (!ret && GetCDDBInfo(&g_ps, 0)) // TODO: get device letter
PostMessage(line.hMainWindow, WM_USER, (WPARAM) L"cda://", 247/*IPC_REFRESHPLCACHE*/);
if (wtitle[0])
lstrcpynA(title, AutoChar(wtitle), GETFILEINFO_TITLE_LENGTH);
}
g_ps_inuse = 0;
}
}
if (length_in_ms) *length_in_ms = g_playlength;
#endif
}
return ;
}
if (title)
{
const char *p = filename + strlen(filename);
while (p >= filename && *p != '\\') p--;
lstrcpynA(title, ++p, GETFILEINFO_TITLE_LENGTH);
}
track = 0;
if (!_strnicmp(filename, "cda://", 6)) // determine length of cd track via MCI
{
track = atoi(filename + 8);
device = filename[6];
if (device >= 'a' && device <= 'z') device += 'A' -'a';
if (length_in_ms)
{
if (CDOpen(&dev2, device, L"getfileinfo"))
{
MCI_SET_PARMS sMCISet;
sMCISet.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
MCISendCommand(dev2, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID) &sMCISet);
*length_in_ms = CDGetTrackLength(dev2, track);
sMCISet.dwTimeFormat = MCI_FORMAT_TMSF;
MCISendCommand(dev2, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID) &sMCISet);
}
}
}
else // determine from RIFF structure of .CDA
{
HMMIO hmmio;
hmmio = mmioOpenA((char *)filename, NULL, MMIO_READ | MMIO_ALLOCBUF);
device = filename[0];
if (device >= 'a' && device <= 'z') device += 'A' -'a';
if (hmmio)
{
MMCKINFO mmckinfoParent; // parent chunk information
mmckinfoParent.fccType = mmioFOURCC('C', 'D', 'D', 'A');
if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL, MMIO_FINDRIFF) == MMSYSERR_NOERROR)
{
MMCKINFO mmckinfoSubchunk; // subchunk information structure
mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK) == MMSYSERR_NOERROR)
{
char *format;
DWORD dwFmtSize; // size of "FMT" chunk
dwFmtSize = mmckinfoSubchunk.cksize;
format = (char *) GlobalAlloc(GPTR, dwFmtSize);
if (mmioRead(hmmio, (HPSTR) format, dwFmtSize) == (int)dwFmtSize)
{
mmioAscend(hmmio, &mmckinfoSubchunk, 0);
track = *((short int *)format + 1);
if (length_in_ms)
{
int length = *((int *)format + 3);
int l = length % 75;
length /= 75;
length *= 1000;
length += (l * 1000) / 75;
*length_in_ms = length;
}
}
GlobalFree(format);
}
}
mmioClose(hmmio, 0);
}
}
#ifdef IGNORE_API_GRACENOTE
if (title && track)
{
if (0 && !g_ps_inuse)
{
g_ps_inuse = 1;
memset(&g_ps, 0, sizeof(g_ps));
if (!dev2)
{
CDOpen(&dev2, device, L"getfileinfo");
}
if (dev2)
{
StringCchPrintfA(title, GETFILEINFO_TITLE_LENGTH, "CD Track %d", track);
wchar_t wtitle[256] = L"";
int ret = GetDiscID(dev2, &g_ps);
CDClose(&dev2);
dev2=0;
if (!ret && GetCDDBInfo(&g_ps, device))
PostMessage(line.hMainWindow, WM_USER, (WPARAM) L"cda://", 247 /*IPC_REFRESHPLCACHE*/);
if (wtitle[0])
lstrcpynA(title, AutoChar(wtitle), GETFILEINFO_TITLE_LENGTH);
}
g_ps_inuse = 0;
}
}
#endif
if (dev2) CDClose(&dev2);
#endif
}
void eq_set(int on, char data[10], int preamp)
{}
In_Module line =
{
IN_VER_RET,
"nullsoft(in_cdda.dll)",
0, // hMainWindow
0, // hDllInstance
0,
0, // is_seekable
1, // uses output plugins
about,//config,
about,
init,
quit,
getfileinfo,
infoDlg,
isourfile,
play,
pause,
unpause,
ispaused,
stop,
getlength,
getoutputtime,
setoutputtime,
setvolume,
setpan,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, // dsp shit
eq_set,
NULL, // setinfo
NULL // out_mod
};
int m_nblock = 0;
extern "C"
{
__declspec(dllexport) In_Module * winampGetInModule2()
{
s_last_error = NULL;
return &line;
}
#if 0 // TODO?
__declspec(dllexport) int winampWriteExtendedFileInfo()
{
s_last_error = NULL;
// write it out
if (m_eiw_lastdrive)
{
AddToDatabase(&setInfo);
m_eiw_lastdrive = 0;
return 1;
}
return 0;
}
#endif
};
// wasabi based services for localisation support
api_language *WASABI_API_LNG = 0;
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
#ifndef IGNORE_API_GRACENOTE
api_gracenote *AGAVE_API_GRACENOTE = 0;
#endif
api_application *WASABI_API_APP = 0;
void SetFileExtensions(void)
{
static char fileExtensionsString[1200] = {0}; // "CDA\0CDDA Audio Tracks (*.CDA)\0"
char* end = 0;
StringCchCopyExA(fileExtensionsString, 1200, "CDA", &end, 0, 0);
StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_CDDA_AUDIO_TRACKS), 0, 0, 0);
line.FileExtensions = fileExtensionsString;
}
int init()
{
if (!IsWindow(line.hMainWindow))
return IN_INIT_FAILURE;
//CoInitialize(0);
#ifndef IGNORE_API_GRACENOTE
Cddb_Initialize();
InitializeCddbCache();
#endif
// loader so that we can get the localisation service api for use
waServiceFactory *sf = line.service->service_getServiceByGuid(languageApiGUID);
if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
sf = line.service->service_getServiceByGuid(AgaveConfigGUID);
if (sf) AGAVE_API_CONFIG = reinterpret_cast<api_config*>(sf->getInterface());
#ifndef IGNORE_API_GRACENOTE
sf = line.service->service_getServiceByGuid(gracenoteApiGUID);
if (sf) AGAVE_API_GRACENOTE = reinterpret_cast<api_gracenote*>(sf->getInterface());
#endif
sf = line.service->service_getServiceByGuid(applicationApiServiceGuid);
if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
// need to have this initialised before we try to do anything with localisation features
WASABI_API_START_LANG(line.hDllInstance,InCDDALangGUID);
static wchar_t szDescription[256];
StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_CD_PLUGIN),PLUGIN_VERSION);
line.description = (char*)szDescription;
SetFileExtensions();
playDeviceGuard = new Nullsoft::Utility::LockGuard;
playStatusGuard = new Nullsoft::Utility::LockGuard;
MainThreadId = GetCurrentThreadId();
config_read();
return IN_INIT_SUCCESS;
}
void quit()
{
#ifndef IGNORE_API_GRACENOTE
ShutdownMusicIDWorkOrder();
#endif
if (playStatusGuard)
{
delete playStatusGuard;
playStatusGuard = 0;
}
if (windacPlayer)
{
delete windacPlayer;
windacPlayer = NULL;
}
if (daePlayer)
{
delete daePlayer;
daePlayer = NULL;
}
if (mciPlayer)
{
delete mciPlayer;
mciPlayer = NULL;
}
#ifndef IGNORE_API_GRACENOTE
ShutDownCDDB();
#endif
waServiceFactory *sf = line.service->service_getServiceByGuid(languageApiGUID);
if (sf) sf->releaseInterface(WASABI_API_LNG);
sf = line.service->service_getServiceByGuid(AgaveConfigGUID);
if (sf) sf->releaseInterface(AGAVE_API_CONFIG);
#ifndef IGNORE_API_GRACENOTE
sf = line.service->service_getServiceByGuid(gracenoteApiGUID);
if (sf) sf->releaseInterface(AGAVE_API_GRACENOTE);
Cddb_Uninitialize();
UninitializeCddbCache();
#endif
CloseTables();
//CoUninitialize();
}