Main.c 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067
  1. #include "api.h"
  2. extern "C" {
  3. #include "main.h"
  4. }
  5. #include "log.h"
  6. #include "../../winamp/wa_ipc.h"
  7. #include "../nu/AutoWide.h"
  8. #include "../nu/AutoCharFn.h"
  9. #include <shlwapi.h>
  10. #include <commdlg.h>
  11. extern "C" MMSTREAM *_mm_fopen_rf(const CHAR *fname); //rf_wrapper.c
  12. //
  13. // data types and stuff
  14. //
  15. #define SU_POSITION 1
  16. #define SU_TIME 2
  17. #define PPF_CONT_LOOP 1
  18. #define PPF_LOOPALL 2
  19. #define PPF_ADD_TITLE 4
  20. typedef struct
  21. {
  22. const char *cmd;
  23. const char *file;
  24. const char *title;
  25. int titleLength;
  26. int start;
  27. int startUnit;
  28. int loops;
  29. int flags;
  30. } PlayParams;
  31. // Public Globals!
  32. // ---------------
  33. extern "C"
  34. {
  35. UNIMOD *mf;
  36. MPLAYER *mp;
  37. int paused;
  38. int decode_pos; // in 1/64th of millisecond
  39. extern char cfg_format[];
  40. }
  41. void infobox_setmodule(HWND hwnd);
  42. // Static Globals!
  43. // ---------------
  44. #define SILENCE_THRESHOLD 10800
  45. extern "C" int GetSampleSizeFlag();
  46. static char ERROR_TITLE[64];
  47. static int is_tempfile = 0;
  48. static char cmdName[2048], saveName[MAX_PATH];
  49. static char songTitle[400]; // as in Winamp
  50. static PlayParams currParams;
  51. static HANDLE thread_handle = INVALID_HANDLE_VALUE;
  52. static volatile int killDecodeThread;
  53. static volatile int seek_needed;
  54. // wasabi based services for localisation support
  55. api_application *WASABI_API_APP = 0;
  56. api_language *WASABI_API_LNG = 0;
  57. HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
  58. extern "C" DWORD WINAPI decodeThread(void *b);
  59. void __cdecl setoutputtime(int time_in_ms);
  60. // =====================================================================================
  61. // error handling shiz
  62. // =====================================================================================
  63. static int lastError = 0;
  64. __inline void mm_clearerror() { lastError = 0; }
  65. static void mmerr(int crap, const CHAR *crud)
  66. {
  67. char tmp[128] = {0};
  68. if (lastError==crap || crap==MMERR_OPENING_FILE)
  69. return;
  70. else
  71. {
  72. if(!lstrcmpi(crud,"Corrupt file or unsupported module type."))
  73. {
  74. WASABI_API_LNGSTRING_BUF(IDS_CORRUPT_UNSUPPORTED_TYPE,tmp,128);
  75. }
  76. else
  77. tmp[0] = 0;
  78. }
  79. MessageBox(mikmod.hMainWindow, (tmp[0]?tmp:crud), ERROR_TITLE, MB_ICONERROR);
  80. lastError = crap;
  81. }
  82. // =====================================================================================
  83. static int __cdecl init(void)
  84. // =====================================================================================
  85. {
  86. if (!IsWindow(mikmod.hMainWindow))
  87. return IN_INIT_FAILURE;
  88. waServiceFactory *sf = mikmod.service->service_getServiceByGuid(languageApiGUID);
  89. if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
  90. sf = mikmod.service->service_getServiceByGuid(applicationApiServiceGuid);
  91. if (sf) WASABI_API_APP = reinterpret_cast<api_application*>(sf->getInterface());
  92. // need to have this initialised before we try to do anything with localisation features
  93. WASABI_API_START_LANG(mikmod.hDllInstance,InModLangGUID);
  94. static wchar_t szDescription[256];
  95. swprintf(szDescription,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MODULE_DECODER),PLUGIN_VER);
  96. mikmod.description = (char*)szDescription;
  97. WASABI_API_LNGSTRING_BUF(IDS_MOD_PLUGIN_ERROR,ERROR_TITLE,64);
  98. _mmerr_sethandler(&mmerr);
  99. config_read();
  100. Mikmod_RegisterAllLoaders();
  101. Mikmod_RegisterDriver(drv_amp);
  102. //Mikmod_RegisterDriver(drv_buffer);
  103. return IN_INIT_SUCCESS;
  104. }
  105. // =====================================================================================
  106. static void __cdecl quit()
  107. // =====================================================================================
  108. {
  109. //LOG log_exit();
  110. SL_Cleanup();
  111. }
  112. static MDRIVER *md;
  113. // =====================================================================================
  114. // file open shiz
  115. // =====================================================================================
  116. #define IPC_GETHTTPGETTER 240
  117. __inline char *GetFileName(const char *fullname)
  118. {
  119. const char *c = fullname + strlen(fullname) - 1;
  120. while (c > fullname)
  121. {
  122. if (*c=='\\' || *c=='/')
  123. {
  124. c++;
  125. break;
  126. }
  127. c--;
  128. }
  129. return (char*)c;
  130. }
  131. char* BuildFilterString(void)
  132. {
  133. static char filterStr[128] = {0};
  134. if(!filterStr[0])
  135. {
  136. char* temp = filterStr;
  137. WASABI_API_LNGSTRING_BUF(IDS_ALL_FILES,filterStr,128);
  138. temp += lstrlen(filterStr)+1;
  139. lstrcpy(temp, "*.*");
  140. *(temp = temp + lstrlen(temp) + 1) = 0;
  141. }
  142. return filterStr;
  143. }
  144. BOOL GetPlayParams(const char *fileName, BOOL open, PlayParams *params)
  145. {
  146. mm_clearerror();
  147. // fill params
  148. params->cmd = fileName;
  149. params->start = 0;
  150. params->loops = config_loopcount;
  151. params->titleLength = 0;
  152. params->flags = 0;
  153. if (config_playflag & CPLAYFLG_CONT_LOOP)
  154. params->flags |= PPF_CONT_LOOP;
  155. if (config_playflag & CPLAYFLG_LOOPALL)
  156. params->flags |= PPF_LOOPALL;
  157. if (params->loops == -1)
  158. params->flags &= ~PPF_CONT_LOOP;
  159. // check for mod:// prefix
  160. if (!strncmp(fileName, "mod://", 6))
  161. {
  162. const char *c = fileName += 6;
  163. while (c && *c && *c!=':')
  164. {
  165. // jump to
  166. if (!strncmp(c, "jmp=", 4))
  167. {
  168. // jump units
  169. switch (*(c + 4))
  170. {
  171. // position
  172. case 'p':
  173. params->startUnit = SU_POSITION;
  174. params->flags &= ~PPF_CONT_LOOP;
  175. break;
  176. // time
  177. case 't':
  178. params->startUnit = SU_TIME;
  179. break;
  180. // invalid
  181. default:
  182. return FALSE;
  183. }
  184. params->start = atoi(c + 5);
  185. }
  186. // loops
  187. else if (!strncmp(c, "lop=", 4))
  188. {
  189. if (*(c+4) == 'u')
  190. {
  191. params->flags |= PPF_LOOPALL;
  192. c++;
  193. }
  194. params->loops = atoi(c + 4);
  195. params->loops = _mm_boundscheck(params->loops, -1, 64);
  196. if (params->loops == -1)
  197. params->flags &= ~PPF_CONT_LOOP;
  198. }
  199. // continue after loop
  200. else if (!strncmp(c, "con=", 4))
  201. {
  202. if (atoi(c + 4))
  203. params->flags |= PPF_CONT_LOOP;
  204. else params->flags &= ~PPF_CONT_LOOP;
  205. }
  206. // title
  207. else if (!strncmp(c, "tit=", 4))
  208. {
  209. // find string
  210. const char *p = c + 4;
  211. if (*p == '+')
  212. {
  213. params->flags |= PPF_ADD_TITLE;
  214. c++;
  215. p++;
  216. }
  217. if (*p++ != '"') return FALSE;
  218. while (p && *p && *p!='"')
  219. p++;
  220. if (*p != '"') return FALSE;
  221. // set
  222. params->title = c + 5;
  223. params->titleLength = p - c - 5;
  224. c = p - 3;
  225. }
  226. // invalid
  227. else return FALSE;
  228. // skip
  229. c += 4;
  230. while (c && *c && *c!=',' && *c!=':')
  231. c++;
  232. if (*c == ',') c++;
  233. }
  234. if (!*c) return FALSE;
  235. fileName = c + 1;
  236. }
  237. params->file = fileName;
  238. // check for URLs
  239. if (open)
  240. {
  241. saveName[0] = 0;
  242. is_tempfile = 0;
  243. if (!_strnicmp(fileName, "http://", 7) || !_strnicmp(fileName, "https://", 8) ||
  244. !_strnicmp(fileName, "ftp://", 6)) // FTP is now currently supported, but still...
  245. {
  246. typedef int (__cdecl *HttpRetrieveFile)(HWND hwnd, const char *url, const char *file, const char *dlgtitle);
  247. HttpRetrieveFile fileGetter;
  248. int t = SendMessage(mikmod.hMainWindow,WM_USER,0,IPC_GETHTTPGETTER);
  249. // try to get httpGetter
  250. if (!t || t==1)
  251. {
  252. MessageBox(mikmod.hMainWindow,
  253. WASABI_API_LNGSTRING(IDS_URLS_ONLY_SUPPORTED_IN_2_10_PLUS),
  254. ERROR_TITLE, MB_ICONERROR);
  255. return FALSE;
  256. }
  257. fileGetter = (HttpRetrieveFile)t;
  258. // save stream if required
  259. if (config_savestr)
  260. {
  261. OPENFILENAME l = {0};
  262. lstrcpyn(saveName, GetFileName(fileName), MAX_PATH);
  263. l.lStructSize = sizeof(l);
  264. l.hwndOwner = mikmod.hMainWindow;
  265. l.hInstance = NULL;
  266. l.lpstrFilter = BuildFilterString();
  267. l.lpstrCustomFilter = NULL;
  268. l.nMaxCustFilter = 0;
  269. l.nFilterIndex = 0;
  270. l.lpstrFile = saveName;
  271. l.nMaxFile = sizeof(saveName);
  272. l.lpstrFileTitle = 0;;
  273. l.nMaxFileTitle = 0;
  274. l.lpstrInitialDir = NULL;
  275. l.lpstrTitle = WASABI_API_LNGSTRING(IDS_SAVE_MODULE);
  276. l.lpstrDefExt = "mod";
  277. l.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_OVERWRITEPROMPT;
  278. if (!GetSaveFileName(&l))
  279. saveName[0] = 0;
  280. }
  281. // generate temp name, if not saving
  282. if (!saveName[0])
  283. {
  284. char p[MAX_PATH] = {0};
  285. GetTempPath(sizeof(p), p);
  286. GetTempFileName(p, "mod", 0, saveName);
  287. is_tempfile = 1;
  288. }
  289. // get file
  290. if (fileGetter(mikmod.hMainWindow, fileName, saveName, WASABI_API_LNGSTRING(IDS_RETRIEVING_MODULE)))
  291. {
  292. is_tempfile = 0;
  293. saveName[0] = 0;
  294. return FALSE;
  295. }
  296. params->file = saveName;
  297. }
  298. }
  299. else
  300. {
  301. if (saveName[0] && !_stricmp(fileName, cmdName))
  302. params->file = saveName;
  303. }
  304. return TRUE;
  305. }
  306. static void CleanupTemp()
  307. {
  308. if (is_tempfile && saveName[0])
  309. {
  310. DeleteFile(saveName);
  311. is_tempfile = 0;
  312. }
  313. saveName[0] = 0;
  314. }
  315. BOOL InitPlayer(UNIMOD *mf, MPLAYER **ps, const PlayParams *params, BOOL quick)
  316. {
  317. int flags;
  318. // strip silence
  319. if (config_playflag & CPLAYFLG_STRIPSILENCE)
  320. Unimod_StripSilence(mf, SILENCE_THRESHOLD);
  321. // set flags
  322. flags = PF_TIMESEEK;
  323. if (params->flags & PPF_CONT_LOOP) flags |= PF_CONT_LOOP;
  324. // init player
  325. if (quick)
  326. *ps = Player_Create(mf, flags);
  327. else *ps = Player_InitSong(mf, NULL, flags, config_voices);
  328. if (!*ps) return FALSE;
  329. // position seek
  330. if (params->start && params->startUnit==SU_POSITION)
  331. Player_SetStartPosition(*ps, params->start);
  332. // looping
  333. Player_SetLoopStatus(*ps, params->flags & PPF_LOOPALL, params->loops);
  334. if (quick || config_playflag&CPLAYFLG_SEEKBYORDERS)
  335. Player_PredictSongLength(*ps);
  336. else
  337. {
  338. // time calculation & seeking-lookups creation
  339. Player_BuildQuickLookups(*ps);
  340. // fade (needs results of Player_BuildQuickLookups)
  341. if (config_playflag & CPLAYFLG_FADEOUT)
  342. Player_VolumeFadeEx(*ps, MP_VOLUME_CUR, 0, config_fadeout, MP_SEEK_END, config_fadeout);
  343. }
  344. // remember song length
  345. mf->songlen = (*ps)->songlen;
  346. return TRUE;
  347. }
  348. static UNIMOD *GetModuleInfo(const PlayParams *params)
  349. {
  350. UNIMOD *m = mf;
  351. // check against the current one
  352. if (!m || _stricmp(cmdName, params->cmd)) // check the whole string, not just file name
  353. {
  354. MPLAYER *ps;
  355. MMSTREAM * fp;
  356. // load module
  357. mm_clearerror();
  358. fp = _mm_fopen_rf(params->file);
  359. if (!fp) return NULL;
  360. m = Unimod_LoadInfo_FP(params->file,fp);
  361. _mm_fclose(fp);
  362. if (!m) return NULL;
  363. // get info and clean up
  364. if (!InitPlayer(m, &ps, params, TRUE))
  365. {
  366. Unimod_Free(m);
  367. return NULL;
  368. }
  369. Player_Free(ps);
  370. }
  371. return m;
  372. }
  373. static int __cdecl isourfile(const char *fn)
  374. {
  375. return !_strnicmp(fn, "mod://", 6);
  376. }
  377. // =====================================================================================
  378. // helpers
  379. // =====================================================================================
  380. static UNIMOD *FindInfoBox(const char *fileName, HWND *hwnd)
  381. {
  382. INFOBOX *cruise;
  383. for (cruise=infobox_list; cruise; cruise=cruise->next)
  384. if (!_stricmp(cruise->dlg.module->filename, fileName))
  385. {
  386. if (hwnd) *hwnd = cruise->hwnd;
  387. return cruise->dlg.module;
  388. }
  389. return NULL;
  390. }
  391. static BOOL FindInfoBoxPtr(const UNIMOD *mf)
  392. {
  393. INFOBOX *cruise;
  394. for (cruise=infobox_list; cruise; cruise=cruise->next)
  395. if (cruise->dlg.module == mf)
  396. return TRUE;
  397. return FALSE;
  398. }
  399. // =====================================================================================
  400. static int __cdecl play(const char *fileName)
  401. // =====================================================================================
  402. {
  403. PlayParams params;
  404. uint md_mode = 0;
  405. // parse parameters
  406. if (!GetPlayParams(fileName, TRUE, &params))
  407. return 1;
  408. // save strings locally
  409. lstrcpyn(cmdName, params.cmd, 2048);
  410. if (params.titleLength)
  411. lstrcpyn(songTitle, params.title, min(params.titleLength+1, sizeof(songTitle)));
  412. else songTitle[0] = 0;
  413. // save current values
  414. currParams = params;
  415. currParams.cmd = params.cmd;
  416. currParams.title = songTitle;
  417. // Initialize MDRVER
  418. // -----------------
  419. if (config_interp & 1) md_mode |= DMODE_INTERP;
  420. if (config_interp & 2) md_mode |= DMODE_NOCLICK;
  421. if (config_interp & 4) md_mode |= DMODE_FIR;
  422. md_mode |= GetSampleSizeFlag();
  423. if (AllowSurround()) md_mode |= DMODE_SURROUND;
  424. if (config_panrev) md_mode |= DMODE_REVERSE;
  425. if (config_resonance) md_mode |= DMODE_RESONANCE;
  426. md = Mikmod_Init(config_srate, 1000, NULL, GetNumChannels()==1 ? MD_MONO : MD_STEREO, config_cpu, md_mode, &drv_amp);
  427. if (!md)
  428. {
  429. CleanupTemp();
  430. return 1;
  431. }
  432. md->pansep = config_pansep;
  433. // Register non-interpolation mixers
  434. // ---------------------------------
  435. // if the user has disabled interpolation...
  436. if(!(config_interp & 1))
  437. {
  438. VC_RegisterMixer(md->device.vc, &RF_M8_MONO);
  439. VC_RegisterMixer(md->device.vc, &RF_M16_MONO);
  440. VC_RegisterMixer(md->device.vc, &RF_M8_STEREO);
  441. VC_RegisterMixer(md->device.vc, &RF_M16_STEREO);
  442. VC_RegisterMixer(md->device.vc, &M8_MONO);
  443. VC_RegisterMixer(md->device.vc, &M16_MONO);
  444. VC_RegisterMixer(md->device.vc, &M8_STEREO);
  445. VC_RegisterMixer(md->device.vc, &M16_STEREO);
  446. }
  447. else if (config_interp&4)
  448. {
  449. /*
  450. VC_RegisterMixerHack(md->device.vc, &M16_MONO_CUBIC);
  451. VC_RegisterMixerHack(md->device.vc, &M16_STEREO_CUBIC);
  452. VC_RegisterMixerHack(md->device.vc, &M8_MONO_CUBIC);
  453. VC_RegisterMixerHack(md->device.vc, &M8_STEREO_CUBIC);
  454. */
  455. VC_RegisterMixerHack(md->device.vc, &M16_MONO_FIR);
  456. VC_RegisterMixerHack(md->device.vc, &M16_STEREO_FIR);
  457. VC_RegisterMixerHack(md->device.vc, &M8_MONO_FIR);
  458. VC_RegisterMixerHack(md->device.vc, &M8_STEREO_FIR);
  459. }
  460. // LOADING THE SONG
  461. // ----------------
  462. // Check through the list of active info boxes for a matching filename. If found,
  463. // then we use the already-loaded module information instead!
  464. {
  465. HWND hwnd;
  466. if ((mf=FindInfoBox(params.file, &hwnd)) != NULL)
  467. {
  468. MMSTREAM *smpfp;
  469. // prepare for reloading
  470. info_killseeker(hwnd);
  471. // reload samples
  472. smpfp = _mm_fopen_rf(params.file);
  473. Unimod_LoadSamples(mf, md, smpfp);
  474. _mm_fclose(smpfp);
  475. }
  476. // not already loaded
  477. else
  478. {
  479. MMSTREAM *fp;
  480. fp = _mm_fopen_rf(params.file);
  481. if (!fp)
  482. {
  483. Mikmod_Exit(md);
  484. CleanupTemp();
  485. return -1;
  486. }
  487. //MMEXPORT UNIMOD *Unimod_LoadFP(MDRIVER *md, MMSTREAM *modfp, MMSTREAM *smpfp, int mode);
  488. //MMEXPORT UNIMOD *Unimod_Load(MDRIVER *md, const CHAR *filename);
  489. mf=Unimod_Load_FP(md, params.file,fp);
  490. _mm_fclose(fp);
  491. if (mf==NULL)
  492. {
  493. Mikmod_Exit(md);
  494. CleanupTemp();
  495. return -1;
  496. }
  497. }
  498. }
  499. // file name is stored in module now
  500. if (!saveName[0])
  501. params.file = mf->filename;
  502. // init player
  503. if (!InitPlayer(mf, &mp, &params, FALSE))
  504. {
  505. CleanupTemp();
  506. return -1;
  507. }
  508. Player_Start(mp);
  509. // set start time
  510. seek_needed = -1;
  511. decode_pos = 0;
  512. if (params.start && params.startUnit==SU_TIME)
  513. setoutputtime(params.start*1000);
  514. // init output & info
  515. mikmod.outMod->SetVolume(-666);
  516. mikmod.SetInfo(MulDiv(mf->filesize, 8, mf->songlen), config_srate/1000, GetNumChannels(), 1);
  517. // init decoding thread
  518. {
  519. DWORD threadid;
  520. killDecodeThread = 0;
  521. paused = 0;
  522. thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)decodeThread, NULL, 0, &threadid);
  523. set_priority();
  524. }
  525. return 0;
  526. }
  527. // =====================================================================================
  528. static void __cdecl stop(void)
  529. // =====================================================================================
  530. {
  531. if (thread_handle != INVALID_HANDLE_VALUE)
  532. {
  533. killDecodeThread = 1;
  534. if (WaitForSingleObject(thread_handle, 2000) == WAIT_TIMEOUT)
  535. {
  536. MessageBox(mikmod.hMainWindow,
  537. WASABI_API_LNGSTRING(IDS_ERROR_KILLING_DECODING_THREAD),
  538. ERROR_TITLE, MB_ICONWARNING);
  539. TerminateThread(thread_handle, 0);
  540. }
  541. CloseHandle(thread_handle);
  542. thread_handle = INVALID_HANDLE_VALUE;
  543. CleanupTemp();
  544. }
  545. Player_Free(mp);
  546. mp = NULL;
  547. // We need to see if mf is in use. If so, then we can't unload it.
  548. // Bute we *do* have to unload its samples, because those are not needed.
  549. if (FindInfoBoxPtr(mf))
  550. Unimod_UnloadSamples(mf);
  551. else Unimod_Free(mf);
  552. mf = NULL;
  553. Mikmod_Exit(md); md = NULL;
  554. mikmod.SAVSADeInit();
  555. }
  556. // =====================================================================================
  557. // pausing stuff
  558. // =====================================================================================
  559. static void __cdecl pause(void) { paused=1; mikmod.outMod->Pause(1); }
  560. static void __cdecl unpause(void) { paused=0; mikmod.outMod->Pause(0); }
  561. static int __cdecl ispaused(void) { return paused; }
  562. // =====================================================================================
  563. // seeking/timing related stuff
  564. // =====================================================================================
  565. static int __cdecl getlength(void)
  566. {
  567. if (mp)
  568. {
  569. if (!(config_playflag & CPLAYFLG_SEEKBYORDERS))
  570. return mp->songlen;
  571. else return mf->numpos * 1000;
  572. }
  573. else return 0;
  574. }
  575. static int __cdecl getoutputtime(void)
  576. {
  577. if (!(config_playflag & CPLAYFLG_SEEKBYORDERS))
  578. return decode_pos/64 + (mikmod.outMod->GetOutputTime() - mikmod.outMod->GetWrittenTime());
  579. else return mp ? mp->state.sngpos * 1000 : 0;
  580. }
  581. static void __cdecl setoutputtime(int time_in_ms)
  582. {
  583. seek_needed = time_in_ms;
  584. }
  585. // =====================================================================================
  586. static int __cdecl infobox(const char *fileName, HWND hwnd)
  587. // =====================================================================================
  588. {
  589. PlayParams params;
  590. // parse params
  591. if (!GetPlayParams(fileName, FALSE, &params))
  592. return 1;
  593. // First we check our array of loaded dialog boxes. If there are any filename matches,
  594. // then we just bring that window to the foreground!
  595. if (FindInfoBox(params.file, &hwnd) != NULL)
  596. {
  597. SetForegroundWindow(hwnd);
  598. return 0;
  599. }
  600. infoDlg(hwnd, GetModuleInfo(&params), TRUE, TRUE);
  601. return 0;
  602. }
  603. /*extern "C" __declspec(dllexport) int winampGetExtendedFileInfo(const char *fn, const char *data, char *dest, int destlen)
  604. {
  605. UNIMOD *m=0;
  606. PlayParams params;
  607. const char *ret=0;
  608. if (!_stricmp(data,"TYPE"))
  609. {
  610. dest[0] = '0';
  611. dest[1] = 0x00;
  612. return 1;
  613. }
  614. if (!_stricmp(data,"FAMILY"))
  615. {
  616. LPCTSTR e;
  617. e = PathFindExtension(fn);
  618. if (L'.' != *e) return 0;
  619. e++;
  620. return GetTypeInfo(e, dest, destlen);
  621. }
  622. if (!GetPlayParams(fn, FALSE, &params))
  623. return 0;
  624. m=GetModuleInfo(&params);
  625. if (!m)
  626. return 0;
  627. if (!_stricmp(data,"TITLE"))
  628. {
  629. if (!params.titleLength || params.flags&PPF_ADD_TITLE)
  630. ret = m->songname;
  631. else
  632. ret=params.title;
  633. }
  634. else if (!_stricmp(data,"PART"))
  635. {
  636. if (params.titleLength && params.flags&PPF_ADD_TITLE)
  637. ret=params.title;
  638. }
  639. else if (!_stricmp(data,"ARTIST") )
  640. ret=m->composer;
  641. else if (!_stricmp(data,"COMPOSER"))
  642. ret=m->composer;
  643. else if (!_stricmp(data,"COMMENT"))
  644. ret=m->comment;
  645. else if (!_stricmp(data,"FORMAT") || !_stricmp(data,"MODTYPE"))
  646. ret=m->modtype;
  647. else if (!_stricmp(data,"LENGTH"))
  648. {
  649. _itoa(m->songlen, dest, 10);
  650. if (m!=mf) // make sure it's not the currently playing file
  651. Unimod_Free(m); // in theory this is a race condition
  652. return 1;
  653. }
  654. else
  655. {
  656. if (m!=mf) // make sure it's not the currently playing file
  657. Unimod_Free(m); // in theory this is a race condition
  658. return 0;
  659. }
  660. if (ret)
  661. lstrcpyn(dest, ret, destlen);
  662. else
  663. dest[0]=0;
  664. if (m!=mf) // make sure it's not the currently playing file
  665. Unimod_Free(m); // in theory this is a race condition
  666. return 1;
  667. }*/
  668. extern "C" __declspec(dllexport) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
  669. {
  670. UNIMOD *m=0;
  671. PlayParams params;
  672. const char *ret=0;
  673. if (!_stricmp(data,"TYPE"))
  674. {
  675. dest[0] = L'0';
  676. dest[1] = 0x00;
  677. return 1;
  678. }
  679. if (!_stricmp(data,"FAMILY"))
  680. {
  681. LPCWSTR e;
  682. e = PathFindExtensionW(fn);
  683. if (L'.' != *e) return 0;
  684. e++;
  685. return GetTypeInfo(e, dest, destlen);
  686. }
  687. if (!GetPlayParams(AutoCharFn(fn), FALSE, &params))
  688. return 0;
  689. m=GetModuleInfo(&params);
  690. if (!m)
  691. return 0;
  692. if (!_stricmp(data,"TITLE"))
  693. {
  694. if (!params.titleLength || params.flags&PPF_ADD_TITLE)
  695. ret = m->songname;
  696. else
  697. ret=params.title;
  698. }
  699. else if (!_stricmp(data,"PART"))
  700. {
  701. if (params.titleLength && params.flags&PPF_ADD_TITLE)
  702. ret=params.title;
  703. }
  704. else if (!_stricmp(data,"ARTIST") )
  705. ret=m->composer;
  706. else if (!_stricmp(data,"COMPOSER"))
  707. ret=m->composer;
  708. else if (!_stricmp(data,"COMMENT"))
  709. ret=m->comment;
  710. else if (!_stricmp(data,"FORMAT") || !_stricmp(data,"MODTYPE"))
  711. ret=m->modtype;
  712. else if (!_stricmp(data,"LENGTH"))
  713. {
  714. _itow(m->songlen, dest, 10);
  715. if (m!=mf) // make sure it's not the currently playing file
  716. Unimod_Free(m); // in theory this is a race condition
  717. return 1;
  718. }
  719. else
  720. {
  721. if (m!=mf) // make sure it's not the currently playing file
  722. Unimod_Free(m); // in theory this is a race condition
  723. return 0;
  724. }
  725. if (ret)
  726. lstrcpynW(dest, AutoWide(ret), destlen);
  727. else
  728. dest[0]=0;
  729. if (m!=mf) // make sure it's not the currently playing file
  730. Unimod_Free(m); // in theory this is a race condition
  731. return 1;
  732. }
  733. // =====================================================================================
  734. static void __cdecl getfileinfo(const char *fileName, char *title, int *length_in_ms)
  735. // =====================================================================================
  736. {
  737. PlayParams params;
  738. UNIMOD *m;
  739. BOOL unload = FALSE;
  740. // empty string stands for the current file
  741. if (fileName!=NULL && *fileName)
  742. {
  743. if (!GetPlayParams(fileName, FALSE, &params))
  744. {
  745. lstrcpyn(title, fileName, GETFILEINFO_TITLE_LENGTH);
  746. if (length_in_ms)
  747. *length_in_ms = -1;
  748. return;
  749. }
  750. }
  751. else
  752. params = currParams;
  753. // module loaded
  754. if ((m=FindInfoBox(params.file, NULL))!=NULL || (unload=1, m=GetModuleInfo(&params))!=NULL)
  755. {
  756. if (title)
  757. {
  758. if (!params.titleLength || params.flags&PPF_ADD_TITLE)
  759. lstrcpyn(title, m->songname, GETFILEINFO_TITLE_LENGTH);
  760. else
  761. lstrcpyn(title, params.title, GETFILEINFO_TITLE_LENGTH);
  762. }
  763. // set playing time
  764. if (length_in_ms)
  765. *length_in_ms = m->songlen;
  766. // clean up
  767. if (unload && m!=mf)
  768. Unimod_Free(m);
  769. }
  770. // invalid module or smth else
  771. else
  772. {
  773. lstrcpyn(title, GetFileName(params.file), GETFILEINFO_TITLE_LENGTH);
  774. if (length_in_ms)
  775. *length_in_ms = -1;
  776. }
  777. }
  778. // =====================================================================================
  779. // misc stuff
  780. // =====================================================================================
  781. static void __cdecl setvolume(int volume) { mikmod.outMod->SetVolume(volume); }
  782. static void __cdecl setpan(int pan) { mikmod.outMod->SetPan(pan); }
  783. static void __cdecl eq_set(int on, char data[10], int preamp) {}
  784. static CHAR capnstupid[4096];
  785. // =====================================================================================
  786. In_Module mikmod =
  787. // =====================================================================================
  788. {
  789. IN_VER_RET,
  790. "nullsoft(in_mod.dll)", // need to set this to some form of valid buffer otherwise in_bass crashes (why it's looking at this i don't know!!)
  791. 0, // hMainWindow
  792. 0, // hDllInstance
  793. capnstupid,
  794. 1, // is_seekable
  795. 1, // uses_output_plug
  796. config,
  797. about,
  798. init,
  799. quit,
  800. getfileinfo,
  801. infobox,
  802. isourfile,
  803. play,
  804. pause,
  805. unpause,
  806. ispaused,
  807. stop,
  808. getlength,
  809. getoutputtime,
  810. setoutputtime,
  811. setvolume,
  812. setpan,
  813. 0,0,0,0,0,0,0,0,0, // vis stuff
  814. 0,0, // dsp shit
  815. eq_set,
  816. NULL, // setinfo
  817. NULL // outmod
  818. };
  819. // =====================================================================================
  820. extern "C" __declspec(dllexport) In_Module *__cdecl winampGetInModule2()
  821. // input module getter. the only thing exported from here.
  822. // =====================================================================================
  823. {
  824. return &mikmod;
  825. }
  826. // =====================================================================================
  827. static DWORD WINAPI decodeThread(void *unused)
  828. // =====================================================================================
  829. {
  830. int has_flushed = 0;
  831. while (!killDecodeThread)
  832. {
  833. if (seek_needed >= 0)
  834. {
  835. int ms = seek_needed;
  836. seek_needed = -1;
  837. if (!(config_playflag & CPLAYFLG_SEEKBYORDERS))
  838. {
  839. Player_SetPosTime(mp, ms);
  840. decode_pos = ms * 64;
  841. }
  842. else Player_SetPosition(mp, ms/1000, TRUE);
  843. mikmod.outMod->Flush(ms);
  844. if (paused) mikmod.outMod->Pause(1);
  845. }
  846. if (!Player_Active(mp))
  847. {
  848. // check for infinite looping
  849. // infinite looping is done manually (only here). we check if
  850. // it was requested and if the loop is required (song ended
  851. // with loop or unconditional looping is on)
  852. if (mp->loopcount!=-1 || !(mp->flags&PF_LOOP || mp->state.looping<0))
  853. {
  854. if (!has_flushed)
  855. {
  856. has_flushed = 1;
  857. mikmod.outMod->Write(NULL, 0); // write all samples into buffer queue
  858. }
  859. if (!mikmod.outMod->IsPlaying())
  860. {
  861. PostMessage(mikmod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
  862. return 0;
  863. }
  864. else mikmod.outMod->CanWrite(); // make sure plug-in can do any extra processing needed
  865. Sleep(20);
  866. }
  867. else
  868. {
  869. Player_Restart(mp, TRUE);
  870. decode_pos = mp->state.curtime;
  871. }
  872. }
  873. else
  874. {
  875. Mikmod_Update(md);
  876. Sleep(8);
  877. }
  878. }
  879. return 0;
  880. }
  881. // =====================================================================================
  882. void set_priority(void) // also used in config.c
  883. // =====================================================================================
  884. {
  885. if (thread_handle != INVALID_HANDLE_VALUE)
  886. SetThreadPriority(thread_handle, GetThreadPriorityConfig());
  887. }
  888. BOOL WINAPI DllMain(HANDLE h, DWORD r, void *z)
  889. {
  890. if (r == DLL_PROCESS_ATTACH)
  891. {
  892. DisableThreadLibraryCalls((HMODULE)h);
  893. }
  894. return 1;
  895. }