ExtendedFileInfo.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. #include "main.h"
  2. #include "../nu/AutoWide.h"
  3. #include "WMPlaylist.h"
  4. #include "resource.h"
  5. #include <math.h>
  6. #include <strsafe.h>
  7. WMInformation *setFileInfo = 0;
  8. static wchar_t *setFileInfoName=0;
  9. static bool forcedStop = false;
  10. static int outTime = 0;
  11. float GetGain(WMInformation *info, bool allowDefault);
  12. static const wchar_t *extension(const wchar_t *fn)
  13. {
  14. const wchar_t *x = PathFindExtension(fn);
  15. if (*x)
  16. return CharNext(x);
  17. else
  18. return x;
  19. }
  20. static bool KeywordMatch(const char *mainString, const char *keyword)
  21. {
  22. return !_stricmp(mainString, keyword);
  23. }
  24. static bool KeywordMatch(const wchar_t *mainString, const wchar_t *keyword)
  25. {
  26. return !lstrcmpiW(mainString, keyword);
  27. }
  28. static bool StartsWith(const wchar_t *mainString, const wchar_t *substring)
  29. {
  30. return !_wcsnicmp(mainString, substring, lstrlenW(substring));
  31. }
  32. static int Width(int dec)
  33. {
  34. // there's probably a better way
  35. int width=3;
  36. while (width && (dec % 10) == 0)
  37. {
  38. dec/=10;
  39. width--;
  40. }
  41. return width;
  42. }
  43. int GetExtendedInformation(WMInformation *getExtendedFileInfo, const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
  44. {
  45. AutoWide tagNameW(data);
  46. const wchar_t *tagName = GetAlias(tagNameW);
  47. if (KeywordMatch(tagName, L"streammetadata"))
  48. {
  49. if (config_http_metadata)
  50. {
  51. lstrcpyn(dest, L"1", destlen);
  52. return 1;
  53. }
  54. return 0;
  55. }
  56. else if (KeywordMatch(tagName, L"type"))
  57. {
  58. if (!fn || !fn[0])
  59. lstrcpyn(dest, (config_no_video ? L"0" : L"1"), destlen);
  60. else if (getExtendedFileInfo->IsAttribute(g_wszWMHasVideo))
  61. lstrcpyn(dest, L"1", destlen);
  62. else if (getExtendedFileInfo->IsAttribute(g_wszWMHasAudio))
  63. lstrcpyn(dest, L"0", destlen);
  64. else
  65. {
  66. switch (fileTypes.GetAVType(extension(fn)))
  67. {
  68. case FileType::AUDIO:
  69. lstrcpyn(dest, L"0", destlen);
  70. break;
  71. case FileType::VIDEO:
  72. lstrcpyn(dest, L"1", destlen);
  73. break;
  74. default:
  75. return 0;
  76. }
  77. }
  78. return 1;
  79. }
  80. else if (KeywordMatch(tagName, L"rateable"))
  81. {
  82. dest[0] = '1';
  83. dest[1] = 0;
  84. return 1;
  85. }
  86. else if (StartsWith(tagName, L"WM/"))
  87. {
  88. getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
  89. return 1;
  90. }
  91. /*else if (KeywordMatch(data, "burnable")) // note: this isn't supposed to be any kind of protection - just keeps the burner from misbehaving on protected tracks
  92. {
  93. if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
  94. lstrcpyn(dest, L"0", destlen);
  95. else
  96. lstrcpyn(dest, L"1", destlen);
  97. return 1;
  98. }*/
  99. else if (KeywordMatch(data, "noburnreason")) // note: this isn't supposed to be any kind of protection - just keeps the burner from misbehaving on protected tracks
  100. {
  101. if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
  102. {
  103. lstrcpyn(dest, L"DRM (copy protected) file", destlen);
  104. return 1;
  105. }
  106. }
  107. else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
  108. {
  109. getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
  110. return 1;
  111. }
  112. else if (KeywordMatch(data, "bitrate"))
  113. {
  114. StringCchPrintfW(dest, destlen, L"%u", getExtendedFileInfo->GetBitrate() / 1000);
  115. return 1;
  116. }
  117. else if (KeywordMatch(data, "vbr"))
  118. {
  119. if (getExtendedFileInfo->IsAttribute(g_wszWMIsVBR))
  120. StringCchCopyW(dest, destlen, L"1");
  121. else if (getExtendedFileInfo->IsNotAttribute(g_wszWMIsVBR))
  122. StringCchCopyW(dest, destlen, L"0");
  123. return 1;
  124. }
  125. //else if (KeywordMatch(data, "srate"))
  126. else if (KeywordMatch(data, "length"))
  127. {
  128. long length = getExtendedFileInfo->GetLengthMilliseconds();
  129. if (length == -1000)
  130. return 0;
  131. _itow(length, dest, 10);
  132. return 1;
  133. }
  134. else if (KeywordMatch(data, "rating"))
  135. {
  136. wchar_t rating_string[128] = {0};
  137. getExtendedFileInfo->GetAttribute(L"WM/SharedUserRating", rating_string, 128);
  138. int rating = _wtoi(rating_string);
  139. if (rating == 0)
  140. dest[0]=0;
  141. else if (rating >= 1 && rating <= 12)
  142. dest[0]=L'1';
  143. else if (rating >= 13 && rating <= 37)
  144. dest[0]=L'2';
  145. else if (rating >= 38 && rating <= 62)
  146. dest[0]=L'3';
  147. else if (rating >= 63 && rating <= 86)
  148. dest[0]=L'4';
  149. else
  150. dest[0]=L'5';
  151. dest[1]=0;
  152. return 1;
  153. }
  154. else if (KeywordMatch(data, "replaygain_track_gain")
  155. || KeywordMatch(data, "replaygain_track_peak")
  156. || KeywordMatch(data, "replaygain_album_gain")
  157. || KeywordMatch(data, "replaygain_album_peak"))
  158. {
  159. getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
  160. return 1;
  161. }
  162. else if (KeywordMatch(data, "gain"))
  163. {
  164. StringCchPrintfW(dest, destlen, L"%-+.2f dB", (float)log10f(GetGain(getExtendedFileInfo, false))*20.0f);
  165. return 1;
  166. }
  167. else if (KeywordMatch(data, "audiocodec"))
  168. {
  169. if (!getExtendedFileInfo->GetCodecName(dest, destlen))
  170. dest[0]=0;
  171. return 1;
  172. }
  173. else if (KeywordMatch(data, "lossless"))
  174. {
  175. wchar_t codecname[1024] = {0};
  176. if (!getExtendedFileInfo->GetCodecName(codecname, 1024))
  177. dest[0]=0;
  178. else
  179. {
  180. dest[0] = wcsstr(codecname, L"Lossless")?'1':'0';
  181. dest[1]=0;
  182. }
  183. return 1;
  184. }
  185. else if (KeywordMatch(data, "GracenoteFileID"))
  186. {
  187. getExtendedFileInfo->GetAttribute_BinString(L"GN/UniqueFileIdentifier", dest, destlen);
  188. return 1;
  189. }
  190. else if (KeywordMatch(data, "GracenoteExtData"))
  191. {
  192. getExtendedFileInfo->GetAttribute_BinString(L"GN/ExtData", dest, destlen);
  193. return 1;
  194. }
  195. else if (KeywordMatch(data, "formatinformation"))
  196. {
  197. // this is a bit of a clusterfuck, but it's safe and (hopefully) logically laid out.
  198. wchar_t codec[128]=L"", duration[64]=L"", bitrate[64]=L"", filesize[64]=L"", wmver[64]=L"", seekable[64]=L"";
  199. wchar_t stridable[64]=L"", broadcast[64]=L"", protect[64]=L"", trusted[64]=L"", contents[64]=L"";
  200. wchar_t buf[128]=L""; // temporary buffer
  201. // get the codec name string
  202. if (getExtendedFileInfo->GetCodecName(buf, 128) && buf[0])
  203. StringCchPrintf(codec,128,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_CODEC),buf);
  204. // get the length string formatted h:mm:ss.tttt
  205. long t = getExtendedFileInfo->GetLengthMilliseconds();
  206. if (t)
  207. {
  208. long h = t/36000000;
  209. long m = (t/60000)%60;
  210. long s = (t/1000)%60;
  211. long ms = t%1000;
  212. if (h)
  213. StringCchPrintf(duration,64,L"%s: %u:%02u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),h,m,s,ms);
  214. else if (m)
  215. StringCchPrintf(duration,64,L"%s: %u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),m,s,ms);
  216. else
  217. StringCchPrintf(duration,64,L"%s: %u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),s,ms);
  218. }
  219. // get the bitrate string formatted 128.235 kbps
  220. long br = getExtendedFileInfo->GetBitrate();
  221. wchar_t kbps[16] = {0};
  222. StringCchPrintf(bitrate,64,L"%s: %.*f %s\n",
  223. WASABI_API_LNGSTRINGW(IDS_BITRATE),
  224. Width(br%1000),
  225. br/1000.0,
  226. WASABI_API_LNGSTRINGW_BUF(IDS_KBPS,kbps,16));
  227. // get the filesize string, with commas grouping in threes
  228. buf[0]=0;
  229. getExtendedFileInfo->GetAttribute(L"FileSize",buf,64);
  230. uint64_t fs = _wcstoui64(buf, 0, 10);
  231. if (fs)
  232. {
  233. uint64_t fsgb = (fs/1000000000LL);
  234. uint64_t fsmb = (fs/1000000LL)%1000LL;
  235. uint64_t fskb = (fs/1000LL)%1000LL;
  236. uint64_t fsb = fs%1000LL;
  237. if (fsgb)
  238. StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsgb,fsmb,fskb,fsb);
  239. else if (fsmb)
  240. StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsmb,fskb,fsb);
  241. else if (fskb)
  242. StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fskb,fsb);
  243. else
  244. StringCchPrintf(filesize,64,L"%s: %I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsb);
  245. }
  246. // 4 boolean flags, compose their strings
  247. wchar_t yes[64] = {0}, no[64] = {0};
  248. WASABI_API_LNGSTRINGW_BUF(IDS_YES,yes,64);
  249. WASABI_API_LNGSTRINGW_BUF(IDS_NO,no,64);
  250. buf[0]=0;
  251. getExtendedFileInfo->GetAttribute(L"WMFSDKVersion",buf,128);
  252. if (buf[0])
  253. StringCchPrintf(wmver,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_WMVER),buf);
  254. StringCchPrintf(seekable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_SEEKABLE),getExtendedFileInfo->IsAttribute(L"Seekable")?yes:no);
  255. StringCchPrintf(stridable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_STRIDABLE),getExtendedFileInfo->IsAttribute(L"Stridable")?yes:no);
  256. StringCchPrintf(broadcast,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_BROADCAST),getExtendedFileInfo->IsAttribute(L"Broadcast")?yes:no);
  257. StringCchPrintf(protect,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_PROTECTED),getExtendedFileInfo->IsAttribute(L"Is_Protected")?yes:no);
  258. StringCchPrintf(trusted,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_TRUSTED),getExtendedFileInfo->IsAttribute(L"Is_Trusted")?yes:no);
  259. // file contents. bit gross i know.
  260. wchar_t cont[4][16]={L"",L"",L"",L""};
  261. int i=0;
  262. if (getExtendedFileInfo->IsAttribute(L"HasAudio"))
  263. WASABI_API_LNGSTRINGW_BUF(IDS_AUDIO,cont[i++],16);
  264. if (getExtendedFileInfo->IsAttribute(L"HasVideo"))
  265. WASABI_API_LNGSTRINGW_BUF(IDS_VIDEO,cont[i++],16);
  266. if (getExtendedFileInfo->IsAttribute(L"HasImage"))
  267. WASABI_API_LNGSTRINGW_BUF(IDS_IMAGE,cont[i++],16);
  268. if (getExtendedFileInfo->IsAttribute(L"HasScript"))
  269. WASABI_API_LNGSTRINGW_BUF(IDS_SCRIPT,cont[i++],16);
  270. WASABI_API_LNGSTRINGW_BUF(IDS_NONE,buf,64);
  271. if (i == 0)
  272. StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), buf);
  273. else if (i == 1)
  274. StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0]);
  275. else if (i == 2)
  276. StringCchPrintf(contents,64,L"%s: %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1]);
  277. else if (i == 3)
  278. StringCchPrintf(contents,64,L"%s: %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2]);
  279. else if (i == 4)
  280. StringCchPrintf(contents,64,L"%s: %s, %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2], cont[3]);
  281. // compose our string together!
  282. StringCchPrintf(dest,destlen,L"%s%s%s%s%s%s%s%s%s%s%s",codec, duration, bitrate, filesize, wmver, seekable, stridable, broadcast, protect, trusted, contents);
  283. }
  284. else
  285. return 0;
  286. return 1;
  287. }
  288. #if 0 // had to disable this because it was locking the file from being deleted
  289. WMInformation *lastGetInfo = 0;
  290. wchar_t *lastGetInfoFn;
  291. #endif
  292. extern "C" __declspec(dllexport)
  293. int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
  294. {
  295. /* Check if there's a status message for this filename
  296. doing this forces Winamp to hit plugin.getfileinfo, which gives us better control
  297. over adding things like [Individualizing] to the playlist title for local files
  298. */
  299. if (winamp.HasStatus(fn))
  300. return 0;
  301. if ((!fn || !*fn) && KeywordMatch(data, "type"))
  302. {
  303. if (config_no_video)
  304. lstrcpyn(dest, L"0", destlen);
  305. else
  306. lstrcpyn(dest, L"1", destlen);
  307. return 1;
  308. }
  309. if (KeywordMatch(data, "mime"))
  310. {
  311. int len;
  312. const wchar_t *p;
  313. if (!fn || !fn[0]) return 0;
  314. len = lstrlenW(fn);
  315. if (len < 4 || L'.' != fn[len - 4]) return 0;
  316. p = &fn[len - 3];
  317. if (!_wcsicmp(p, L"WMA")) { StringCchCopyW(dest, destlen, L"audio/x-ms-wma"); return 1; }
  318. if (!_wcsicmp(p, L"WMV")) { StringCchCopyW(dest, destlen, L"video/x-ms-wmv"); return 1; }
  319. if (!_wcsicmp(p, L"ASF")) { StringCchCopyW(dest, destlen, L"video/x-ms-asf"); return 1; }
  320. return 0;
  321. }
  322. if (KeywordMatch(data, "family"))
  323. {
  324. LPCWSTR e, p(NULL);
  325. DWORD lcid;
  326. size_t i;
  327. int len2(0);
  328. if (!fn || !*fn) return 0;
  329. e = PathFindExtensionW(fn);
  330. if (L'.' != *e) return 0;
  331. e++;
  332. if (!*e) return 0;
  333. lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
  334. for (i = 0; i < fileTypes.types.size() && !p; i++)
  335. {
  336. if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, fileTypes.types.at(i).wtype, -1))
  337. p = fileTypes.types.at(i).description;
  338. }
  339. if (p)
  340. {
  341. wchar_t szTest[16];
  342. if (S_OK == StringCchPrintfW(szTest, sizeof(szTest)/sizeof(wchar_t), L" (*.%s)", e))
  343. {
  344. int len1 = lstrlenW(szTest);
  345. len2 = lstrlenW(p);
  346. if (len2 > len1 && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, szTest, -1, (p + len2 - len1), -1))
  347. len2 -= len1;
  348. }
  349. }
  350. return (p && S_OK == StringCchCopyNW(dest, destlen, p, len2));
  351. }
  352. if (!config_http_metadata && PathIsURL(fn))
  353. return 0;
  354. if (KeywordMatch(data, "type") && !PathFileExistsW(fn))
  355. {
  356. switch (fileTypes.GetAVType(extension(fn)))
  357. {
  358. case FileType::AUDIO:
  359. lstrcpyn(dest, L"0", destlen);
  360. return 1;
  361. case FileType::VIDEO:
  362. lstrcpyn(dest, L"1", destlen);
  363. return 1;
  364. default:
  365. return 0;
  366. }
  367. }
  368. if (activePlaylist.IsMe(fn))
  369. {
  370. WMInformation getExtendedFileInfo(activePlaylist.GetFileName());
  371. return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
  372. }
  373. else
  374. {
  375. WMInformation getExtendedFileInfo(fn);
  376. return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
  377. }
  378. #if 0 // had to disable this because it was locking the file from being deleted
  379. if (lastGetInfo && lastGetInfoFn && !_wcsicmp(fn, lastGetInfoFn))
  380. {
  381. return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
  382. }
  383. delete lastGetInfo;
  384. lastGetInfo=0;
  385. free(lastGetInfoFn);
  386. lastGetInfoFn=0;
  387. if (activePlaylist.IsMe(fn))
  388. lastGetInfoFn = _wcsdup(activePlaylist.GetFileName());
  389. else
  390. lastGetInfoFn = _wcsdup(fn);
  391. lastGetInfo = new WMInformation(lastGetInfoFn);
  392. if (lastGetInfo->ErrorOpening())
  393. {
  394. if (KeywordMatch(data, "type"))
  395. {
  396. switch (fileTypes.GetAVType(extension(fn)))
  397. {
  398. case FileType::AUDIO:
  399. lstrcpyn(dest, L"0", destlen);
  400. return 1;
  401. case FileType::VIDEO:
  402. lstrcpyn(dest, L"1", destlen);
  403. return 1;
  404. default:
  405. return 0;
  406. }
  407. }
  408. delete lastGetInfo;
  409. lastGetInfo=0;
  410. free(lastGetInfoFn);
  411. lastGetInfoFn=0;
  412. return 0;
  413. }
  414. return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
  415. #endif
  416. }
  417. extern "C" __declspec(dllexport) int winampClearExtendedFileInfoW(const wchar_t *fn)
  418. {
  419. // TODO: press stop if it's the currently playing file
  420. WMInformation wmInfo(fn);
  421. wmInfo.ClearAllAttributes();
  422. wmInfo.Flush();
  423. return 1;
  424. }
  425. extern "C" __declspec(dllexport) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
  426. {
  427. // if (!lastSetInfoFilename.empty() && lastSetInfoFilename != fn)
  428. // dosomething();
  429. #if 0 // had to disable this because it was locking the file from being deleted
  430. if (lastGetInfoFn && !_wcsicmp(lastGetInfoFn,fn))
  431. {
  432. delete lastGetInfo;
  433. lastGetInfo=0;
  434. free(lastGetInfoFn);
  435. lastGetInfoFn=0;
  436. }
  437. #endif
  438. if (!setFileInfo)
  439. {
  440. if (activePlaylist.IsMe(fn) && mod.playing)
  441. {
  442. forcedStop = true;
  443. outTime = mod.GetOutputTime();
  444. winamp.PressStop();
  445. }
  446. free(setFileInfoName);
  447. setFileInfoName = _wcsdup(fn);
  448. setFileInfo = new WMInformation(fn);
  449. if (!setFileInfo->MakeWritable(fn))
  450. return 0; // can't write
  451. }
  452. AutoWide tagNameW(data);
  453. const wchar_t *tagName = GetAlias(tagNameW);
  454. if (StartsWith(tagName, L"WM/"))
  455. {
  456. setFileInfo->SetAttribute(tagName, val);
  457. return 1;
  458. }
  459. else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
  460. {
  461. setFileInfo->SetAttribute(tagName, val);
  462. return 1;
  463. }
  464. else if (KeywordMatch(data, "rating"))
  465. {
  466. int rating = _wtoi(val);
  467. if (rating == 0)
  468. setFileInfo->SetAttribute(L"WM/SharedUserRating", L"",WMT_TYPE_DWORD);
  469. else
  470. {
  471. switch(rating)
  472. {
  473. case 1:
  474. setFileInfo->SetAttribute(L"WM/SharedUserRating", L"1",WMT_TYPE_DWORD);
  475. break;
  476. case 2:
  477. setFileInfo->SetAttribute(L"WM/SharedUserRating", L"25",WMT_TYPE_DWORD);
  478. break;
  479. case 3:
  480. setFileInfo->SetAttribute(L"WM/SharedUserRating", L"50",WMT_TYPE_DWORD);
  481. break;
  482. case 4:
  483. setFileInfo->SetAttribute(L"WM/SharedUserRating", L"75",WMT_TYPE_DWORD);
  484. break;
  485. default:
  486. setFileInfo->SetAttribute(L"WM/SharedUserRating", L"99",WMT_TYPE_DWORD);
  487. break;
  488. }
  489. }
  490. }
  491. else if (KeywordMatch(data, "replaygain_track_gain")
  492. || KeywordMatch(data, "replaygain_track_peak")
  493. || KeywordMatch(data, "replaygain_album_gain")
  494. || KeywordMatch(data, "replaygain_album_peak"))
  495. {
  496. setFileInfo->SetAttribute(tagName, val);
  497. return 1;
  498. }
  499. else if (KeywordMatch(data, "GracenoteFileID"))
  500. {
  501. setFileInfo->SetAttribute_BinString(L"GN/UniqueFileIdentifier", val);
  502. return 1;
  503. }
  504. else if (KeywordMatch(data, "GracenoteExtData"))
  505. {
  506. setFileInfo->SetAttribute_BinString(L"GN/ExtData", val);
  507. return 1;
  508. }
  509. // else if (KeywordMatch(data, "bitrate"))
  510. //else if (KeywordMatch(data, "disc"))
  511. // else if (KeywordMatch(data, "vbr"))
  512. //else if (KeywordMatch(data, "srate"))
  513. // else if (KeywordMatch(data, "length"))
  514. return 0;
  515. }
  516. extern "C" __declspec(dllexport) int winampWriteExtendedFileInfo()
  517. {
  518. if (setFileInfo)
  519. {
  520. bool flushOK = setFileInfo->Flush();
  521. delete setFileInfo;
  522. setFileInfo = 0;
  523. if (forcedStop)
  524. {
  525. mod.startAtMilliseconds = outTime;
  526. winamp.PressPlay();
  527. }
  528. forcedStop=false;
  529. if (flushOK)
  530. return 1;
  531. else
  532. return 0;
  533. }
  534. return 0;
  535. }