7
|
1 /**
|
|
2 * config.c:
|
|
3 *
|
|
4 * Functions to load/save/edit the config.
|
|
5 **/
|
|
6
|
10
|
7 #include "dirtools.h"
|
|
8 #include "config.h"
|
11
|
9 #include "ini.h"
|
|
10 #include "plugin.h" /* g_plugin */
|
|
11
|
|
12 /* bypass intptr_t check */
|
|
13 #ifndef _MSC_VER
|
|
14 #define _MSC_VER 1201
|
|
15 #define WGSDK_UGLY_MSC_HACK 1
|
|
16 #endif
|
10
|
17
|
11
|
18 #include <Winamp/wa_ipc.h>
|
|
19
|
|
20 #ifdef WGSDK_UGLY_MSC_HACK
|
|
21 #undef _MSC_VER
|
|
22 #endif
|
|
23
|
|
24 #include <shlwapi.h>
|
10
|
25 #include <shlobj.h>
|
|
26
|
0
|
27 #include <assert.h>
|
10
|
28
|
|
29 #define MAX_LINE_LENGTH 256
|
0
|
30
|
11
|
31 #ifndef IPC_GETPLUGINDIRECTORYW
|
|
32 /* requires Winamp 5.58+ */
|
|
33 #define IPC_GETPLUGINDIRECTORYW 1336
|
|
34 #endif
|
|
35
|
|
36 /* set defaults */
|
|
37 struct config config = {
|
|
38 .display_title = 1,
|
|
39 .display_artist_name = 1,
|
|
40 .display_album_name = 1,
|
|
41 .display_album_art = 1,
|
|
42 .display_song_info = 1,
|
|
43 .show_elapsed_time = 1
|
|
44 };
|
0
|
45
|
10
|
46 /* must be free'd by the caller */
|
|
47 LPWSTR cfg_get_path() {
|
11
|
48 LPWSTR plugins_folder = NULL;
|
|
49 DWORD winamp_version = SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETVERSION);
|
|
50
|
|
51 if (winamp_version >= 0x5580) {
|
|
52 /* Native wide string version of IPC_GETPLUGINDIRECTORY */
|
|
53 LPCWSTR w_plugins_folder = (LPCWSTR)SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW);
|
|
54 size_t len = wcslen(w_plugins_folder);
|
|
55
|
|
56 plugins_folder = calloc(len + 1, sizeof(WCHAR));
|
|
57 if (!plugins_folder)
|
|
58 return NULL;
|
|
59
|
|
60 wcsncpy(plugins_folder, w_plugins_folder, len);
|
|
61 } else if (winamp_version >= 0x5110) {
|
|
62 /* ANSI string version of IPC_GETPLUGINDIRECTORY, convert */
|
|
63 LPCSTR plugins_folder_ansi = (LPCSTR)SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORY);
|
|
64 int required_size = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plugins_folder_ansi, -1, NULL, 0);
|
|
65 if (required_size < 0)
|
|
66 return NULL;
|
|
67
|
|
68 plugins_folder = calloc(required_size, sizeof(WCHAR));
|
|
69 if (!plugins_folder)
|
|
70 return NULL;
|
10
|
71
|
11
|
72 int size = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plugins_folder_ansi, -1, plugins_folder, required_size);
|
|
73 if (size < 0)
|
|
74 return NULL;
|
|
75 } else if (winamp_version >= 0x2900) {
|
|
76 /* Ancient winamp, use IPC_GETINIDIRECTORY and find out from there */
|
|
77 LPCSTR ini_directory = (LPCSTR)SendMessage(g_plugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
|
|
78
|
|
79 int required_size = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ini_directory, -1, NULL, 0);
|
|
80 if (required_size < 0)
|
|
81 return NULL;
|
|
82
|
|
83 LPWSTR ini_directory_w = calloc(required_size, sizeof(WCHAR));
|
|
84 if (!ini_directory_w)
|
|
85 return NULL;
|
|
86
|
|
87 int size = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ini_directory, -1, ini_directory_w, required_size);
|
|
88 if (size < 0) {
|
|
89 free(ini_directory_w);
|
|
90 return NULL;
|
|
91 }
|
|
92
|
|
93 plugins_folder = dirtools_concat_paths(ini_directory_w, L"Plugins");
|
|
94 free(ini_directory_w);
|
|
95 } else {
|
|
96 /* please, please for the love of God update winamp. */
|
|
97 WCHAR appdata[MAX_PATH] = {L'\0'};
|
|
98
|
|
99 HRESULT res = SHGetFolderPathW(
|
|
100 g_plugin.hwndParent, CSIDL_APPDATA, NULL,
|
|
101 SHGFP_TYPE_CURRENT, appdata
|
|
102 );
|
|
103
|
|
104 if (res != S_OK)
|
|
105 return NULL;
|
|
106
|
|
107 plugins_folder = dirtools_concat_paths(appdata, L"Winamp\\Plugins");
|
10
|
108 }
|
|
109
|
11
|
110 LPWSTR final = dirtools_concat_paths(plugins_folder, L"wgsdk");
|
|
111 free(plugins_folder);
|
10
|
112
|
|
113 return final;
|
|
114 }
|
|
115
|
11
|
116 static int cfg_ini_handler(void* data, const char* section, const char* key, const char* value) {
|
|
117 struct config* config = (struct config*)data;
|
|
118
|
|
119 char* ptr; // used with strtol
|
|
120
|
|
121 if (!strcmp(section, "Display configuration")) {
|
|
122 if (!strcmp(key, "Display title")) {
|
|
123 config->display_title = !!strtol(value, &ptr, 10);
|
|
124 return 1;
|
|
125 } else if (!strcmp(key, "Display artist name")) {
|
|
126 config->display_artist_name = !!strtol(value, &ptr, 10);
|
|
127 return 1;
|
|
128 } else if (!strcmp(key, "Display album name")) {
|
|
129 config->display_album_name = !!strtol(value, &ptr, 10);
|
|
130 return 1;
|
|
131 } else if (!strcmp(key, "Display album art")) {
|
|
132 config->display_album_art = !!strtol(value, &ptr, 10);
|
|
133 return 1;
|
|
134 } else if (!strcmp(key, "Display song information")) {
|
|
135 config->display_song_info = !!strtol(value, &ptr, 10);
|
|
136 return 1;
|
|
137 } else if (!strcmp(key, "Show elapsed time")) {
|
|
138 config->show_elapsed_time = !!strtol(value, &ptr, 10);
|
|
139 return 1;
|
|
140 } else return 0;
|
|
141 } else return 0;
|
|
142 }
|
|
143
|
|
144 char* cfg_ini_reader(char* str, int num, void* stream) {
|
|
145 HANDLE file = (HANDLE)stream;
|
|
146 DWORD bytes_read;
|
|
147
|
|
148 return (ReadFile(file, str, num, &bytes_read, NULL) && num == bytes_read) ? str : NULL;
|
|
149 }
|
|
150
|
9
|
151 int cfg_load(struct config* restrict config) {
|
10
|
152 LPWSTR fold_path = cfg_get_path();
|
11
|
153 assert(fold_path);
|
|
154
|
|
155 LPWSTR path = dirtools_concat_paths(fold_path, L"config.ini");
|
10
|
156 free(fold_path);
|
|
157
|
11
|
158 HANDLE file = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
159 if (file == INVALID_HANDLE_VALUE) /* file doesn't exist? */
|
|
160 return 0;
|
10
|
161
|
11
|
162 if (ini_parse_stream(cfg_ini_reader, file, cfg_ini_handler, config))
|
|
163 return 1;
|
|
164
|
|
165 CloseHandle(file);
|
10
|
166
|
0
|
167 free(path);
|
|
168 return 0;
|
|
169 }
|
|
170
|
11
|
171 void cfg_write_int_key(HANDLE file, const char* key, int value) {
|
|
172 DWORD bytes_written;
|
|
173
|
|
174 /* write the key */
|
|
175 size_t len = strlen(key) * sizeof(*key);
|
|
176 WriteFile(file, key, len, &bytes_written, NULL);
|
|
177 assert(bytes_written == len);
|
|
178
|
|
179 static const char equals[] = {'='};
|
|
180 WriteFile(file, equals, sizeof(equals), &bytes_written, NULL);
|
|
181 assert(bytes_written == sizeof(equals));
|
|
182
|
|
183 char buf[16] = {'\0'};
|
|
184 _itoa(value, buf, 10);
|
|
185 len = strlen(buf) * sizeof(buf[0]);
|
|
186 WriteFile(file, (buf), len, &bytes_written, NULL);
|
|
187 assert(bytes_written == len);
|
|
188
|
|
189 static const char ln[] = {'\r', '\n'};
|
|
190 WriteFile(file, ln, sizeof(ln), &bytes_written, NULL);
|
|
191 assert(bytes_written == sizeof(ln));
|
|
192 }
|
|
193
|
|
194 void cfg_write_header(HANDLE file, const char* header) {
|
|
195 DWORD bytes_written = 0;
|
|
196
|
|
197 static const char l_bracket[] = {'['};
|
|
198 WriteFile(file, l_bracket, sizeof(l_bracket), &bytes_written, NULL);
|
|
199 assert(bytes_written == sizeof(l_bracket));
|
|
200
|
|
201 const size_t len = strlen(header) * sizeof(*header);
|
|
202 WriteFile(file, header, len, &bytes_written, NULL);
|
|
203 assert(bytes_written == len);
|
|
204
|
|
205 static const char r_bracket[] = {']'};
|
|
206 WriteFile(file, r_bracket, sizeof(r_bracket), &bytes_written, NULL);
|
|
207 assert(bytes_written == sizeof(r_bracket));
|
|
208
|
|
209 static const char ln[] = {'\r', '\n'};
|
|
210 WriteFile(file, ln, sizeof(ln), &bytes_written, NULL);
|
|
211 assert(bytes_written == sizeof(ln));
|
|
212 }
|
|
213
|
|
214 int cfg_save(const struct config* config) {
|
10
|
215 LPWSTR fold_path = cfg_get_path();
|
11
|
216 assert(fold_path);
|
10
|
217 assert(!dirtools_create_directory(fold_path));
|
|
218
|
11
|
219 LPWSTR path = dirtools_concat_paths(fold_path, L"config.ini");
|
10
|
220 free(fold_path);
|
7
|
221
|
11
|
222 /* dirty little hack that lets me not use goto */
|
|
223 do {
|
|
224 HANDLE file = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
225 if (file == INVALID_HANDLE_VALUE) /* huh */
|
|
226 break;
|
|
227
|
|
228 cfg_write_header(file, "Display configuration");
|
10
|
229
|
11
|
230 cfg_write_int_key(file, "Display title", config->display_title);
|
|
231 cfg_write_int_key(file, "Display artist name", config->display_artist_name);
|
|
232 cfg_write_int_key(file, "Display album name", config->display_album_name);
|
|
233 cfg_write_int_key(file, "Display album art", config->display_album_art);
|
|
234 cfg_write_int_key(file, "Display song information", config->display_song_info);
|
|
235 cfg_write_int_key(file, "Show elapsed time", config->show_elapsed_time);
|
|
236
|
|
237 CloseHandle(file);
|
|
238 } while (0);
|
|
239
|
10
|
240 free(path);
|
0
|
241 return 0;
|
|
242 }
|
1
|
243
|
11
|
244 #undef WritePrivateProfileIntW
|