info_.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  1. //tag editor file i/o code, title formatting interface
  2. #include "main.h"
  3. #include "genres.h"
  4. #include "../nu/AutoWide.h"
  5. #include "../nu/AutoChar.h"
  6. #include "../nu/ns_wc.h"
  7. #include "api__in_vorbis.h"
  8. #include <wchar.h>
  9. #include <math.h>
  10. #include <shlwapi.h>
  11. #include "vcedit.h"
  12. #include <strsafe.h>
  13. #include "resource.h"
  14. namespace ogg_helper //chainedstream_parse
  15. {
  16. int num_get_tracks(HANDLE hFile);
  17. int query_chained_stream_offset(HANDLE hFile, int idx, __int64 * out_beginning, __int64 * out_end);
  18. }
  19. /*static void xfer(HANDLE src, HANDLE dst, __int64 size)
  20. {
  21. enum { BUFFER = 1024 * 1024 };
  22. void * buffer = malloc((int)(BUFFER > size ? size : BUFFER));
  23. while (size > 0)
  24. {
  25. int d = BUFFER;
  26. if ((__int64)d > size) d = (int)size;
  27. DWORD br = 0;
  28. ReadFile(src, buffer, d, &br, 0);
  29. WriteFile(dst, buffer, d, &br, 0);
  30. size -= d;
  31. }
  32. }*/
  33. static void seek64(HANDLE src, __int64 offset)
  34. {
  35. __int64 temp = offset;
  36. SetFilePointer(src, *(DWORD*)&temp, ((long*)&temp + 1), FILE_BEGIN);
  37. }
  38. extern OSVERSIONINFO os_ver;
  39. extern HANDLE hThread;
  40. static DWORDLONG get_space(const wchar_t * url)
  41. {
  42. ULARGE_INTEGER free_space;
  43. char zzz[4] = {(char)url[0], (char)url[1], (char)url[2], 0}; //"c:\";
  44. free_space.QuadPart = 0;
  45. if (os_ver.dwPlatformId == VER_PLATFORM_WIN32_NT || (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && LOWORD(os_ver.dwBuildNumber) > 1000))
  46. {
  47. static BOOL (WINAPI* pGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
  48. if (!pGetDiskFreeSpaceEx)
  49. {
  50. pGetDiskFreeSpaceEx = (BOOL (WINAPI*)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER))GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetDiskFreeSpaceExA");
  51. }
  52. if (pGetDiskFreeSpaceEx)
  53. {
  54. ULARGE_INTEGER blah1, blah2;
  55. pGetDiskFreeSpaceEx((LPCTSTR)zzz, &free_space, &blah1, &blah2);
  56. }
  57. }
  58. if (!free_space.QuadPart)
  59. {
  60. DWORD spc, bps, nfc, tnc;
  61. GetDiskFreeSpaceA(zzz, &spc, &bps, &nfc, &tnc);
  62. free_space.QuadPart = UInt32x32To64(spc * bps, nfc);
  63. }
  64. return free_space.QuadPart;
  65. }
  66. bool sync_movefile(const wchar_t * src, const wchar_t * dst);
  67. struct vcedit_param
  68. {
  69. HANDLE hFile;
  70. __int64 remaining;
  71. };
  72. static size_t callback_fread(void *ptr, size_t size, size_t nmemb, vcedit_param * param)
  73. {
  74. int to_read = (int)(nmemb *size);
  75. if (to_read > param->remaining) to_read = (int)param->remaining;
  76. DWORD br = 0;
  77. ReadFile(param->hFile, ptr, to_read, &br, 0);
  78. param->remaining -= br;
  79. return br / size;
  80. }
  81. static size_t callback_write(const void *ptr, size_t size, size_t nmemb, HANDLE hFile)
  82. {
  83. DWORD bw = 0;
  84. WriteFile(hFile, ptr, (DWORD)(size*nmemb), &bw, 0);
  85. return bw / size;
  86. }
  87. BOOL modify_file(const wchar_t* url, const vorbis_comment * comments, int links)
  88. { //also used for stream save fix
  89. HANDLE dst = INVALID_HANDLE_VALUE;
  90. int scream = 0;
  91. StringW tmp;
  92. winampGetExtendedFileInfoW_Cleanup();
  93. tmp = url;
  94. tmp += L".tmp";
  95. HANDLE src = CreateFileW(url, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
  96. if (src != INVALID_HANDLE_VALUE)
  97. {
  98. ULARGE_INTEGER src_size;
  99. src_size.LowPart = GetFileSize(src, &src_size.HighPart);
  100. if (src_size.QuadPart > get_space(url))
  101. { //shit happens... try default temp location
  102. StringW tempdir;
  103. GetTempPathW(MAX_PATH, StringTempW(tempdir, MAX_PATH));
  104. if (get_space(tempdir) < src_size.QuadPart)
  105. { //oh well
  106. CloseHandle(src);
  107. src = INVALID_HANDLE_VALUE;
  108. }
  109. {
  110. tmp = tempdir;
  111. if (tmp[tmp.Length() - 1] != '\\') tmp.AddChar('\\');
  112. StringCchPrintfW(StringTempW(tempdir, MAX_PATH), MAX_PATH, L"ogg%u_%u.tmp", GetTickCount(), GetCurrentProcessId());
  113. tmp.AddString(tempdir);
  114. }
  115. }
  116. dst = CreateFileW(tmp, GENERIC_WRITE | GENERIC_READ, 0, 0, CREATE_ALWAYS, 0, 0);
  117. }
  118. if (dst != INVALID_HANDLE_VALUE && src != INVALID_HANDLE_VALUE)
  119. {
  120. {
  121. FILETIME ct;
  122. GetFileTime(src, &ct, 0, 0);
  123. SetFileTime(dst, &ct, 0, 0);
  124. }
  125. int num_links = ogg_helper::num_get_tracks(src);
  126. if (num_links < links) scream = 1;
  127. else
  128. {
  129. int cur_link;
  130. for (cur_link = 0; cur_link < links && !scream; cur_link++)
  131. {
  132. __int64 stream_beginning, stream_end;
  133. if (ogg_helper::query_chained_stream_offset(src, cur_link, &stream_beginning, &stream_end))
  134. {
  135. seek64(src, stream_beginning);
  136. vcedit_state *vs;
  137. vcedit_param param;
  138. param.hFile = src;
  139. param.remaining = stream_end - stream_beginning;
  140. vs = vcedit_new_state();
  141. if (vcedit_open_callbacks(vs, &param, (vcedit_read_func)callback_fread, (vcedit_write_func)callback_write) < 0)
  142. {
  143. scream = 1;
  144. }
  145. else
  146. {
  147. vorbis_comment * vc = vcedit_comments(vs);
  148. vorbis_comment_clear(vc);
  149. vorbis_comment_init(vc);
  150. const vorbis_comment * vc_src = comments + cur_link;
  151. int n;
  152. for (n = 0;n < vc_src->comments;n++)
  153. {
  154. if (vc_src->user_comments[n])
  155. vorbis_comment_add(vc, vc_src->user_comments[n]);
  156. }
  157. vcedit_write(vs, dst);
  158. vcedit_clear(vs);
  159. }
  160. }
  161. }
  162. }
  163. }
  164. else scream = 1;
  165. if (src != INVALID_HANDLE_VALUE) CloseHandle(src);
  166. if (dst != INVALID_HANDLE_VALUE)
  167. {
  168. CloseHandle(dst);
  169. if (scream)
  170. {
  171. DeleteFileW(tmp);
  172. }
  173. }
  174. if (!scream)
  175. {
  176. BOOL f_sync;
  177. EnterCriticalSection(&sync);
  178. f_sync = !_wcsicmp(url, cur_file) && hThread; //check for i/o conflict with currently played file
  179. LeaveCriticalSection(&sync);
  180. if (f_sync)
  181. { //drat, it's now playing
  182. scream = !sync_movefile(tmp, url);
  183. }
  184. else
  185. {
  186. if (!DeleteFileW(url)) scream = 1;
  187. else
  188. {
  189. if (!MoveFileW(tmp, url))
  190. {
  191. if (!CopyFileW(tmp, url, 0)) scream = 1;
  192. DeleteFileW(tmp);
  193. }
  194. }
  195. }
  196. }
  197. if (scream) return 0;
  198. else return 1;
  199. }
  200. wchar_t *wdup(const char * src)
  201. {
  202. return _wcsdup(StringW(src));
  203. }
  204. extern StringW stat_disp;
  205. void GetFileInfo(const wchar_t *file, wchar_t *title, int *len)
  206. {
  207. VorbisFile* vf = 0;
  208. BOOL is_cur_file = 0;
  209. BOOL is_vf_local = 1;
  210. if (title) *title = 0;
  211. if (len) *len = -1;
  212. if (!file || !*file)
  213. {
  214. file = cur_file;
  215. is_cur_file = 1;
  216. }
  217. else if (!lstrcmpiW(file, cur_file))
  218. {
  219. is_cur_file = 1;
  220. }
  221. if (title && stat_disp.Length() > 0 && is_cur_file)
  222. {
  223. lstrcpynW(title, stat_disp, 256);
  224. title = 0;
  225. }
  226. if (!len && !title) return ;
  227. if (is_cur_file)
  228. {
  229. EnterCriticalSection(&sync);
  230. if (theFile)
  231. {
  232. vf = theFile;
  233. is_vf_local = 0;
  234. }
  235. else
  236. LeaveCriticalSection(&sync);
  237. }
  238. if (!vf)
  239. {
  240. vf = VorbisFile::Create(file, 1);
  241. if (!vf)
  242. {
  243. if (title)
  244. {
  245. lstrcpynW(title, PathFindFileNameW(file), 256);
  246. wchar_t *blah = PathFindExtensionW(title);
  247. *blah=0;
  248. }
  249. return ;
  250. }
  251. }
  252. if (len)
  253. {
  254. *len = (int)(vf->Length() * 1000);
  255. }
  256. if (title)
  257. {
  258. const char *t = vf->get_meta("ARTIST", 0);
  259. if (t)
  260. {
  261. MultiByteToWideCharSZ(CP_UTF8, 0, t, -1, title, 256);
  262. t = vf->get_meta("TITLE", 0);
  263. if (t)
  264. {
  265. StringCchCatW(title, 256, L" - ");
  266. StringCchCatW(title, 256, AutoWide(t, CP_UTF8));
  267. }
  268. }
  269. else
  270. {
  271. const char *t = vf->get_meta("TITLE", 0);
  272. if (t)
  273. MultiByteToWideCharSZ(CP_UTF8, 0, t, -1, title, 256);
  274. else
  275. {
  276. lstrcpynW(title, PathFindFileNameW(file), 256);
  277. wchar_t *blah = PathFindExtensionW(title);
  278. *blah=0;
  279. }
  280. }
  281. }
  282. //q:
  283. if (is_vf_local)
  284. delete vf;
  285. else
  286. LeaveCriticalSection(&sync);
  287. }
  288. void w9x_itow(wchar_t *dest, int value, int destlen)
  289. {
  290. StringCchPrintfW(dest, destlen, L"%d", value);
  291. }
  292. void w9x_utow(wchar_t *dest, int value, int destlen)
  293. {
  294. StringCchPrintfW(dest, destlen, L"%u", value);
  295. }
  296. void w9x_htow(wchar_t *dest, int value, int destlen)
  297. {
  298. StringCchPrintfW(dest, destlen, L"%08x", value);
  299. }
  300. static void print_misc(VorbisFile * _vf,int link,wchar_t * out, int outlen)
  301. {
  302. OggVorbis_File * vf=&_vf->vf;
  303. double t=ov_time_total(vf,link);
  304. vorbis_info * vi=ov_info(vf,link);
  305. vorbis_comment * vc=ov_comment(vf,link);
  306. if (!vi || !vc) {WASABI_API_LNGSTRINGW_BUF(IDS_FILE_ERROR,out,outlen);return;}
  307. wchar_t kbps_str[16] = {0};
  308. WASABI_API_LNGSTRINGW_BUF(IDS_KBPS, kbps_str, 16);
  309. wchar_t length[48]=L"", avgbitrate[48]=L"", filesize[48]=L"", nombitrate[48]=L"", maxbitrate[48]=L"", minbitrate[48]=L"";
  310. if (t>0)
  311. {
  312. int h = (int)(t/3600.0);
  313. int m = (int)(t/60.0)%60;
  314. int s = (int)t%60;
  315. if(h>0) StringCchPrintfW(length,48,L"%s%u:%02u:%02u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),h,m,s);
  316. else if(m>0) StringCchPrintfW(length,48,L"%s%u:%02u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),m,s);
  317. else if(s>0) StringCchPrintfW(length,48,L"%s%u\r\n",WASABI_API_LNGSTRINGW(IDS_LENGTH),s);
  318. UINT fs=_vf->FileSize();
  319. if (fs>0)
  320. {
  321. int kbps = (int)(((double)fs)/(t*125.0));
  322. wchar_t tmp[32] = {0};
  323. StringCchPrintfW(avgbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_AVERAGE_BITRATE),kbps,kbps_str);
  324. int fs1=fs/1000000;
  325. int fs2=(fs/1000)%1000;
  326. int fs3=fs%1000;
  327. if(fs1)
  328. StringCchPrintfW(filesize,48,L"%s%u%03u%03u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs1,fs2,fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32));
  329. else if(fs2)
  330. StringCchPrintfW(filesize,48,L"%s%u%03u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs2,fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32));
  331. else
  332. StringCchPrintfW(filesize,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_FILE_SIZE),fs3,WASABI_API_LNGSTRINGW_BUF(IDS_BYTES,tmp,32));
  333. }
  334. }
  335. if (vi->bitrate_nominal>0)
  336. StringCchPrintfW(nombitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_NOMINAL_BITRATE),vi->bitrate_nominal/1000,kbps_str);
  337. if (vi->bitrate_lower>0)
  338. StringCchPrintfW(minbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_MIN_BITRATE),vi->bitrate_lower/1000,kbps_str);
  339. if (vi->bitrate_nominal>0)
  340. StringCchPrintfW(maxbitrate,48,L"%s%u %s\r\n",WASABI_API_LNGSTRINGW(IDS_MAX_BITRATE),vi->bitrate_nominal/1000,kbps_str);
  341. wchar_t tmp[32] = {0}, tmp2[32] = {0}, tmp3[32] = {0}, tmp4[32] = {0}, tmp5[32] = {0}, hzStr[8] = {0};
  342. StringCchPrintfW(out,outlen,L"%s%s%s%s%s%s%s: %u\r\n%s: %u %s\r\n%s: %u\r\n%s: %u\r\n%s: \r\n%s",
  343. length, avgbitrate, filesize, nombitrate, maxbitrate, minbitrate,
  344. WASABI_API_LNGSTRINGW_BUF(IDS_CHANNELS,tmp,32),vi->channels,
  345. WASABI_API_LNGSTRINGW_BUF(IDS_SAMPLING_RATE,tmp2,32),vi->rate, WASABI_API_LNGSTRINGW_BUF(IDS_HZ,hzStr,8),
  346. WASABI_API_LNGSTRINGW_BUF(IDS_SERIAL_NUMBER,tmp3,32),ov_serialnumber(vf,link),
  347. WASABI_API_LNGSTRINGW_BUF(IDS_VERSION,tmp4,32),vi->version,
  348. WASABI_API_LNGSTRINGW_BUF(IDS_VENDOR,tmp5,32),(wchar_t*)AutoWide(vc->vendor,CP_UTF8));
  349. }
  350. static VorbisFile* last_vf = 0;
  351. static wchar_t last_file[MAX_PATH] = {0};
  352. static FILETIME ftLastWriteTime;
  353. // is used to determine if the last write time of the file has changed when
  354. // asked to get the metadata for the same cached file so we can update things
  355. BOOL HasFileTimeChanged(const wchar_t *fn)
  356. {
  357. WIN32_FILE_ATTRIBUTE_DATA fileData = {0};
  358. if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
  359. {
  360. if(CompareFileTime(&ftLastWriteTime, &fileData.ftLastWriteTime))
  361. {
  362. ftLastWriteTime = fileData.ftLastWriteTime;
  363. return TRUE;
  364. }
  365. }
  366. return FALSE;
  367. }
  368. void UpdateFileTimeChanged(const wchar_t *fn)
  369. {
  370. WIN32_FILE_ATTRIBUTE_DATA fileData;
  371. if (GetFileAttributesExW(fn, GetFileExInfoStandard, &fileData) == TRUE)
  372. {
  373. ftLastWriteTime = fileData.ftLastWriteTime;
  374. }
  375. }
  376. // need to call this when we shut down just to make sure things are correctly cleaned up
  377. //(the joys of caching for speed)
  378. void winampGetExtendedFileInfoW_Cleanup(void)
  379. {
  380. if (last_vf)
  381. {
  382. delete last_vf;
  383. last_vf = 0;
  384. }
  385. last_file[0] = 0;
  386. }
  387. static void CALLBACK winampGetExtendedFileInfoW_Timer(HWND hwnd, UINT uMsg, UINT_PTR eventId, DWORD elapsed)
  388. {
  389. // TODO need to do a better way of getting and caching the metadata
  390. // this is _just_ a temp fix for the file being locked when it
  391. // it really needs 'class Info' to be able to cache and read.
  392. KillTimer(hwnd, eventId);
  393. winampGetExtendedFileInfoW_Cleanup();
  394. }
  395. bool KeywordMatch(const char *mainString, const char *keyword)
  396. {
  397. return !_stricmp(mainString, keyword);
  398. }
  399. #define START_TAG_ALIAS(name, alias) if (KeywordMatch(data, name)) lookup=alias
  400. #define TAG_ALIAS(name, alias) else if (KeywordMatch(data, name)) lookup=alias
  401. extern "C" __declspec( dllexport ) int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
  402. {
  403. if (!_stricmp(data, "type"))
  404. {
  405. dest[0] = '0';
  406. dest[1] = 0;
  407. return 1;
  408. }
  409. else if (!_stricmp(data, "rateable"))
  410. {
  411. dest[0] = '1';
  412. dest[1] = 0;
  413. return 1;
  414. }
  415. else if (!_stricmp(data, "streammetadata"))
  416. {
  417. return 0;
  418. }
  419. if (!fn || (fn && !fn[0])) return 0;
  420. if (!_stricmp(data, "family"))
  421. {
  422. LPCWSTR e;
  423. int pID = -1;
  424. DWORD lcid;
  425. e = PathFindExtensionW(fn);
  426. if (L'.' != *e) return 0;
  427. e++;
  428. lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
  429. if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"OGG", -1) ||
  430. CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, L"OGA", -1)) pID = IDS_FAMILY_STRING;
  431. if (pID != -1 && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(pID))) return 1;
  432. return 0;
  433. }
  434. if (!_stricmp(data, "mime"))
  435. {
  436. StringCchCopyW(dest, destlen, L"audio/ogg");
  437. return 1;
  438. }
  439. // attempt to cache/use a cached instance of VorbisFile to speed up metadata queries
  440. // which is especially needed with large ogg files (like with a 4Mb embedded image!)
  441. VorbisFile* vf = 0;
  442. if(last_file[0] && !_wcsicmp(last_file, fn) && !HasFileTimeChanged(fn))
  443. {
  444. vf = last_vf;
  445. }
  446. else
  447. {
  448. // different file so clean up if there's a cached instance
  449. if(last_vf)
  450. {
  451. delete last_vf;
  452. last_vf = 0;
  453. }
  454. // open the new file and cache the current filename for subsequent query checks
  455. vf = VorbisFile::Create(fn, 1);
  456. lstrcpynW(last_file, fn, MAX_PATH);
  457. }
  458. if (!vf) return 0;
  459. else last_vf = vf;
  460. // TODO need to do a better way of getting and caching the metadata
  461. // this is _just_ a temp fix for the file being locked when it
  462. // it really needs 'class Info' to be able to cache and read.
  463. SetTimer(mod.hMainWindow, 256, 2000, winampGetExtendedFileInfoW_Timer);
  464. const char *lookup = 0;
  465. if (!_stricmp(data, "length"))
  466. {
  467. int len = (int)(vf->Length() * 1000);
  468. w9x_itow(dest, len, destlen);
  469. return 1;
  470. }
  471. else if (!_stricmp(data, "bitrate"))
  472. {
  473. int br = vf->get_avg_bitrate();
  474. w9x_itow(dest, br, destlen);
  475. return 1;
  476. }
  477. else if (!_stricmp(data, "SERIALNUMBER"))
  478. {
  479. w9x_utow(dest, ov_serialnumber(&vf->vf, -1), destlen);
  480. return 1;
  481. }
  482. else if (!_stricmp(data, "SERIALNUMBER_HEX"))
  483. {
  484. w9x_htow(dest, ov_serialnumber(&vf->vf, -1), destlen);
  485. return 1;
  486. }
  487. else if (!_stricmp(data, "gain"))
  488. {
  489. float gain = 20.0f*(float)log10(vf->GetGain());
  490. StringCchPrintfW(dest, destlen, L"%-+.2f dB", gain);
  491. return 1;
  492. }
  493. else if(!_stricmp(data,"formatinformation"))
  494. {
  495. print_misc(vf,0,dest,destlen);
  496. return 1;
  497. }
  498. TAG_ALIAS("title", "TITLE");
  499. TAG_ALIAS("artist", "ARTIST");
  500. TAG_ALIAS("album", "ALBUM");
  501. TAG_ALIAS("genre", "GENRE");
  502. TAG_ALIAS("comment", "COMMENT");
  503. TAG_ALIAS("year", "DATE");
  504. TAG_ALIAS("track", "TRACKNUMBER");
  505. TAG_ALIAS("albumartist", "ALBUMARTIST");
  506. TAG_ALIAS("composer", "COMPOSER");
  507. TAG_ALIAS("disc", "DISCNUMBER");
  508. TAG_ALIAS("publisher", "PUBLISHER");
  509. TAG_ALIAS("conductor", "CONDUCTOR");
  510. TAG_ALIAS("tool", "ENCODED-BY");
  511. TAG_ALIAS("replaygain_track_gain", "REPLAYGAIN_TRACK_GAIN");
  512. TAG_ALIAS("replaygain_track_peak", "REPLAYGAIN_TRACK_PEAK");
  513. TAG_ALIAS("replaygain_album_gain", "REPLAYGAIN_ALBUM_GAIN");
  514. TAG_ALIAS("replaygain_album_peak", "REPLAYGAIN_ALBUM_PEAK");
  515. TAG_ALIAS("GracenoteFileID", "GRACENOTEFILEID");
  516. TAG_ALIAS("GracenoteExtData", "GRACENOTEEXTDATA");
  517. TAG_ALIAS("bpm", "BPM");
  518. TAG_ALIAS("remixing", "REMIXING");
  519. TAG_ALIAS("subtitle", "VERSION");
  520. TAG_ALIAS("isrc", "ISRC");
  521. TAG_ALIAS("category", "CATEGORY");
  522. TAG_ALIAS("rating", "RATING");
  523. TAG_ALIAS("producer", "PRODUCER");
  524. if (!lookup)
  525. return 0;
  526. const char *value = vf->get_meta(lookup, 0);
  527. if(KeywordMatch("comment",data)) {
  528. if(!value || !*value) value = vf->get_meta("DESCRIPTION", 0);
  529. }
  530. if(KeywordMatch("year",data)) {
  531. if(!value || !*value) value = vf->get_meta("YEAR", 0);
  532. }
  533. if(KeywordMatch("track",data)) {
  534. if(!value || !*value) value = vf->get_meta("TRACK", 0);
  535. }
  536. if(KeywordMatch("albumartist",data)) {
  537. if(!value || !*value) value = vf->get_meta("ALBUM ARTIST", 0);
  538. if(!value || !*value) value = vf->get_meta("ENSEMBLE", 0);
  539. }
  540. if(KeywordMatch("publisher",data)) {
  541. if(!value || !*value) value = vf->get_meta("ORGANIZATION", 0);
  542. }
  543. if(KeywordMatch("category",data)) {
  544. if(!value || !*value) value = vf->get_meta("CONTENTGROUP", 0);
  545. if(!value || !*value) value = vf->get_meta("GROUPING", 0);
  546. }
  547. if(KeywordMatch(data, "rating")) {
  548. if(!value || !*value) value = vf->get_meta("RATING", 0);
  549. if(value && *value) {
  550. int rating = atoi(value);
  551. // keeps things limited to our range of 0-100
  552. if (rating >= 100) {
  553. rating = 5;
  554. }
  555. // 1-100 case
  556. else if (rating > 5 && rating < 100) {
  557. rating /= 20;
  558. // shift up by one rating when in next band
  559. // 1-20 = 1, 21-40 = 2, 41-60 = 3, 61-80 = 4, 81-100 = 5
  560. rating += ((atoi(value) - (rating * 20)) > 0);
  561. }
  562. // Remove support for old 1-10 range
  563. /* or maybe we're dealing with a 1-10 range
  564. else if (rating > 5) {
  565. rating /= 2;
  566. } */
  567. // otherwise it is hopefully in the 0-5 range
  568. else if (rating > 0 && rating <= 5) {
  569. }
  570. // otherwise just make sure and set zero
  571. else {
  572. rating = 0;
  573. }
  574. StringCchPrintfW(dest, destlen, L"%u", rating);
  575. return 1;
  576. }
  577. }
  578. if(value)
  579. MultiByteToWideCharSZ(CP_UTF8, 0, value, -1, dest, destlen);
  580. else
  581. {
  582. dest[0]=0;
  583. return 1;
  584. }
  585. return 1;
  586. }