48b0879ff6027cef158d83703f9dd61d7e10ea58
[platform/upstream/flac.git] / src / plugin_winamp2 / infobox.c
1 /* in_flac - Winamp2 FLAC input plugin\r
2  * Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009  Josh Coalson\r
3  *\r
4  * This library is free software; you can redistribute it and/or\r
5  * modify it under the terms of the GNU Lesser General Public\r
6  * License as published by the Free Software Foundation; either\r
7  * version 2.1 of the License, or (at your option) any later version.\r
8  *\r
9  * This library is distributed in the hope that it will be useful,\r
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
12  * Lesser General Public License for more details.\r
13  *\r
14  * You should have received a copy of the GNU Lesser General Public\r
15  * License along with this library; if not, write to the Free Software\r
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA\r
17  */\r
18 \r
19 #if HAVE_CONFIG_H\r
20 #  include <config.h>\r
21 #endif\r
22 \r
23 #include <windows.h>\r
24 #include <stdio.h>\r
25 #include "FLAC/all.h"\r
26 #include "share/alloc.h"\r
27 #include "plugin_common/all.h"\r
28 #include "infobox.h"\r
29 #include "configure.h"\r
30 #include "resource.h"\r
31 \r
32 \r
33 typedef struct\r
34 {\r
35         char filename[MAX_PATH];\r
36         FLAC__StreamMetadata *tags;\r
37 } LOCALDATA;\r
38 \r
39 static char buffer[8192];\r
40 static char *genres = NULL;\r
41 static DWORD genresSize = 0, genresCount = 0;\r
42 static BOOL genresChanged = FALSE, isNT;\r
43 \r
44 static const char infoTitle[] = "FLAC File Info";\r
45 \r
46 /*\r
47  *  Genres\r
48  */\r
49 \r
50 /* TODO: write genres in utf-8 ? */\r
51 \r
52 static __inline int GetGenresFileName(char *buffer, int size)\r
53 {\r
54         char *c;\r
55 \r
56         if (!GetModuleFileName(NULL, buffer, size))\r
57                 return 0;\r
58         c = strrchr(buffer, '\\');\r
59         if (!c) return 0;\r
60         strcpy(c+1, "genres.txt");\r
61 \r
62         return 1;\r
63 }\r
64 \r
65 static void LoadGenres()\r
66 {\r
67         HANDLE hFile;\r
68         DWORD  spam;\r
69         char  *c;\r
70 \r
71         FLAC__ASSERT(0 != genres);\r
72 \r
73         if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
74         /* load file */\r
75         hFile = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
76         if (hFile == INVALID_HANDLE_VALUE) return;\r
77         genresSize = GetFileSize(hFile, 0);\r
78         if (genresSize && (genres = (char*)safe_malloc_add_2op_(genresSize, /*+*/2)))\r
79         {\r
80                 if (!ReadFile(hFile, genres, genresSize, &spam, NULL) || spam!=genresSize)\r
81                 {\r
82                         free(genres);\r
83                         genres = NULL;\r
84                 }\r
85                 else\r
86                 {\r
87                         genres[genresSize] = 0;\r
88                         genres[genresSize+1] = 0;\r
89                         /* replace newlines */\r
90                         genresChanged = FALSE;\r
91                         genresCount = 1;\r
92 \r
93                         for (c=genres; *c; c++)\r
94                         {\r
95                                 if (*c == 10)\r
96                                 {\r
97                                         *c = 0;\r
98                                         if (*(c+1))\r
99                                                 genresCount++;\r
100                                         else genresSize--;\r
101                                 }\r
102                         }\r
103                 }\r
104         }\r
105 \r
106         CloseHandle(hFile);\r
107 }\r
108 \r
109 static void SaveGenres(HWND hlist)\r
110 {\r
111         HANDLE hFile;\r
112         DWORD  spam;\r
113         int i, count, len;\r
114 \r
115         if (!GetGenresFileName(buffer, sizeof(buffer))) return;\r
116         /* write file */\r
117         hFile = CreateFile(buffer, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);\r
118         if (hFile == INVALID_HANDLE_VALUE) return;\r
119 \r
120         count = SendMessage(hlist, CB_GETCOUNT, 0, 0);\r
121         for (i=0; i<count; i++)\r
122         {\r
123                 SendMessage(hlist, CB_GETLBTEXT, i, (LPARAM)buffer);\r
124                 len = strlen(buffer);\r
125                 if (i != count-1)\r
126                 {\r
127                         buffer[len] = 10;\r
128                         len++;\r
129                 }\r
130                 WriteFile(hFile, buffer, len, &spam, NULL);\r
131         }\r
132 \r
133         CloseHandle(hFile);\r
134 }\r
135 \r
136 static void AddGenre(HWND hwnd, const char *genre)\r
137 {\r
138         HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
139 \r
140         if (SendMessage(hgen, CB_FINDSTRINGEXACT, -1, (LPARAM)genre) == CB_ERR)\r
141         {\r
142                 genresChanged = TRUE;\r
143                 SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)genre);\r
144         }\r
145 }\r
146 \r
147 static void InitGenres(HWND hwnd)\r
148 {\r
149         HWND hgen = GetDlgItem(hwnd, IDC_GENRE);\r
150         char *c;\r
151 \r
152         /* set text length limit to 64 chars */\r
153         SendMessage(hgen, CB_LIMITTEXT, 64, 0);\r
154         /* try to load genres */\r
155         if (!genres)\r
156                 LoadGenres(hgen);\r
157         /* add the to list */\r
158         if (genres)\r
159         {\r
160                 SendMessage(hgen, CB_INITSTORAGE, genresCount, genresSize);\r
161 \r
162                 for (c = genres; *c; c += strlen(c)+1)\r
163                         SendMessage(hgen, CB_ADDSTRING, 0, (LPARAM)c);\r
164         }\r
165 }\r
166 \r
167 static void DeinitGenres(HWND hwnd, BOOL final)\r
168 {\r
169         if (genresChanged && hwnd)\r
170         {\r
171                 SaveGenres(GetDlgItem(hwnd, IDC_GENRE));\r
172                 genresChanged = FALSE;\r
173                 final = TRUE;\r
174         }\r
175         if (final)\r
176         {\r
177                 free(genres);\r
178                 genres = 0;\r
179         }\r
180 }\r
181 \r
182 static wchar_t *AnsiToWide(const char *src)\r
183 {\r
184         int len;\r
185         wchar_t *dest;\r
186 \r
187         FLAC__ASSERT(0 != src);\r
188 \r
189         len = strlen(src) + 1;\r
190         /* copy */\r
191         dest = (wchar_t*)safe_malloc_mul_2op_(len, /*times*/sizeof(wchar_t));\r
192         if (dest) mbstowcs(dest, src, len);\r
193         return dest;\r
194 }\r
195 \r
196 /*\r
197  *  Infobox helpers\r
198  */\r
199 \r
200 #define SetText(x,y)            ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, y); \\r
201                                 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, ucs2, -1, buffer, sizeof(buffer), NULL, NULL); \\r
202                                 if(ucs2) free(ucs2); \\r
203                                 SetDlgItemText(hwnd, x, buffer)\r
204 \r
205 #define GetText(x,y)            GetDlgItemText(hwnd, x, buffer, sizeof(buffer));                        \\r
206                                 if (*buffer) { ucs2 = AnsiToWide(buffer); FLAC_plugin__tags_set_tag_ucs2(data->tags, y, ucs2, /*replace_all=*/false); free(ucs2); } \\r
207                                 else FLAC_plugin__tags_delete_tag(data->tags, y)\r
208 \r
209 #define SetTextW(x,y)           ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, y); \\r
210                                 SetDlgItemTextW(hwnd, x, ucs2); \\r
211                                 free(ucs2)\r
212 \r
213 #define GetTextW(x,y)           GetDlgItemTextW(hwnd, x, (WCHAR*)buffer, sizeof(buffer)/2);                     \\r
214                                 if (*(WCHAR*)buffer) FLAC_plugin__tags_set_tag_ucs2(data->tags, y, (WCHAR*)buffer, /*replace_all=*/false); \\r
215                                 else FLAC_plugin__tags_delete_tag(data->tags, y)\r
216 \r
217 \r
218 static BOOL InitInfoboxInfo(HWND hwnd, const char *file)\r
219 {\r
220         LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
221         wchar_t *ucs2;\r
222         FLAC__StreamMetadata streaminfo;\r
223         DWORD    length, bps, ratio, rg;\r
224         LONGLONG filesize;\r
225 \r
226         SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
227         /* file name */\r
228         strncpy(data->filename, file, sizeof(data->filename));\r
229         SetDlgItemText(hwnd, IDC_NAME, file);\r
230         /* stream data and vorbis comment */\r
231         filesize = FileSize(file);\r
232         if (!filesize) return FALSE;\r
233         if (!FLAC__metadata_get_streaminfo(file, &streaminfo))\r
234                 return FALSE;\r
235         ReadTags(file, &data->tags, false);\r
236 \r
237         length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);\r
238         bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate));\r
239         ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample);\r
240         rg  = FLAC_plugin__tags_get_tag_utf8(data->tags, "REPLAYGAIN_TRACK_GAIN") ? 1 : 0;\r
241         rg |= FLAC_plugin__tags_get_tag_utf8(data->tags, "REPLAYGAIN_ALBUM_GAIN") ? 2 : 0;\r
242 \r
243         sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n"\r
244                         "File size: %I64d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n"\r
245                         "ReplayGain: %s\n",\r
246             streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample,\r
247             streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples,\r
248             length/60, length%60, bps, ratio/10, ratio%10,\r
249             rg==3 ? "track gain\nReplayGain: album gain" : rg==2 ? "album gain" : rg==1 ? "track gain" : "not present");\r
250 \r
251         SetDlgItemText(hwnd, IDC_INFO, buffer);\r
252         /* tag */\r
253         if (isNT)\r
254         {\r
255                 SetTextW(IDC_TITLE,   "TITLE");\r
256                 SetTextW(IDC_ARTIST,  "ARTIST");\r
257                 SetTextW(IDC_ALBUM,   "ALBUM");\r
258                 SetTextW(IDC_COMMENT, "COMMENT");\r
259                 SetTextW(IDC_YEAR,    "DATE");\r
260                 SetTextW(IDC_TRACK,   "TRACKNUMBER");\r
261                 SetTextW(IDC_GENRE,   "GENRE");\r
262         }\r
263         else\r
264         {\r
265                 SetText(IDC_TITLE,   "TITLE");\r
266                 SetText(IDC_ARTIST,  "ARTIST");\r
267                 SetText(IDC_ALBUM,   "ALBUM");\r
268                 SetText(IDC_COMMENT, "COMMENT");\r
269                 SetText(IDC_YEAR,    "DATE");\r
270                 SetText(IDC_TRACK,   "TRACKNUMBER");\r
271                 SetText(IDC_GENRE,   "GENRE");\r
272         }\r
273 \r
274         return TRUE;\r
275 }\r
276 \r
277 static void __inline SetTag(HWND hwnd, const char *filename, FLAC__StreamMetadata *tags)\r
278 {\r
279         strcpy(buffer, infoTitle);\r
280 \r
281         if (FLAC_plugin__tags_set(filename, tags))\r
282                 strcat(buffer, " [Updated]");\r
283         else strcat(buffer, " [Failed]");\r
284 \r
285         SetWindowText(hwnd, buffer);\r
286 }\r
287 \r
288 static void UpdateTag(HWND hwnd)\r
289 {\r
290         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
291         wchar_t *ucs2;\r
292 \r
293         /* get fields */\r
294         if (isNT)\r
295         {\r
296                 GetTextW(IDC_TITLE,   "TITLE");\r
297                 GetTextW(IDC_ARTIST,  "ARTIST");\r
298                 GetTextW(IDC_ALBUM,   "ALBUM");\r
299                 GetTextW(IDC_COMMENT, "COMMENT");\r
300                 GetTextW(IDC_YEAR,    "DATE");\r
301                 GetTextW(IDC_TRACK,   "TRACKNUMBER");\r
302                 GetTextW(IDC_GENRE,   "GENRE");\r
303 \r
304                 ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, "GENRE");\r
305                 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, ucs2, -1, buffer, sizeof(buffer), NULL, NULL);\r
306                 free(ucs2);\r
307         }\r
308         else\r
309         {\r
310                 GetText(IDC_TITLE,   "TITLE");\r
311                 GetText(IDC_ARTIST,  "ARTIST");\r
312                 GetText(IDC_ALBUM,   "ALBUM");\r
313                 GetText(IDC_COMMENT, "COMMENT");\r
314                 GetText(IDC_YEAR,    "DATE");\r
315                 GetText(IDC_TRACK,   "TRACKNUMBER");\r
316                 GetText(IDC_GENRE,   "GENRE");\r
317         }\r
318 \r
319         /* update genres list (buffer should contain genre) */\r
320         if (buffer[0]) AddGenre(hwnd, buffer);\r
321 \r
322         /* write tag */\r
323         SetTag(hwnd, data->filename, data->tags);\r
324 }\r
325 \r
326 static void RemoveTag(HWND hwnd)\r
327 {\r
328         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
329         FLAC_plugin__tags_delete_all(data->tags);\r
330 \r
331         SetDlgItemText(hwnd, IDC_TITLE,   "");\r
332         SetDlgItemText(hwnd, IDC_ARTIST,  "");\r
333         SetDlgItemText(hwnd, IDC_ALBUM,   "");\r
334         SetDlgItemText(hwnd, IDC_COMMENT, "");\r
335         SetDlgItemText(hwnd, IDC_YEAR,    "");\r
336         SetDlgItemText(hwnd, IDC_TRACK,   "");\r
337         SetDlgItemText(hwnd, IDC_GENRE,   "");\r
338 \r
339         SetTag(hwnd, data->filename, data->tags);\r
340 }\r
341 \r
342 \r
343 static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
344 {\r
345         switch (msg)\r
346         {\r
347         /* init */\r
348         case WM_INITDIALOG:\r
349                 SetWindowText(hwnd, infoTitle);\r
350                 InitGenres(hwnd);\r
351                 /* init fields */\r
352                 if (!InitInfoboxInfo(hwnd, (const char*)lParam))\r
353                         PostMessage(hwnd, WM_CLOSE, 0, 0);\r
354                 return TRUE;\r
355         /* destroy */\r
356         case WM_DESTROY:\r
357                 {\r
358                         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
359                         FLAC_plugin__tags_destroy(&data->tags);\r
360                         LocalFree(data);\r
361                         DeinitGenres(hwnd, FALSE);\r
362                 }\r
363                 break;\r
364         /* commands */\r
365         case WM_COMMAND:\r
366                 switch (LOWORD(wParam))\r
367                 {\r
368                 /* ok/cancel */\r
369                 case IDOK:\r
370                 case IDCANCEL:\r
371                         EndDialog(hwnd, LOWORD(wParam));\r
372                         return TRUE;\r
373                 /* save */\r
374                 case IDC_UPDATE:\r
375                         UpdateTag(hwnd);\r
376                         break;\r
377                 /* remove */\r
378                 case IDC_REMOVE:\r
379                         RemoveTag(hwnd);\r
380                         break;\r
381                 }\r
382                 break;\r
383         }\r
384 \r
385         return 0;\r
386 }\r
387 \r
388 /*\r
389  *  Helpers\r
390  */\r
391 \r
392 ULONGLONG FileSize(const char *fileName)\r
393 {\r
394         LARGE_INTEGER res;\r
395         HANDLE hFile = CreateFile(fileName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
396 \r
397         if (hFile == INVALID_HANDLE_VALUE) return 0;\r
398         res.LowPart = GetFileSize(hFile, &res.HighPart);\r
399         CloseHandle(hFile);\r
400         return res.QuadPart;\r
401 }\r
402 \r
403 static __inline char *GetFileName(const char *fullname)\r
404 {\r
405         const char *c = fullname + strlen(fullname) - 1;\r
406 \r
407         while (c > fullname)\r
408         {\r
409                 if (*c=='\\' || *c=='/')\r
410                 {\r
411                         c++;\r
412                         break;\r
413                 }\r
414                 c--;\r
415         }\r
416 \r
417         return (char*)c;\r
418 }\r
419 \r
420 void ReadTags(const char *fileName, FLAC__StreamMetadata **tags, BOOL forDisplay)\r
421 {\r
422         if(FLAC_plugin__tags_get(fileName, tags)) {\r
423 \r
424                 /* add file name */\r
425                 if (forDisplay)\r
426                 {\r
427                         char *c;\r
428                         wchar_t *ucs2;\r
429                         ucs2 = AnsiToWide(fileName);\r
430                         FLAC_plugin__tags_set_tag_ucs2(*tags, "filepath", ucs2, /*replace_all=*/true);\r
431                         free(ucs2);\r
432 \r
433                         strcpy(buffer, GetFileName(fileName));\r
434                         if (c = strrchr(buffer, '.')) *c = 0;\r
435                         ucs2 = AnsiToWide(buffer);\r
436                         FLAC_plugin__tags_set_tag_ucs2(*tags, "filename", ucs2, /*replace_all=*/true);\r
437                         free(ucs2);\r
438                 }\r
439         }\r
440 }\r
441 \r
442 /*\r
443  *  Front-end\r
444  */\r
445 \r
446 void InitInfobox()\r
447 {\r
448         isNT = !(GetVersion() & 0x80000000);\r
449 }\r
450 \r
451 void DeinitInfobox()\r
452 {\r
453         DeinitGenres(NULL, true);\r
454 }\r
455 \r
456 void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename)\r
457 {\r
458         DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename);\r
459 }\r