fixes to get winamp2 plugin to build on win32 again
[flac.git] / src / plugin_winamp2 / infobox.c
1 /* in_flac - Winamp2 FLAC input plugin\r
2  * Copyright (C) 2002,2003,2004,2005  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__StreamMetadata *tags;\r
32 } LOCALDATA;\r
33 \r
34 static char buffer[8192];\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 static wchar_t *AnsiToWide(const char *src)\r
178 {\r
179         int len;\r
180         wchar_t *dest;\r
181 \r
182         FLAC__ASSERT(0 != src);\r
183 \r
184         len = strlen(src) + 1;\r
185         /* copy */\r
186         dest = (wchar_t*)malloc(len*sizeof(wchar_t));\r
187         if (dest) mbstowcs(dest, src, len);\r
188         return dest;\r
189 }\r
190 \r
191 /*\r
192  *  Infobox helpers\r
193  */\r
194 \r
195 #define SetText(x,y)            ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, y); \\r
196                                 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, ucs2, -1, buffer, sizeof(buffer), NULL, NULL); \\r
197                                 if(ucs2) free(ucs2); \\r
198                                 SetDlgItemText(hwnd, x, buffer)\r
199 \r
200 #define GetText(x,y)            GetDlgItemText(hwnd, x, buffer, sizeof(buffer));                        \\r
201                                 if (*buffer) { ucs2 = AnsiToWide(buffer); FLAC_plugin__tags_set_tag_ucs2(data->tags, y, ucs2, /*replace_all=*/false); free(ucs2); } \\r
202                                 else FLAC_plugin__tags_delete_tag(data->tags, y)\r
203 \r
204 #define SetTextW(x,y)           ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, y); \\r
205                                 SetDlgItemTextW(hwnd, x, ucs2); \\r
206                                 free(ucs2)\r
207 \r
208 #define GetTextW(x,y)           GetDlgItemTextW(hwnd, x, (WCHAR*)buffer, sizeof(buffer)/2);                     \\r
209                                 if (*(WCHAR*)buffer) FLAC_plugin__tags_set_tag_ucs2(data->tags, y, (WCHAR*)buffer, /*replace_all=*/false); \\r
210                                 else FLAC_plugin__tags_delete_tag(data->tags, y)\r
211 \r
212 \r
213 static BOOL InitInfoboxInfo(HWND hwnd, const char *file)\r
214 {\r
215         LOCALDATA *data = LocalAlloc(LPTR, sizeof(LOCALDATA));\r
216         wchar_t *ucs2;\r
217         FLAC__StreamMetadata streaminfo;\r
218         DWORD    length, bps, ratio, rg;\r
219         LONGLONG filesize;\r
220 \r
221         SetWindowLong(hwnd, GWL_USERDATA, (LONG)data);\r
222         /* file name */\r
223         strncpy(data->filename, file, sizeof(data->filename));\r
224         SetDlgItemText(hwnd, IDC_NAME, file);\r
225         /* stream data and vorbis comment */\r
226         filesize = FileSize(file);\r
227         if (!filesize) return FALSE;\r
228         if (!FLAC__metadata_get_streaminfo(file, &streaminfo))\r
229                 return FALSE;\r
230         ReadTags(file, &data->tags, false);\r
231 \r
232         length = (DWORD)(streaminfo.data.stream_info.total_samples / streaminfo.data.stream_info.sample_rate);\r
233         bps = (DWORD)(filesize / (125*streaminfo.data.stream_info.total_samples/streaminfo.data.stream_info.sample_rate));\r
234         ratio = bps*1000000 / (streaminfo.data.stream_info.sample_rate*streaminfo.data.stream_info.channels*streaminfo.data.stream_info.bits_per_sample);\r
235         rg  = FLAC_plugin__tags_get_tag_utf8(data->tags, "REPLAYGAIN_TRACK_GAIN") ? 1 : 0;\r
236         rg |= FLAC_plugin__tags_get_tag_utf8(data->tags, "REPLAYGAIN_ALBUM_GAIN") ? 2 : 0;\r
237 \r
238         sprintf(buffer, "Sample rate: %d Hz\nChannels: %d\nBits per sample: %d\nMin block size: %d\nMax block size: %d\n"\r
239                         "File size: %I64d bytes\nTotal samples: %I64d\nLength: %d:%02d\nAvg. bitrate: %d\nCompression ratio: %d.%d%%\n"\r
240                         "ReplayGain: %s\n",\r
241             streaminfo.data.stream_info.sample_rate, streaminfo.data.stream_info.channels, streaminfo.data.stream_info.bits_per_sample,\r
242             streaminfo.data.stream_info.min_blocksize, streaminfo.data.stream_info.max_blocksize, filesize, streaminfo.data.stream_info.total_samples,\r
243             length/60, length%60, bps, ratio/10, ratio%10,\r
244             rg==3 ? "track gain\nReplayGain: album gain" : rg==2 ? "album gain" : rg==1 ? "track gain" : "not present");\r
245 \r
246         SetDlgItemText(hwnd, IDC_INFO, buffer);\r
247         /* tag */\r
248         if (isNT)\r
249         {\r
250                 SetTextW(IDC_TITLE,   "TITLE");\r
251                 SetTextW(IDC_ARTIST,  "ARTIST");\r
252                 SetTextW(IDC_ALBUM,   "ALBUM");\r
253                 SetTextW(IDC_COMMENT, "COMMENT");\r
254                 SetTextW(IDC_YEAR,    "DATE");\r
255                 SetTextW(IDC_TRACK,   "TRACKNUMBER");\r
256                 SetTextW(IDC_GENRE,   "GENRE");\r
257         }\r
258         else\r
259         {\r
260                 SetText(IDC_TITLE,   "TITLE");\r
261                 SetText(IDC_ARTIST,  "ARTIST");\r
262                 SetText(IDC_ALBUM,   "ALBUM");\r
263                 SetText(IDC_COMMENT, "COMMENT");\r
264                 SetText(IDC_YEAR,    "DATE");\r
265                 SetText(IDC_TRACK,   "TRACKNUMBER");\r
266                 SetText(IDC_GENRE,   "GENRE");\r
267         }\r
268 \r
269         return TRUE;\r
270 }\r
271 \r
272 static void __inline SetTag(HWND hwnd, const char *filename, FLAC__StreamMetadata *tags)\r
273 {\r
274         strcpy(buffer, infoTitle);\r
275 \r
276         if (FLAC_plugin__tags_set(filename, tags))\r
277                 strcat(buffer, " [Updated]");\r
278         else strcat(buffer, " [Failed]");\r
279 \r
280         SetWindowText(hwnd, buffer);\r
281 }\r
282 \r
283 static void UpdateTag(HWND hwnd)\r
284 {\r
285         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
286         wchar_t *ucs2;\r
287 \r
288         /* get fields */\r
289         if (isNT)\r
290         {\r
291                 GetTextW(IDC_TITLE,   "TITLE");\r
292                 GetTextW(IDC_ARTIST,  "ARTIST");\r
293                 GetTextW(IDC_ALBUM,   "ALBUM");\r
294                 GetTextW(IDC_COMMENT, "COMMENT");\r
295                 GetTextW(IDC_YEAR,    "DATE");\r
296                 GetTextW(IDC_TRACK,   "TRACKNUMBER");\r
297                 GetTextW(IDC_GENRE,   "GENRE");\r
298 \r
299                 ucs2 = FLAC_plugin__tags_get_tag_ucs2(data->tags, "GENRE");\r
300                 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, ucs2, -1, buffer, sizeof(buffer), NULL, NULL);\r
301                 free(ucs2);\r
302         }\r
303         else\r
304         {\r
305                 GetText(IDC_TITLE,   "TITLE");\r
306                 GetText(IDC_ARTIST,  "ARTIST");\r
307                 GetText(IDC_ALBUM,   "ALBUM");\r
308                 GetText(IDC_COMMENT, "COMMENT");\r
309                 GetText(IDC_YEAR,    "DATE");\r
310                 GetText(IDC_TRACK,   "TRACKNUMBER");\r
311                 GetText(IDC_GENRE,   "GENRE");\r
312         }\r
313 \r
314         /* update genres list (buffer should contain genre) */\r
315         if (buffer[0]) AddGenre(hwnd, buffer);\r
316 \r
317         /* write tag */\r
318         SetTag(hwnd, data->filename, data->tags);\r
319 }\r
320 \r
321 static void RemoveTag(HWND hwnd)\r
322 {\r
323         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
324         FLAC_plugin__tags_delete_all(data->tags);\r
325 \r
326         SetDlgItemText(hwnd, IDC_TITLE,   "");\r
327         SetDlgItemText(hwnd, IDC_ARTIST,  "");\r
328         SetDlgItemText(hwnd, IDC_ALBUM,   "");\r
329         SetDlgItemText(hwnd, IDC_COMMENT, "");\r
330         SetDlgItemText(hwnd, IDC_YEAR,    "");\r
331         SetDlgItemText(hwnd, IDC_TRACK,   "");\r
332         SetDlgItemText(hwnd, IDC_GENRE,   "");\r
333 \r
334         SetTag(hwnd, data->filename, data->tags);\r
335 }\r
336 \r
337 \r
338 static INT_PTR CALLBACK InfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)\r
339 {\r
340         switch (msg)\r
341         {\r
342         /* init */\r
343         case WM_INITDIALOG:\r
344                 SetWindowText(hwnd, infoTitle);\r
345                 InitGenres(hwnd);\r
346                 /* init fields */\r
347                 if (!InitInfoboxInfo(hwnd, (const char*)lParam))\r
348                         PostMessage(hwnd, WM_CLOSE, 0, 0);\r
349                 return TRUE;\r
350         /* destroy */\r
351         case WM_DESTROY:\r
352                 {\r
353                         LOCALDATA *data = (LOCALDATA*)GetWindowLong(hwnd, GWL_USERDATA);\r
354                         FLAC_plugin__tags_destroy(&data->tags);\r
355                         LocalFree(data);\r
356                         DeinitGenres(hwnd, FALSE);\r
357                 }\r
358                 break;\r
359         /* commands */\r
360         case WM_COMMAND:\r
361                 switch (LOWORD(wParam))\r
362                 {\r
363                 /* ok/cancel */\r
364                 case IDOK:\r
365                 case IDCANCEL:\r
366                         EndDialog(hwnd, LOWORD(wParam));\r
367                         return TRUE;\r
368                 /* save */\r
369                 case IDC_UPDATE:\r
370                         UpdateTag(hwnd);\r
371                         break;\r
372                 /* remove */\r
373                 case IDC_REMOVE:\r
374                         RemoveTag(hwnd);\r
375                         break;\r
376                 }\r
377                 break;\r
378         }\r
379 \r
380         return 0;\r
381 }\r
382 \r
383 /*\r
384  *  Helpers\r
385  */\r
386 \r
387 ULONGLONG FileSize(const char *fileName)\r
388 {\r
389         LARGE_INTEGER res;\r
390         HANDLE hFile = CreateFile(fileName, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);\r
391 \r
392         if (hFile == INVALID_HANDLE_VALUE) return 0;\r
393         res.LowPart = GetFileSize(hFile, &res.HighPart);\r
394         CloseHandle(hFile);\r
395         return res.QuadPart;\r
396 }\r
397 \r
398 static __inline char *GetFileName(const char *fullname)\r
399 {\r
400         const char *c = fullname + strlen(fullname) - 1;\r
401 \r
402         while (c > fullname)\r
403         {\r
404                 if (*c=='\\' || *c=='/')\r
405                 {\r
406                         c++;\r
407                         break;\r
408                 }\r
409                 c--;\r
410         }\r
411 \r
412         return (char*)c;\r
413 }\r
414 \r
415 void ReadTags(const char *fileName, FLAC__StreamMetadata **tags, BOOL forDisplay)\r
416 {\r
417         if(FLAC_plugin__tags_get(fileName, tags)) {\r
418 \r
419                 /* add file name */\r
420                 if (forDisplay)\r
421                 {\r
422                         char *c;\r
423                         wchar_t *ucs2;\r
424                         ucs2 = AnsiToWide(fileName);\r
425                         FLAC_plugin__tags_set_tag_ucs2(*tags, "filepath", ucs2, /*replace_all=*/true);\r
426                         free(ucs2);\r
427 \r
428                         strcpy(buffer, GetFileName(fileName));\r
429                         if (c = strrchr(buffer, '.')) *c = 0;\r
430                         ucs2 = AnsiToWide(buffer);\r
431                         FLAC_plugin__tags_set_tag_ucs2(*tags, "filename", ucs2, /*replace_all=*/true);\r
432                         free(ucs2);\r
433                 }\r
434         }\r
435 }\r
436 \r
437 /*\r
438  *  Front-end\r
439  */\r
440 \r
441 void InitInfobox()\r
442 {\r
443         isNT = !(GetVersion() & 0x80000000);\r
444 }\r
445 \r
446 void DeinitInfobox()\r
447 {\r
448         DeinitGenres(NULL, true);\r
449 }\r
450 \r
451 void DoInfoBox(HINSTANCE inst, HWND hwnd, const char *filename)\r
452 {\r
453         DialogBoxParam(inst, MAKEINTRESOURCE(IDD_INFOBOX), hwnd, InfoProc, (LONG)filename);\r
454 }\r