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