formatfilename.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. #include "main.h"
  2. #include "../nu/ns_wc.h"
  3. #include "../winamp/wa_ipc.h"
  4. #include <shlwapi.h>
  5. #include <strsafe.h>
  6. #define TID_UNKNOWN 0
  7. #define TID_ARTIST 1
  8. #define TID_ALBUM 2
  9. #define TID_TITLE 3
  10. #define TID_GENRE 4
  11. #define TID_YEAR 5
  12. #define TID_TRACKARTIST 6
  13. #define TID_FILENAME 7
  14. #define TID_EXTENSION 8
  15. #define TID_DISC 9
  16. #define TID_DISCS 10
  17. #define TOKEN_ARTIST TEXT("<artist>")
  18. #define TOKEN_ALBUM TEXT("<album>")
  19. #define TOKEN_TITLE TEXT("<title>")
  20. #define TOKEN_GENRE TEXT("<genre>")
  21. #define TOKEN_YEAR TEXT("<year>")
  22. #define TOKEN_TRACKARTIST TEXT("<trackartist>")
  23. #define TOKEN_FILENAME TEXT("<filename>")
  24. #define TOKEN_EXTENSION TEXT("<extension>")
  25. #define TOKEN_DISC TEXT("<disc>")
  26. #define TOKEN_DISCS TEXT("<discs>")
  27. #define TOKEN_LEN_ARTIST 8
  28. #define TOKEN_LEN_ALBUM 7
  29. #define TOKEN_LEN_TITLE 7
  30. #define TOKEN_LEN_GENRE 7
  31. #define TOKEN_LEN_YEAR 6
  32. #define TOKEN_LEN_TRACKARTIST 13
  33. #define TOKEN_LEN_FILENAME 10
  34. #define TOKEN_LEN_EXTENSION 11
  35. #define TOKEN_LEN_DISC 6
  36. #define TOKEN_LEN_DISCS 7
  37. #define STRCOMP_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT)
  38. #define CHECK_TOKEN(token, format) (CSTR_EQUAL == CompareString(STRCOMP_INVARIANT, NORM_IGNORECASE, TOKEN_##token##, TOKEN_LEN_##token##, format, TOKEN_LEN_##token##))
  39. static TCHAR PathValidateChar(TCHAR cVal, BOOL bAllowBackSlash)
  40. {
  41. switch(cVal)
  42. {
  43. case TEXT('\\'): if (!bAllowBackSlash) return TEXT('-');
  44. break;
  45. case TEXT('/'): if (!bAllowBackSlash) return TEXT('-');
  46. return TEXT('\\');
  47. case TEXT(':'): return TEXT('-');
  48. case TEXT('*'): return TEXT('_');
  49. case TEXT('?'): return TEXT('_');
  50. case TEXT('\"'): return TEXT('\'');
  51. case TEXT('<'): return TEXT('(');
  52. case TEXT('>'): return TEXT(')');
  53. case TEXT('|'): return TEXT('_');
  54. }
  55. return cVal;
  56. }
  57. void CleanupDirectoryString(LPTSTR pszDirectory)
  58. {
  59. PathUnquoteSpaces(pszDirectory);
  60. PathRemoveBlanks(pszDirectory);
  61. LPTSTR pc = pszDirectory;
  62. while (TEXT('\0') != *pc) { if (TEXT('/') == *pc) *pc = TEXT('\\'); pc++; }
  63. if (pc > pszDirectory)
  64. {
  65. pc--;
  66. while (pszDirectory != pc &&
  67. (TEXT('.') == *pc || TEXT(' ') == *pc || TEXT('\\') == *pc))
  68. {
  69. *pc = TEXT('\0');
  70. pc--;
  71. }
  72. }
  73. }
  74. LPWSTR GetExtensionString(LPWSTR pszBuffer, INT cchBufferMax, DWORD fourcc)
  75. {
  76. char configExt[10] = { '\0', };
  77. pszBuffer[0] = L'\0';
  78. convertConfigItem cfi;
  79. cfi.configfile = 0;
  80. cfi.data = configExt;
  81. cfi.format = fourcc;
  82. cfi.item = "extension";
  83. cfi.len = 10;
  84. SENDWAIPC(plugin.hwndWinampParent, IPC_CONVERT_CONFIG_GET_ITEM, (WPARAM)&cfi);
  85. if ('\0' != *configExt)
  86. {
  87. if (!MultiByteToWideCharSZ(CP_ACP, 0, configExt, -1, pszBuffer, cchBufferMax))
  88. return NULL;
  89. }
  90. else
  91. {
  92. if (cchBufferMax < 5) return NULL;
  93. pszBuffer[0] = (TCHAR)((fourcc) & 0xff);
  94. pszBuffer[1] = (TCHAR)((fourcc >> 8) & 0xff);
  95. pszBuffer[2] = (TCHAR)((fourcc >> 16) & 0xff);
  96. pszBuffer[3] = TEXT('\0');
  97. for (LPTSTR p = &pszBuffer[2]; p >= pszBuffer && TEXT(' ') == *p; p--) *p = TEXT('\0');
  98. }
  99. return pszBuffer;
  100. }
  101. // if trackno is 0xdeadbeef, or title is 0, they are both ignored
  102. // TODO: use ATF instead
  103. HRESULT FormatFileName(LPTSTR pszTextOut, INT cchTextMax, LPCTSTR pszFormat,
  104. INT nTrackNo, LPCTSTR pszArtist,
  105. LPCTSTR pszAlbum, LPCTSTR pszTitle,
  106. LPCTSTR pszGenre, LPCTSTR pszYear,
  107. LPCTSTR pszTrackArtist,
  108. LPCTSTR pszFileName, LPCTSTR pszDisc)
  109. {
  110. HRESULT hr = S_OK;
  111. TCHAR szBuffer[MAX_PATH] = {0};
  112. LPTSTR pszStart = pszTextOut + lstrlen(pszTextOut);
  113. while (pszFormat && TEXT('\0') != *pszFormat)
  114. {
  115. int whichstr = TID_UNKNOWN;
  116. if (*pszFormat == TEXT('#') && nTrackNo != 0xdeadbeef)
  117. {
  118. int cnt = 0;
  119. while (pszFormat && *pszFormat == TEXT('#')) { pszFormat++; cnt++; }
  120. if (cnt > 8) cnt = 8;
  121. TCHAR szFormat[32] = {0};
  122. hr = StringCchPrintf(szFormat, ARRAYSIZE(szFormat), TEXT("%%%02dd"), cnt);
  123. if (S_OK == hr) StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), szFormat, nTrackNo);
  124. if (S_OK == hr) StringCchCat(pszTextOut, cchTextMax, szBuffer);
  125. if (S_OK != hr) return hr;
  126. }
  127. else if (pszArtist && CHECK_TOKEN(ARTIST, pszFormat)) whichstr = TID_ARTIST;
  128. else if (pszAlbum && CHECK_TOKEN(ALBUM, pszFormat)) whichstr = TID_ALBUM;
  129. else if (pszTitle && CHECK_TOKEN(TITLE, pszFormat)) whichstr = TID_TITLE;
  130. else if (pszGenre && CHECK_TOKEN(GENRE, pszFormat)) whichstr = TID_GENRE;
  131. else if (pszYear && CHECK_TOKEN(YEAR, pszFormat)) whichstr = TID_YEAR;
  132. else if (pszTrackArtist && CHECK_TOKEN(TRACKARTIST, pszFormat)) whichstr = TID_TRACKARTIST;
  133. else if (pszFileName && CHECK_TOKEN(FILENAME, pszFormat)) whichstr = TID_FILENAME;
  134. else if (pszFileName && CHECK_TOKEN(EXTENSION, pszFormat)) whichstr = TID_EXTENSION;
  135. else if (pszDisc && CHECK_TOKEN(DISC, pszFormat)) whichstr = TID_DISC;
  136. else if (pszDisc && CHECK_TOKEN(DISCS, pszFormat)) whichstr = TID_DISCS;
  137. else
  138. {
  139. INT l = lstrlen(pszTextOut);
  140. pszTextOut += l;
  141. cchTextMax -= l;
  142. pszTextOut[0] = *pszFormat++;
  143. if (cchTextMax < 2) return STRSAFE_E_INSUFFICIENT_BUFFER;
  144. pszTextOut[0] = PathValidateChar(pszTextOut[0], TRUE);
  145. if (TEXT('\\') == *pszTextOut)
  146. {
  147. // remove end spaces and dots
  148. while (pszTextOut > pszStart &&
  149. (TEXT('\\') == *(pszTextOut - 1) || TEXT(' ') == *(pszTextOut - 1) || TEXT('.') == *(pszTextOut - 1)))
  150. {
  151. pszTextOut--;
  152. cchTextMax--;
  153. }
  154. if (pszTextOut == pszStart)
  155. {
  156. pszTextOut--;
  157. cchTextMax--;
  158. }
  159. else *pszTextOut = TEXT('\\');
  160. }
  161. pszTextOut++;
  162. cchTextMax--;
  163. *pszTextOut = TEXT('\0');
  164. if (S_OK != hr) return hr;
  165. }
  166. if (whichstr != TID_UNKNOWN)
  167. {
  168. LPCTSTR pszSrc = NULL;
  169. int islow = IsCharLower(pszFormat[1]) && IsCharLower(pszFormat[2]);
  170. int ishi = IsCharUpper(pszFormat[1]) && IsCharUpper(pszFormat[2]);
  171. switch(whichstr)
  172. {
  173. case TID_ARTIST: pszSrc = pszArtist; pszFormat += TOKEN_LEN_ARTIST; break;
  174. case TID_ALBUM: pszSrc = pszAlbum; pszFormat += TOKEN_LEN_ALBUM; break;
  175. case TID_TITLE: pszSrc = pszTitle; pszFormat += TOKEN_LEN_TITLE; break;
  176. case TID_GENRE: pszSrc = pszGenre; pszFormat += TOKEN_LEN_GENRE; break;
  177. case TID_YEAR: pszSrc = pszYear; pszFormat += TOKEN_LEN_YEAR; break;
  178. case TID_TRACKARTIST: pszSrc = pszTrackArtist; pszFormat += TOKEN_LEN_TRACKARTIST; break;
  179. case TID_DISC:
  180. if (pszDisc && *pszDisc && TEXT('\0') == *pszDisc) pszSrc = L"1";
  181. else
  182. {
  183. // default to 1 when we've not got a proper value passed to us
  184. int disc = _wtoi(pszDisc);
  185. if(disc <= 0) disc = 1;
  186. StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%d", disc);
  187. pszSrc = szBuffer;
  188. }
  189. pszFormat += TOKEN_LEN_DISC;
  190. break;
  191. case TID_DISCS:
  192. if (pszDisc && *pszDisc && TEXT('\0') == *pszDisc) pszSrc = L"1";
  193. else
  194. {
  195. LPTSTR pszTemp = wcschr((LPTSTR)pszDisc, L'/');
  196. if(pszTemp == NULL) pszTemp = wcschr((LPTSTR)pszDisc, L'\\');
  197. if(pszTemp == NULL)
  198. {
  199. pszTemp = (LPTSTR)pszDisc;
  200. }
  201. else
  202. {
  203. pszTemp = CharNext(pszTemp);
  204. }
  205. int disc = _wtoi(pszTemp);
  206. if(disc <= 0) disc = 1;
  207. StringCchPrintf(szBuffer, ARRAYSIZE(szBuffer), L"%d", disc);
  208. pszSrc = szBuffer;
  209. }
  210. pszFormat += TOKEN_LEN_DISCS;
  211. break;
  212. case TID_FILENAME:
  213. pszSrc = PathFindExtension(pszFileName);
  214. if (TEXT('\0') == *pszSrc) pszSrc = pszFileName;
  215. else
  216. {
  217. StringCchCopy(szBuffer, ARRAYSIZE(szBuffer), pszFileName);
  218. PathRemoveExtension(szBuffer);
  219. pszSrc = szBuffer;
  220. }
  221. pszFormat += TOKEN_LEN_FILENAME;
  222. break;
  223. case TID_EXTENSION:
  224. pszSrc = PathFindExtension(pszFileName); pszFormat += TOKEN_LEN_EXTENSION;
  225. while (pszTextOut > pszStart && TEXT('.') == *(pszTextOut - 1))
  226. {
  227. pszTextOut--;
  228. *pszTextOut = TEXT('\0');
  229. cchTextMax++;
  230. }
  231. break;
  232. }
  233. INT l = lstrlen(pszTextOut);
  234. pszTextOut += l;
  235. cchTextMax -= l;
  236. while (pszSrc && TEXT('\0') != *pszSrc && cchTextMax > 0)
  237. {
  238. pszTextOut[0] = pszSrc[0];
  239. if (ishi) CharUpperBuffW(pszTextOut, 1);
  240. else if (islow) CharLowerBuffW(pszTextOut, 1);
  241. pszTextOut[0] = PathValidateChar(pszTextOut[0], FALSE);
  242. cchTextMax--;
  243. if (cchTextMax) pszTextOut++;
  244. pszSrc++;
  245. }
  246. pszTextOut[0] = TEXT('\0');
  247. if (0 == cchTextMax) return STRSAFE_E_INSUFFICIENT_BUFFER;
  248. }
  249. }
  250. return hr;
  251. }