Main.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. //#define PLUGIN_NAME "Nullsoft CD Plug-in"
  2. #define PLUGIN_VERSION L"4.7"
  3. #include "main.h"
  4. #include "cddb.h"
  5. #include "CDPlay.h"
  6. #include "DAEPlay.h"
  7. #include "MCIPlay.h"
  8. #include "WindacPlay.h"
  9. #include "PlayStatus.h"
  10. #include "../nu/AutoWide.h"
  11. #include "../nu/AutoChar.h"
  12. #include "../Winamp/strutil.h"
  13. #include "../winamp/wa_ipc.h"
  14. #include <shlwapi.h>
  15. #include "api__in_cdda.h"
  16. #include "workorder.h"
  17. #include <strsafe.h>
  18. using namespace Nullsoft::Utility;
  19. Nullsoft::Utility::LockGuard *playDeviceGuard = 0;
  20. char * s_last_error = NULL;
  21. static wchar_t playDriveLetter;
  22. //extern int config_maxextractspeed;
  23. api_config *AGAVE_API_CONFIG=0;
  24. #ifndef _DEBUG
  25. BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
  26. {
  27. DisableThreadLibraryCalls(hInst);
  28. return TRUE;
  29. }
  30. #endif
  31. #ifdef IGNORE_API_GRACENOTE
  32. static DINFO g_ps;
  33. #endif
  34. int g_ps_inuse = 0;
  35. int g_playtrack, g_playlength;
  36. wchar_t lastfn[1024] = {0};
  37. int paused;
  38. void _setvolume();
  39. DWORD MainThreadId;
  40. extern char INI_FILE[];
  41. extern char app_name[];
  42. int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
  43. {
  44. MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
  45. msgbx.lpszText = message;
  46. msgbx.lpszCaption = title;
  47. msgbx.lpszIcon = MAKEINTRESOURCE(102);
  48. msgbx.hInstance = GetModuleHandle(0);
  49. msgbx.dwStyle = MB_USERICON;
  50. msgbx.hwndOwner = parent;
  51. return MessageBoxIndirect(&msgbx);
  52. }
  53. void about(HWND hwndParent)
  54. {
  55. wchar_t message[1024] = {0}, text[1024] = {0};
  56. WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_CD_PLUGIN_OLD,text,1024);
  57. StringCchPrintf(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
  58. line.description, TEXT(__DATE__));
  59. DoAboutMessageBox(hwndParent,text,message);
  60. }
  61. void quit();
  62. int init();
  63. int isourfile(const in_char *fn)
  64. {
  65. if (!_wcsnicmp(fn, L"cda://", 6)) return 1;
  66. return 0;
  67. }
  68. volatile int done = 0;
  69. int g_lastpos = 0;
  70. C_CDPlay *g_cdplay = 0;
  71. const wchar_t *filename(const wchar_t *fn)
  72. {
  73. const wchar_t *s = scanstr_backcW(fn, L"\\/", 0);
  74. if (!s) return fn;
  75. return (s + 1);
  76. }
  77. bool ParseName(const wchar_t *fn, wchar_t &device, int &trackNum)
  78. {
  79. wchar_t track[16] = L"1";
  80. if (!_wcsnicmp(fn, L"cda://", 6))
  81. {
  82. fn += 6;
  83. wchar_t d[16] = {0};
  84. wchar_t *p = d;
  85. while (fn && *fn && *fn != L',' && (p - d < 15)) *p++ = *fn++;
  86. if (p) *p = 0;
  87. device = toupper(d[0]);
  88. if (*fn == L',') fn++;
  89. lstrcpyn(track, fn, ARRAYSIZE(track));
  90. trackNum = _wtoi(track);
  91. return true;
  92. }
  93. else if (!_wcsicmp(extensionW(fn), L"cda"))
  94. {
  95. const wchar_t *f = filename(fn);
  96. if (!_wcsnicmp(f, L"track", 5)) f += 5;
  97. wchar_t t[16] = {0};
  98. wchar_t *p = t;
  99. while (f && *f && *f != L'.' && (p - t < 15)) *p++ = *f++;
  100. lstrcpyn(track, t, ARRAYSIZE(track));
  101. device = toupper(fn[0]);
  102. trackNum = _wtoi(track);
  103. return true;
  104. }
  105. return false;
  106. }
  107. WindacPlay *windacPlayer = 0;
  108. DAEPlay *daePlayer = 0;
  109. MciPlay *mciPlayer = 0;
  110. int play(const in_char *fn)
  111. {
  112. done = 0;
  113. lstrcpyn(lastfn, fn, 1024);
  114. line.is_seekable = 0;
  115. g_lastpos = 0;
  116. int track = -1;
  117. if (!ParseName(fn, playDriveLetter, track))
  118. return 1;
  119. if (playStatus[playDriveLetter].IsRipping() || (g_cdplay && g_cdplay->IsPlaying(playDriveLetter)))
  120. {
  121. wchar_t title[32] = {0};
  122. MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CD_CURRENTLY_IN_USE),
  123. WASABI_API_LNGSTRINGW_BUF(IDS_DRIVE_IN_USE,title,32), MB_OK);
  124. return 1;
  125. }
  126. if (g_cdplay) delete g_cdplay; g_cdplay = NULL;
  127. //first, try DAE
  128. if (!daePlayer) daePlayer = new DAEPlay;
  129. g_cdplay = daePlayer;
  130. int ret = (g_cdplay ? g_cdplay->play(playDriveLetter, track) : 0);
  131. if (ret != 0)
  132. {
  133. if (g_cdplay) delete g_cdplay; g_cdplay = daePlayer = NULL;
  134. //second, try Windac
  135. if (!windacPlayer) windacPlayer = new WindacPlay;
  136. g_cdplay = windacPlayer;
  137. ret = (g_cdplay ? g_cdplay->play(playDriveLetter, track) : 0);
  138. if (ret != 0)
  139. {
  140. if (g_cdplay) delete g_cdplay; g_cdplay = windacPlayer = NULL;
  141. //try MCI
  142. if (!mciPlayer) mciPlayer = new MciPlay;
  143. g_cdplay = mciPlayer;
  144. int ret = (g_cdplay ? g_cdplay->play(playDriveLetter, track) : 1);
  145. if (ret != 0)
  146. {
  147. //no luck
  148. if (g_cdplay) delete g_cdplay; g_cdplay = mciPlayer = NULL;
  149. return ret;
  150. }
  151. }
  152. }
  153. paused = 0;
  154. return 0;
  155. }
  156. void pause()
  157. {
  158. if (g_cdplay) g_cdplay->pause();
  159. paused = 1;
  160. }
  161. void unpause()
  162. {
  163. if (g_cdplay) g_cdplay->unpause();
  164. paused = 0;
  165. }
  166. int ispaused()
  167. {
  168. return paused;
  169. }
  170. void stop()
  171. {
  172. if (g_cdplay)
  173. {
  174. g_cdplay->stop();
  175. g_cdplay = NULL;
  176. }
  177. done = 0;
  178. line.SAVSADeInit();
  179. }
  180. int getlength()
  181. {
  182. if (g_cdplay) return g_cdplay->getlength();
  183. return -1000;
  184. }
  185. int getoutputtime()
  186. {
  187. if (g_cdplay) return g_cdplay->getoutputtime();
  188. return 0;
  189. //return audioGetPos();
  190. }
  191. void setoutputtime(int time_in_ms)
  192. {
  193. if (g_cdplay) g_cdplay->setoutputtime(time_in_ms);
  194. }
  195. void setvolume(int volume)
  196. {
  197. if (volume != -666)
  198. {
  199. a_v = volume;
  200. }
  201. if (g_cdplay) g_cdplay->setvolume(a_v, a_p);
  202. }
  203. void setpan(int pan)
  204. {
  205. a_p = pan;
  206. if (g_cdplay) g_cdplay->setvolume(a_v, a_p);
  207. }
  208. int infoDlg(const in_char *fn, HWND hwnd)
  209. {
  210. return 0;
  211. #if 0 // switched to unified file info dialog in 5.53
  212. if (!_stricmp(extension(fn), "cda") || !_strnicmp(fn, "cda://", 6))
  213. {
  214. if (!g_ps_inuse)
  215. {
  216. char device;
  217. int res=1;
  218. MCIDEVICEID d = 0;
  219. g_ps_inuse = 1;
  220. device = fn[_strnicmp(fn, "cda://", 6) ? 0 : 6];
  221. if (device >= 'a' && device <= 'z') device += 'A' -'a';
  222. CDOpen(&d, device, L"infoDlg");
  223. memset(&g_ps, 0, sizeof(g_ps));
  224. res = GetDiscID(d, &g_ps);
  225. CDClose(&d);
  226. if (!res)
  227. res = GetCDDBInfo(&g_ps, device);
  228. //if (!res) DBEdit(&g_ps, hwnd, 0, device);
  229. //if (!res)
  230. {
  231. if (CDEdit(device, &g_ps, hwnd))
  232. {
  233. g_ps_inuse = 0;
  234. return INFOBOX_EDITED;
  235. }
  236. }
  237. g_ps_inuse = 0;
  238. }
  239. }
  240. return INFOBOX_UNCHANGED;
  241. #endif
  242. }
  243. void getfileinfo(const in_char *filename, in_char *title, int *length_in_ms)
  244. {
  245. #if 0
  246. int track;
  247. char device;
  248. MCIDEVICEID dev2 = 0;
  249. if (length_in_ms) *length_in_ms = -1000;
  250. if (!filename || !*filename) // currently playing
  251. {
  252. if (!_stricmp(extension(lastfn), "cda") || !_strnicmp(lastfn, "cda://", 6))
  253. {
  254. #ifdef IGNORE_API_GRACENOTE
  255. if (title)
  256. {
  257. lstrcpynA(title, "CD Track", GETFILEINFO_TITLE_LENGTH);
  258. if (!g_ps_inuse)
  259. {
  260. g_ps_inuse = 1;
  261. memset(&g_ps, 0, sizeof(g_ps));
  262. if (CDOpen(&dev2, playDriveLetter, L"getfileinfo"))
  263. {
  264. wchar_t wtitle[256] = {0};
  265. int ret = GetDiscID(dev2, &g_ps);
  266. CDClose(&dev2);
  267. if (!ret && GetCDDBInfo(&g_ps, 0)) // TODO: get device letter
  268. PostMessage(line.hMainWindow, WM_USER, (WPARAM) L"cda://", 247/*IPC_REFRESHPLCACHE*/);
  269. if (wtitle[0])
  270. lstrcpynA(title, AutoChar(wtitle), GETFILEINFO_TITLE_LENGTH);
  271. }
  272. g_ps_inuse = 0;
  273. }
  274. }
  275. if (length_in_ms) *length_in_ms = g_playlength;
  276. #endif
  277. }
  278. return ;
  279. }
  280. if (title)
  281. {
  282. const char *p = filename + strlen(filename);
  283. while (p >= filename && *p != '\\') p--;
  284. lstrcpynA(title, ++p, GETFILEINFO_TITLE_LENGTH);
  285. }
  286. track = 0;
  287. if (!_strnicmp(filename, "cda://", 6)) // determine length of cd track via MCI
  288. {
  289. track = atoi(filename + 8);
  290. device = filename[6];
  291. if (device >= 'a' && device <= 'z') device += 'A' -'a';
  292. if (length_in_ms)
  293. {
  294. if (CDOpen(&dev2, device, L"getfileinfo"))
  295. {
  296. MCI_SET_PARMS sMCISet;
  297. sMCISet.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
  298. MCISendCommand(dev2, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID) &sMCISet);
  299. *length_in_ms = CDGetTrackLength(dev2, track);
  300. sMCISet.dwTimeFormat = MCI_FORMAT_TMSF;
  301. MCISendCommand(dev2, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPVOID) &sMCISet);
  302. }
  303. }
  304. }
  305. else // determine from RIFF structure of .CDA
  306. {
  307. HMMIO hmmio;
  308. hmmio = mmioOpenA((char *)filename, NULL, MMIO_READ | MMIO_ALLOCBUF);
  309. device = filename[0];
  310. if (device >= 'a' && device <= 'z') device += 'A' -'a';
  311. if (hmmio)
  312. {
  313. MMCKINFO mmckinfoParent; // parent chunk information
  314. mmckinfoParent.fccType = mmioFOURCC('C', 'D', 'D', 'A');
  315. if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL, MMIO_FINDRIFF) == MMSYSERR_NOERROR)
  316. {
  317. MMCKINFO mmckinfoSubchunk; // subchunk information structure
  318. mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
  319. if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK) == MMSYSERR_NOERROR)
  320. {
  321. char *format;
  322. DWORD dwFmtSize; // size of "FMT" chunk
  323. dwFmtSize = mmckinfoSubchunk.cksize;
  324. format = (char *) GlobalAlloc(GPTR, dwFmtSize);
  325. if (mmioRead(hmmio, (HPSTR) format, dwFmtSize) == (int)dwFmtSize)
  326. {
  327. mmioAscend(hmmio, &mmckinfoSubchunk, 0);
  328. track = *((short int *)format + 1);
  329. if (length_in_ms)
  330. {
  331. int length = *((int *)format + 3);
  332. int l = length % 75;
  333. length /= 75;
  334. length *= 1000;
  335. length += (l * 1000) / 75;
  336. *length_in_ms = length;
  337. }
  338. }
  339. GlobalFree(format);
  340. }
  341. }
  342. mmioClose(hmmio, 0);
  343. }
  344. }
  345. #ifdef IGNORE_API_GRACENOTE
  346. if (title && track)
  347. {
  348. if (0 && !g_ps_inuse)
  349. {
  350. g_ps_inuse = 1;
  351. memset(&g_ps, 0, sizeof(g_ps));
  352. if (!dev2)
  353. {
  354. CDOpen(&dev2, device, L"getfileinfo");
  355. }
  356. if (dev2)
  357. {
  358. StringCchPrintfA(title, GETFILEINFO_TITLE_LENGTH, "CD Track %d", track);
  359. wchar_t wtitle[256] = L"";
  360. int ret = GetDiscID(dev2, &g_ps);
  361. CDClose(&dev2);
  362. dev2=0;
  363. if (!ret && GetCDDBInfo(&g_ps, device))
  364. PostMessage(line.hMainWindow, WM_USER, (WPARAM) L"cda://", 247 /*IPC_REFRESHPLCACHE*/);
  365. if (wtitle[0])
  366. lstrcpynA(title, AutoChar(wtitle), GETFILEINFO_TITLE_LENGTH);
  367. }
  368. g_ps_inuse = 0;
  369. }
  370. }
  371. #endif
  372. if (dev2) CDClose(&dev2);
  373. #endif
  374. }
  375. void eq_set(int on, char data[10], int preamp)
  376. {}
  377. In_Module line =
  378. {
  379. IN_VER_RET,
  380. "nullsoft(in_cdda.dll)",
  381. 0, // hMainWindow
  382. 0, // hDllInstance
  383. 0,
  384. 0, // is_seekable
  385. 1, // uses output plugins
  386. about,//config,
  387. about,
  388. init,
  389. quit,
  390. getfileinfo,
  391. infoDlg,
  392. isourfile,
  393. play,
  394. pause,
  395. unpause,
  396. ispaused,
  397. stop,
  398. getlength,
  399. getoutputtime,
  400. setoutputtime,
  401. setvolume,
  402. setpan,
  403. 0, 0, 0, 0, 0, 0, 0, 0, 0,
  404. 0, 0, // dsp shit
  405. eq_set,
  406. NULL, // setinfo
  407. NULL // out_mod
  408. };
  409. int m_nblock = 0;
  410. extern "C"
  411. {
  412. __declspec(dllexport) In_Module * winampGetInModule2()
  413. {
  414. s_last_error = NULL;
  415. return &line;
  416. }
  417. #if 0 // TODO?
  418. __declspec(dllexport) int winampWriteExtendedFileInfo()
  419. {
  420. s_last_error = NULL;
  421. // write it out
  422. if (m_eiw_lastdrive)
  423. {
  424. AddToDatabase(&setInfo);
  425. m_eiw_lastdrive = 0;
  426. return 1;
  427. }
  428. return 0;
  429. }
  430. #endif
  431. };
  432. // wasabi based services for localisation support
  433. api_language *WASABI_API_LNG = 0;
  434. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  435. #ifndef IGNORE_API_GRACENOTE
  436. api_gracenote *AGAVE_API_GRACENOTE = 0;
  437. #endif
  438. api_application *WASABI_API_APP = 0;
  439. void SetFileExtensions(void)
  440. {
  441. static char fileExtensionsString[1200] = {0}; // "CDA\0CDDA Audio Tracks (*.CDA)\0"
  442. char* end = 0;
  443. StringCchCopyExA(fileExtensionsString, 1200, "CDA", &end, 0, 0);
  444. StringCchCopyExA(end+1, 1200, WASABI_API_LNGSTRING(IDS_CDDA_AUDIO_TRACKS), 0, 0, 0);
  445. line.FileExtensions = fileExtensionsString;
  446. }
  447. int init()
  448. {
  449. if (!IsWindow(line.hMainWindow))
  450. return IN_INIT_FAILURE;
  451. //CoInitialize(0);
  452. #ifndef IGNORE_API_GRACENOTE
  453. Cddb_Initialize();
  454. InitializeCddbCache();
  455. #endif
  456. // loader so that we can get the localisation service api for use
  457. waServiceFactory *sf = line.service->service_getServiceByGuid(languageApiGUID);
  458. if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
  459. sf = line.service->service_getServiceByGuid(AgaveConfigGUID);
  460. if (sf) AGAVE_API_CONFIG = reinterpret_cast<api_config*>(sf->getInterface());
  461. #ifndef IGNORE_API_GRACENOTE
  462. sf = line.service->service_getServiceByGuid(gracenoteApiGUID);
  463. if (sf) AGAVE_API_GRACENOTE = reinterpret_cast<api_gracenote*>(sf->getInterface());
  464. #endif
  465. sf = line.service->service_getServiceByGuid(applicationApiServiceGuid);
  466. if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
  467. // need to have this initialised before we try to do anything with localisation features
  468. WASABI_API_START_LANG(line.hDllInstance,InCDDALangGUID);
  469. static wchar_t szDescription[256];
  470. StringCchPrintfW(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_CD_PLUGIN),PLUGIN_VERSION);
  471. line.description = (char*)szDescription;
  472. SetFileExtensions();
  473. playDeviceGuard = new Nullsoft::Utility::LockGuard;
  474. playStatusGuard = new Nullsoft::Utility::LockGuard;
  475. MainThreadId = GetCurrentThreadId();
  476. config_read();
  477. return IN_INIT_SUCCESS;
  478. }
  479. void quit()
  480. {
  481. #ifndef IGNORE_API_GRACENOTE
  482. ShutdownMusicIDWorkOrder();
  483. #endif
  484. if (playStatusGuard)
  485. {
  486. delete playStatusGuard;
  487. playStatusGuard = 0;
  488. }
  489. if (windacPlayer)
  490. {
  491. delete windacPlayer;
  492. windacPlayer = NULL;
  493. }
  494. if (daePlayer)
  495. {
  496. delete daePlayer;
  497. daePlayer = NULL;
  498. }
  499. if (mciPlayer)
  500. {
  501. delete mciPlayer;
  502. mciPlayer = NULL;
  503. }
  504. #ifndef IGNORE_API_GRACENOTE
  505. ShutDownCDDB();
  506. #endif
  507. waServiceFactory *sf = line.service->service_getServiceByGuid(languageApiGUID);
  508. if (sf) sf->releaseInterface(WASABI_API_LNG);
  509. sf = line.service->service_getServiceByGuid(AgaveConfigGUID);
  510. if (sf) sf->releaseInterface(AGAVE_API_CONFIG);
  511. #ifndef IGNORE_API_GRACENOTE
  512. sf = line.service->service_getServiceByGuid(gracenoteApiGUID);
  513. if (sf) sf->releaseInterface(AGAVE_API_GRACENOTE);
  514. Cddb_Uninitialize();
  515. UninitializeCddbCache();
  516. #endif
  517. CloseTables();
  518. //CoUninitialize();
  519. }