123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597 |
- #include "main.h"
- #include "../nu/AutoWide.h"
- #include "WMPlaylist.h"
- #include "resource.h"
- #include <math.h>
- #include <strsafe.h>
- WMInformation *setFileInfo = 0;
- static wchar_t *setFileInfoName=0;
- static bool forcedStop = false;
- static int outTime = 0;
- float GetGain(WMInformation *info, bool allowDefault);
- static const wchar_t *extension(const wchar_t *fn)
- {
- const wchar_t *x = PathFindExtension(fn);
- if (*x)
- return CharNext(x);
- else
- return x;
- }
- static bool KeywordMatch(const char *mainString, const char *keyword)
- {
- return !_stricmp(mainString, keyword);
- }
- static bool KeywordMatch(const wchar_t *mainString, const wchar_t *keyword)
- {
- return !lstrcmpiW(mainString, keyword);
- }
- static bool StartsWith(const wchar_t *mainString, const wchar_t *substring)
- {
- return !_wcsnicmp(mainString, substring, lstrlenW(substring));
- }
- static int Width(int dec)
- {
- // there's probably a better way
- int width=3;
- while (width && (dec % 10) == 0)
- {
- dec/=10;
- width--;
- }
- return width;
- }
- int GetExtendedInformation(WMInformation *getExtendedFileInfo, const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
- {
- AutoWide tagNameW(data);
- const wchar_t *tagName = GetAlias(tagNameW);
- if (KeywordMatch(tagName, L"streammetadata"))
- {
- if (config_http_metadata)
- {
- lstrcpyn(dest, L"1", destlen);
- return 1;
- }
- return 0;
- }
- else if (KeywordMatch(tagName, L"type"))
- {
- if (!fn || !fn[0])
- lstrcpyn(dest, (config_no_video ? L"0" : L"1"), destlen);
- else if (getExtendedFileInfo->IsAttribute(g_wszWMHasVideo))
- lstrcpyn(dest, L"1", destlen);
- else if (getExtendedFileInfo->IsAttribute(g_wszWMHasAudio))
- lstrcpyn(dest, L"0", destlen);
- else
- {
- switch (fileTypes.GetAVType(extension(fn)))
- {
- case FileType::AUDIO:
- lstrcpyn(dest, L"0", destlen);
- break;
- case FileType::VIDEO:
- lstrcpyn(dest, L"1", destlen);
- break;
- default:
- return 0;
- }
- }
- return 1;
- }
- else if (KeywordMatch(tagName, L"rateable"))
- {
- dest[0] = '1';
- dest[1] = 0;
- return 1;
- }
- else if (StartsWith(tagName, L"WM/"))
- {
- getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
- return 1;
- }
- /*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
- {
- if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
- lstrcpyn(dest, L"0", destlen);
- else
- lstrcpyn(dest, L"1", destlen);
- return 1;
- }*/
- 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
- {
- if (getExtendedFileInfo->IsAttribute(g_wszWMProtected))
- {
- lstrcpyn(dest, L"DRM (copy protected) file", destlen);
- return 1;
- }
- }
- else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
- {
- getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
- return 1;
- }
- else if (KeywordMatch(data, "bitrate"))
- {
- StringCchPrintfW(dest, destlen, L"%u", getExtendedFileInfo->GetBitrate() / 1000);
- return 1;
- }
- else if (KeywordMatch(data, "vbr"))
- {
- if (getExtendedFileInfo->IsAttribute(g_wszWMIsVBR))
- StringCchCopyW(dest, destlen, L"1");
- else if (getExtendedFileInfo->IsNotAttribute(g_wszWMIsVBR))
- StringCchCopyW(dest, destlen, L"0");
- return 1;
- }
- //else if (KeywordMatch(data, "srate"))
- else if (KeywordMatch(data, "length"))
- {
- long length = getExtendedFileInfo->GetLengthMilliseconds();
- if (length == -1000)
- return 0;
- _itow(length, dest, 10);
- return 1;
- }
- else if (KeywordMatch(data, "rating"))
- {
- wchar_t rating_string[128] = {0};
- getExtendedFileInfo->GetAttribute(L"WM/SharedUserRating", rating_string, 128);
- int rating = _wtoi(rating_string);
- if (rating == 0)
- dest[0]=0;
- else if (rating >= 1 && rating <= 12)
- dest[0]=L'1';
- else if (rating >= 13 && rating <= 37)
- dest[0]=L'2';
- else if (rating >= 38 && rating <= 62)
- dest[0]=L'3';
- else if (rating >= 63 && rating <= 86)
- dest[0]=L'4';
- else
- dest[0]=L'5';
- dest[1]=0;
- return 1;
- }
- else if (KeywordMatch(data, "replaygain_track_gain")
- || KeywordMatch(data, "replaygain_track_peak")
- || KeywordMatch(data, "replaygain_album_gain")
- || KeywordMatch(data, "replaygain_album_peak"))
- {
- getExtendedFileInfo->GetAttribute(tagName, dest, destlen);
- return 1;
- }
- else if (KeywordMatch(data, "gain"))
- {
- StringCchPrintfW(dest, destlen, L"%-+.2f dB", (float)log10f(GetGain(getExtendedFileInfo, false))*20.0f);
- return 1;
- }
- else if (KeywordMatch(data, "audiocodec"))
- {
- if (!getExtendedFileInfo->GetCodecName(dest, destlen))
- dest[0]=0;
- return 1;
- }
- else if (KeywordMatch(data, "lossless"))
- {
- wchar_t codecname[1024] = {0};
- if (!getExtendedFileInfo->GetCodecName(codecname, 1024))
- dest[0]=0;
- else
- {
- dest[0] = wcsstr(codecname, L"Lossless")?'1':'0';
- dest[1]=0;
- }
- return 1;
- }
- else if (KeywordMatch(data, "GracenoteFileID"))
- {
- getExtendedFileInfo->GetAttribute_BinString(L"GN/UniqueFileIdentifier", dest, destlen);
- return 1;
- }
- else if (KeywordMatch(data, "GracenoteExtData"))
- {
- getExtendedFileInfo->GetAttribute_BinString(L"GN/ExtData", dest, destlen);
- return 1;
- }
- else if (KeywordMatch(data, "formatinformation"))
- {
- // this is a bit of a clusterfuck, but it's safe and (hopefully) logically laid out.
- wchar_t codec[128]=L"", duration[64]=L"", bitrate[64]=L"", filesize[64]=L"", wmver[64]=L"", seekable[64]=L"";
- wchar_t stridable[64]=L"", broadcast[64]=L"", protect[64]=L"", trusted[64]=L"", contents[64]=L"";
- wchar_t buf[128]=L""; // temporary buffer
- // get the codec name string
- if (getExtendedFileInfo->GetCodecName(buf, 128) && buf[0])
- StringCchPrintf(codec,128,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_CODEC),buf);
- // get the length string formatted h:mm:ss.tttt
- long t = getExtendedFileInfo->GetLengthMilliseconds();
- if (t)
- {
- long h = t/36000000;
- long m = (t/60000)%60;
- long s = (t/1000)%60;
- long ms = t%1000;
- if (h)
- StringCchPrintf(duration,64,L"%s: %u:%02u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),h,m,s,ms);
- else if (m)
- StringCchPrintf(duration,64,L"%s: %u:%02u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),m,s,ms);
- else
- StringCchPrintf(duration,64,L"%s: %u.%03u\n",WASABI_API_LNGSTRINGW(IDS_DURATION),s,ms);
- }
- // get the bitrate string formatted 128.235 kbps
- long br = getExtendedFileInfo->GetBitrate();
- wchar_t kbps[16] = {0};
- StringCchPrintf(bitrate,64,L"%s: %.*f %s\n",
- WASABI_API_LNGSTRINGW(IDS_BITRATE),
- Width(br%1000),
- br/1000.0,
- WASABI_API_LNGSTRINGW_BUF(IDS_KBPS,kbps,16));
- // get the filesize string, with commas grouping in threes
- buf[0]=0;
- getExtendedFileInfo->GetAttribute(L"FileSize",buf,64);
- uint64_t fs = _wcstoui64(buf, 0, 10);
- if (fs)
- {
- uint64_t fsgb = (fs/1000000000LL);
- uint64_t fsmb = (fs/1000000LL)%1000LL;
- uint64_t fskb = (fs/1000LL)%1000LL;
- uint64_t fsb = fs%1000LL;
- if (fsgb)
- StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsgb,fsmb,fskb,fsb);
- else if (fsmb)
- StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsmb,fskb,fsb);
- else if (fskb)
- StringCchPrintf(filesize,64,L"%s: %I64u,%03I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fskb,fsb);
- else
- StringCchPrintf(filesize,64,L"%s: %I64u\n",WASABI_API_LNGSTRINGW(IDS_FILESIZE),fsb);
- }
- // 4 boolean flags, compose their strings
- wchar_t yes[64] = {0}, no[64] = {0};
- WASABI_API_LNGSTRINGW_BUF(IDS_YES,yes,64);
- WASABI_API_LNGSTRINGW_BUF(IDS_NO,no,64);
- buf[0]=0;
- getExtendedFileInfo->GetAttribute(L"WMFSDKVersion",buf,128);
- if (buf[0])
- StringCchPrintf(wmver,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_WMVER),buf);
- StringCchPrintf(seekable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_SEEKABLE),getExtendedFileInfo->IsAttribute(L"Seekable")?yes:no);
- StringCchPrintf(stridable,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_STRIDABLE),getExtendedFileInfo->IsAttribute(L"Stridable")?yes:no);
- StringCchPrintf(broadcast,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_BROADCAST),getExtendedFileInfo->IsAttribute(L"Broadcast")?yes:no);
- StringCchPrintf(protect,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_PROTECTED),getExtendedFileInfo->IsAttribute(L"Is_Protected")?yes:no);
- StringCchPrintf(trusted,64,L"%s: %s\n",WASABI_API_LNGSTRINGW(IDS_TRUSTED),getExtendedFileInfo->IsAttribute(L"Is_Trusted")?yes:no);
- // file contents. bit gross i know.
- wchar_t cont[4][16]={L"",L"",L"",L""};
- int i=0;
- if (getExtendedFileInfo->IsAttribute(L"HasAudio"))
- WASABI_API_LNGSTRINGW_BUF(IDS_AUDIO,cont[i++],16);
- if (getExtendedFileInfo->IsAttribute(L"HasVideo"))
- WASABI_API_LNGSTRINGW_BUF(IDS_VIDEO,cont[i++],16);
- if (getExtendedFileInfo->IsAttribute(L"HasImage"))
- WASABI_API_LNGSTRINGW_BUF(IDS_IMAGE,cont[i++],16);
- if (getExtendedFileInfo->IsAttribute(L"HasScript"))
- WASABI_API_LNGSTRINGW_BUF(IDS_SCRIPT,cont[i++],16);
- WASABI_API_LNGSTRINGW_BUF(IDS_NONE,buf,64);
- if (i == 0)
- StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), buf);
- else if (i == 1)
- StringCchPrintf(contents,64,L"%s: %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0]);
- else if (i == 2)
- StringCchPrintf(contents,64,L"%s: %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1]);
- else if (i == 3)
- StringCchPrintf(contents,64,L"%s: %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2]);
- else if (i == 4)
- StringCchPrintf(contents,64,L"%s: %s, %s, %s, %s",WASABI_API_LNGSTRINGW(IDS_CONTAINS), cont[0], cont[1], cont[2], cont[3]);
- // compose our string together!
- 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);
- }
- else
- return 0;
- return 1;
- }
- #if 0 // had to disable this because it was locking the file from being deleted
- WMInformation *lastGetInfo = 0;
- wchar_t *lastGetInfoFn;
- #endif
- extern "C" __declspec(dllexport)
- int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, int destlen)
- {
- /* Check if there's a status message for this filename
- doing this forces Winamp to hit plugin.getfileinfo, which gives us better control
- over adding things like [Individualizing] to the playlist title for local files
- */
- if (winamp.HasStatus(fn))
- return 0;
- if ((!fn || !*fn) && KeywordMatch(data, "type"))
- {
- if (config_no_video)
- lstrcpyn(dest, L"0", destlen);
- else
- lstrcpyn(dest, L"1", destlen);
- return 1;
- }
-
- if (KeywordMatch(data, "mime"))
- {
- int len;
- const wchar_t *p;
- if (!fn || !fn[0]) return 0;
- len = lstrlenW(fn);
- if (len < 4 || L'.' != fn[len - 4]) return 0;
- p = &fn[len - 3];
- if (!_wcsicmp(p, L"WMA")) { StringCchCopyW(dest, destlen, L"audio/x-ms-wma"); return 1; }
- if (!_wcsicmp(p, L"WMV")) { StringCchCopyW(dest, destlen, L"video/x-ms-wmv"); return 1; }
- if (!_wcsicmp(p, L"ASF")) { StringCchCopyW(dest, destlen, L"video/x-ms-asf"); return 1; }
-
- return 0;
- }
- if (KeywordMatch(data, "family"))
- {
- LPCWSTR e, p(NULL);
- DWORD lcid;
- size_t i;
- int len2(0);
- if (!fn || !*fn) return 0;
- e = PathFindExtensionW(fn);
- if (L'.' != *e) return 0;
- e++;
- if (!*e) return 0;
- lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
-
- for (i = 0; i < fileTypes.types.size() && !p; i++)
- {
- if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, e, -1, fileTypes.types.at(i).wtype, -1))
- p = fileTypes.types.at(i).description;
- }
- if (p)
- {
- wchar_t szTest[16];
- if (S_OK == StringCchPrintfW(szTest, sizeof(szTest)/sizeof(wchar_t), L" (*.%s)", e))
- {
- int len1 = lstrlenW(szTest);
- len2 = lstrlenW(p);
- if (len2 > len1 && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, szTest, -1, (p + len2 - len1), -1))
- len2 -= len1;
- }
- }
- return (p && S_OK == StringCchCopyNW(dest, destlen, p, len2));
- }
- if (!config_http_metadata && PathIsURL(fn))
- return 0;
- if (KeywordMatch(data, "type") && !PathFileExistsW(fn))
- {
- switch (fileTypes.GetAVType(extension(fn)))
- {
- case FileType::AUDIO:
- lstrcpyn(dest, L"0", destlen);
- return 1;
- case FileType::VIDEO:
- lstrcpyn(dest, L"1", destlen);
- return 1;
- default:
- return 0;
- }
- }
-
- if (activePlaylist.IsMe(fn))
- {
- WMInformation getExtendedFileInfo(activePlaylist.GetFileName());
- return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
- }
- else
- {
- WMInformation getExtendedFileInfo(fn);
- return GetExtendedInformation(&getExtendedFileInfo, fn, data, dest, destlen);
- }
- #if 0 // had to disable this because it was locking the file from being deleted
- if (lastGetInfo && lastGetInfoFn && !_wcsicmp(fn, lastGetInfoFn))
- {
- return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
- }
- delete lastGetInfo;
- lastGetInfo=0;
- free(lastGetInfoFn);
- lastGetInfoFn=0;
- if (activePlaylist.IsMe(fn))
- lastGetInfoFn = _wcsdup(activePlaylist.GetFileName());
- else
- lastGetInfoFn = _wcsdup(fn);
- lastGetInfo = new WMInformation(lastGetInfoFn);
- if (lastGetInfo->ErrorOpening())
- {
- if (KeywordMatch(data, "type"))
- {
- switch (fileTypes.GetAVType(extension(fn)))
- {
- case FileType::AUDIO:
- lstrcpyn(dest, L"0", destlen);
- return 1;
- case FileType::VIDEO:
- lstrcpyn(dest, L"1", destlen);
- return 1;
- default:
- return 0;
- }
- }
- delete lastGetInfo;
- lastGetInfo=0;
- free(lastGetInfoFn);
- lastGetInfoFn=0;
- return 0;
- }
- return GetExtendedInformation(lastGetInfo, fn, data, dest, destlen);
- #endif
- }
- extern "C" __declspec(dllexport) int winampClearExtendedFileInfoW(const wchar_t *fn)
- {
- // TODO: press stop if it's the currently playing file
- WMInformation wmInfo(fn);
- wmInfo.ClearAllAttributes();
- wmInfo.Flush();
- return 1;
- }
- extern "C" __declspec(dllexport) int winampSetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *val)
- {
- // if (!lastSetInfoFilename.empty() && lastSetInfoFilename != fn)
- // dosomething();
- #if 0 // had to disable this because it was locking the file from being deleted
- if (lastGetInfoFn && !_wcsicmp(lastGetInfoFn,fn))
- {
- delete lastGetInfo;
- lastGetInfo=0;
- free(lastGetInfoFn);
- lastGetInfoFn=0;
- }
- #endif
- if (!setFileInfo)
- {
- if (activePlaylist.IsMe(fn) && mod.playing)
- {
- forcedStop = true;
- outTime = mod.GetOutputTime();
- winamp.PressStop();
- }
- free(setFileInfoName);
- setFileInfoName = _wcsdup(fn);
- setFileInfo = new WMInformation(fn);
- if (!setFileInfo->MakeWritable(fn))
- return 0; // can't write
- }
- AutoWide tagNameW(data);
- const wchar_t *tagName = GetAlias(tagNameW);
- if (StartsWith(tagName, L"WM/"))
- {
- setFileInfo->SetAttribute(tagName, val);
- return 1;
- }
- else if ((const wchar_t *)tagNameW != tagName) // if the tag was in the tag list
- {
- setFileInfo->SetAttribute(tagName, val);
- return 1;
- }
- else if (KeywordMatch(data, "rating"))
- {
- int rating = _wtoi(val);
- if (rating == 0)
- setFileInfo->SetAttribute(L"WM/SharedUserRating", L"",WMT_TYPE_DWORD);
- else
- {
- switch(rating)
- {
- case 1:
- setFileInfo->SetAttribute(L"WM/SharedUserRating", L"1",WMT_TYPE_DWORD);
- break;
- case 2:
- setFileInfo->SetAttribute(L"WM/SharedUserRating", L"25",WMT_TYPE_DWORD);
- break;
- case 3:
- setFileInfo->SetAttribute(L"WM/SharedUserRating", L"50",WMT_TYPE_DWORD);
- break;
- case 4:
- setFileInfo->SetAttribute(L"WM/SharedUserRating", L"75",WMT_TYPE_DWORD);
- break;
- default:
- setFileInfo->SetAttribute(L"WM/SharedUserRating", L"99",WMT_TYPE_DWORD);
- break;
- }
- }
- }
- else if (KeywordMatch(data, "replaygain_track_gain")
- || KeywordMatch(data, "replaygain_track_peak")
- || KeywordMatch(data, "replaygain_album_gain")
- || KeywordMatch(data, "replaygain_album_peak"))
- {
- setFileInfo->SetAttribute(tagName, val);
- return 1;
- }
- else if (KeywordMatch(data, "GracenoteFileID"))
- {
- setFileInfo->SetAttribute_BinString(L"GN/UniqueFileIdentifier", val);
- return 1;
- }
- else if (KeywordMatch(data, "GracenoteExtData"))
- {
- setFileInfo->SetAttribute_BinString(L"GN/ExtData", val);
- return 1;
- }
- // else if (KeywordMatch(data, "bitrate"))
- //else if (KeywordMatch(data, "disc"))
- // else if (KeywordMatch(data, "vbr"))
- //else if (KeywordMatch(data, "srate"))
- // else if (KeywordMatch(data, "length"))
- return 0;
- }
- extern "C" __declspec(dllexport) int winampWriteExtendedFileInfo()
- {
- if (setFileInfo)
- {
- bool flushOK = setFileInfo->Flush();
- delete setFileInfo;
- setFileInfo = 0;
- if (forcedStop)
- {
- mod.startAtMilliseconds = outTime;
- winamp.PressPlay();
- }
- forcedStop=false;
- if (flushOK)
- return 1;
- else
- return 0;
- }
- return 0;
- }
|