a better fix for CUE/TOC string escaping: if the text is not Latin-1 already, reject...
[ardour.git] / libs / ardour / linux_vst_info_file.cc
1 /***********************************************************/
2 /*vstfx infofile - module to manage info files             */
3 /*containing cached information about a plugin. e.g. its   */
4 /*name, creator etc etc                                    */
5 /***********************************************************/
6
7 #include <iostream>
8
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12 #include <errno.h>
13
14 #include <stdlib.h>
15 #include <stddef.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include <libgen.h>
19
20 #include <glib.h>
21 #include <glib/gstdio.h>
22
23 #include "ardour/linux_vst_support.h"
24
25 #define MAX_STRING_LEN 256
26
27 static char* read_string(FILE *fp)
28 {
29     char buf[MAX_STRING_LEN];
30
31     if (!fgets( buf, MAX_STRING_LEN, fp )) {
32             return 0;
33     }
34     
35     if(strlen(buf) < MAX_STRING_LEN) {
36             if (strlen(buf)) {
37                     buf[strlen(buf)-1] = 0;
38             }
39             return strdup(buf);
40     } else {
41             return 0;
42     }
43 }
44
45 static VSTInfo *
46 load_vstfx_info_file (FILE* fp)
47 {
48         VSTInfo *info;
49         int i;
50         
51         if ((info = (VSTInfo*) malloc (sizeof (VSTInfo))) == 0) {
52                 return 0;
53         }
54
55         if((info->name = read_string(fp)) == 0) goto error;
56         if((info->creator = read_string(fp)) == 0) goto error;
57         if(1 != fscanf(fp, "%d\n", &info->UniqueID)) goto error;
58         if((info->Category = read_string(fp)) == 0) goto error;
59         if(1 != fscanf(fp, "%d\n", &info->numInputs)) goto error;
60         if(1 != fscanf(fp, "%d\n", &info->numOutputs)) goto error;
61         if(1 != fscanf(fp, "%d\n", &info->numParams)) goto error;
62         if(1 != fscanf(fp, "%d\n", &info->wantMidi)) goto error;
63         if(1 != fscanf(fp, "%d\n", &info->hasEditor)) goto error;
64         if(1 != fscanf(fp, "%d\n", &info->canProcessReplacing)) goto error;
65         
66         if((info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams)) == 0) {
67                 goto error;
68         }
69
70         for (i=0; i<info->numParams; i++) {
71                 if((info->ParamNames[i] = read_string(fp)) == 0) goto error;
72         }
73
74         if ((info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams)) == 0) {
75                 goto error;
76         }
77         
78         for (i=0; i < info->numParams; i++) {
79                 if((info->ParamLabels[i] = read_string(fp)) == 0) goto error;
80         }
81         
82         return info;
83         
84   error:
85         free( info );
86         return 0;
87 }
88
89 static int
90 save_vstfx_info_file (VSTInfo *info, FILE* fp)
91 {
92     int i;
93
94     if (info == 0) {
95             vstfx_error("** ERROR ** VSTFXinfofile : info ptr is 0\n");
96             return -1;
97     }
98
99     if (fp == 0) {
100             vstfx_error("** ERROR ** VSTFXinfofile : file ptr is 0\n");
101             return -1;
102     }
103     
104     fprintf( fp, "%s\n", info->name );
105     fprintf( fp, "%s\n", info->creator );
106     fprintf( fp, "%d\n", info->UniqueID );
107     fprintf( fp, "%s\n", info->Category );
108     fprintf( fp, "%d\n", info->numInputs );
109     fprintf( fp, "%d\n", info->numOutputs );
110     fprintf( fp, "%d\n", info->numParams );
111     fprintf( fp, "%d\n", info->wantMidi );
112     fprintf( fp, "%d\n", info->hasEditor );
113     fprintf( fp, "%d\n", info->canProcessReplacing );
114
115     for (i=0; i < info->numParams; i++) {
116                 fprintf(fp, "%s\n", info->ParamNames[i]);
117     }
118         
119     for (i=0; i < info->numParams; i++) {
120                 fprintf(fp, "%s\n", info->ParamLabels[i]);
121     }
122         
123     return 0;
124 }
125
126 static char* vstfx_infofile_stat (char *dllpath, struct stat* statbuf, int personal)
127 {
128         char* path;
129         char* dir_path;
130         char* basename;
131         char* base;
132         size_t blen;
133
134         if (strstr (dllpath, ".so" ) == 0) {
135                 return 0;
136         }
137         
138         if (personal) {
139                 dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL);
140         } else {
141                 dir_path = g_path_get_dirname (dllpath);
142         }
143         
144         base = g_path_get_basename (dllpath);
145         blen = strlen (base) + 2; // null char and '.'
146         basename = (char*) g_malloc (blen);
147         snprintf (basename, blen, ".%s.fsi", base);
148         g_free (base);
149         
150         path = g_build_filename (dir_path, basename, NULL);
151         
152         g_free (dir_path);
153         g_free (basename);
154
155
156         if (g_file_test (path, GFileTest (G_FILE_TEST_EXISTS|G_FILE_TEST_IS_REGULAR))) {
157
158                 /* info file exists in same location as the shared object, so
159                    check if its current and up to date
160                 */
161
162
163                 struct stat dllstat;
164                 
165                 if (stat (dllpath, &dllstat) == 0) {
166                         if (stat(path, statbuf) == 0) {
167                                 if (dllstat.st_mtime <= statbuf->st_mtime) {
168                                         /* plugin is older than info file */
169                                         return path;
170                                 }
171                         }
172                 } 
173         }
174
175         g_free (path);
176
177         return 0;
178 }
179
180
181 static FILE* vstfx_infofile_for_read (char* dllpath)
182 {
183         struct stat own_statbuf;
184         struct stat sys_statbuf;
185         char *own_info;
186         char *sys_info;
187         
188         own_info = vstfx_infofile_stat (dllpath, &own_statbuf, 1);
189         sys_info = vstfx_infofile_stat (dllpath, &sys_statbuf, 0);
190
191         if (own_info) {
192                 if (sys_info) {
193                         if (own_statbuf.st_mtime <= sys_statbuf.st_mtime) {
194                                 /* system info file is newer, use it */
195                                 return fopen (sys_info, "r");
196                         }
197                 } else {
198                         return fopen (own_info, "r");
199                 }
200         }
201
202         return 0;
203 }
204
205 static FILE* vstfx_infofile_create (char* dllpath, int personal)
206 {
207         char* path;
208         char* dir_path;
209         char* basename;
210         char* base;
211         size_t blen;
212
213         if (strstr (dllpath, ".so" ) == 0) {
214                 return 0;
215         }
216         
217         if (personal) {
218                 dir_path = g_build_filename (g_get_home_dir(), ".fst", NULL);
219
220                 /* if the directory doesn't yet exist, try to create it */
221
222                 if (!g_file_test (dir_path, G_FILE_TEST_IS_DIR)) {
223                         if (g_mkdir (dir_path, 0700)) {
224                                 return 0;
225                         }
226                 }
227
228         } else {
229                 dir_path = g_path_get_dirname (dllpath);
230         }
231         
232         base = g_path_get_basename (dllpath);
233         blen = strlen (base) + 2; // null char and '.'
234         basename = (char*) g_malloc (blen);
235         snprintf (basename, blen, ".%s.fsi", base);
236         g_free (base);
237
238         path = g_build_filename (dir_path, basename, NULL);
239
240         g_free (dir_path);
241         g_free (basename);
242
243         FILE* f = fopen (path, "w");
244         g_free (path);
245
246         return f;
247 }
248
249 static FILE* vstfx_infofile_for_write (char* dllpath)
250 {
251         FILE* f;
252
253         if ((f = vstfx_infofile_create (dllpath, 0)) == 0) {
254                 f = vstfx_infofile_create (dllpath, 1);
255         }
256         
257         return f;
258 }
259
260 static
261 int vstfx_can_midi (VSTState* vstfx)
262 {
263         AEffect *plugin = vstfx->plugin;
264         
265         int vst_version = plugin->dispatcher (plugin, effGetVstVersion, 0, 0, 0, 0.0f);
266
267         if (vst_version >= 2)
268         {
269                 /* should we send it VST events (i.e. MIDI) */
270                 
271                 if ((plugin->flags & effFlagsIsSynth) || (plugin->dispatcher (plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0))
272                     return -1;
273         }
274         return false;
275 }
276
277 static VSTInfo *
278 vstfx_info_from_plugin (VSTState* vstfx)
279 {
280         VSTInfo* info = (VSTInfo*) malloc (sizeof (VSTInfo));
281         
282         AEffect *plugin;
283         int i;
284         
285         /*We need to init the creator because some plugins
286           fail to implement getVendorString, and so won't stuff the
287           string with any name*/
288         
289         char creator[65] = "Unknown\0";
290         
291         if(!vstfx)
292         {
293                 vstfx_error( "** ERROR ** VSTFXinfofile : vstfx ptr is 0\n" );
294                 return 0;
295         }
296         
297         if(!info)
298                 return 0;
299         
300         plugin = vstfx->plugin;
301         
302         info->name = strdup(vstfx->handle->name ); 
303         
304         /*If the plugin doesn't bother to implement GetVendorString we will
305           have pre-stuffed the string with 'Unkown' */
306         
307         plugin->dispatcher (plugin, effGetVendorString, 0, 0, creator, 0);
308         
309         /*Some plugins DO implement GetVendorString, but DON'T put a name in it
310           so if its just a zero length string we replace it with 'Unknown' */
311         
312         if (strlen(creator) == 0) {
313                 info->creator = strdup("Unknown");
314         } else {
315                 info->creator = strdup (creator);
316         }
317         
318         info->UniqueID = plugin->uniqueID;
319         
320         info->Category = strdup("None");          // FIXME:  
321         info->numInputs = plugin->numInputs;
322         info->numOutputs = plugin->numOutputs;
323         info->numParams = plugin->numParams;
324         info->wantMidi = vstfx_can_midi(vstfx); 
325         info->hasEditor = plugin->flags & effFlagsHasEditor ? true : false;
326         info->canProcessReplacing = plugin->flags & effFlagsCanReplacing ? true : false;
327         info->ParamNames = (char **) malloc(sizeof(char*)*info->numParams);
328         info->ParamLabels = (char **) malloc(sizeof(char*)*info->numParams);
329
330         for(i=0; i < info->numParams; i++) {
331                 char name[64];
332                 char label[64];
333                 
334                 /*Not all plugins give parameters labels as well as names*/
335                 
336                 strcpy(name, "No Name");
337                 strcpy(label, "No Label");
338                 
339                 plugin->dispatcher (plugin, effGetParamName, i, 0, name, 0);
340                 info->ParamNames[i] = strdup(name);
341                 
342                 //NOTE: 'effGetParamLabel' is no longer defined in vestige headers
343                 //plugin->dispatcher (plugin, effGetParamLabel, i, 0, label, 0);
344                 info->ParamLabels[i] = strdup(label);
345         }
346         return info;
347 }
348
349 /* A simple 'dummy' audiomaster callback which should be ok,
350 we will only be instantiating the plugin in order to get its info*/
351
352 static intptr_t
353 simple_master_callback (AEffect *, int32_t opcode, int32_t, intptr_t, void *, float)
354 {
355         if (opcode == audioMasterVersion)
356                 return 2;
357         else
358                 return 0;
359 }
360
361 /*Try to get plugin info - first by looking for a .fsi cache of the
362 data, and if that doesn't exist, load the plugin, get its data and
363 then cache it for future ref*/
364
365 VSTInfo *
366 vstfx_get_info (char* dllpath)
367 {
368         FILE* infofile;
369         VSTHandle* h;
370         VSTState* vstfx;
371         VSTInfo* info;
372
373         if ((infofile = vstfx_infofile_for_read (dllpath)) != 0) {
374                 VSTInfo *info;
375                 info = load_vstfx_info_file (infofile);
376                 fclose (infofile);
377                 return info;
378         } 
379         
380         if(!(h = vstfx_load(dllpath)))
381                 return 0;
382         
383         if(!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) {
384                 vstfx_unload(h);
385                 vstfx_error( "** ERROR ** VSTFXinfofile : Instantiate failed\n" );
386                 return 0;
387         }
388         
389         infofile = vstfx_infofile_for_write (dllpath);
390         
391         if(!infofile) {
392                 vstfx_close(vstfx);
393                 vstfx_unload(h);
394                 vstfx_error("cannot create new FST info file for plugin");
395                 return 0;
396         }
397         
398         info = vstfx_info_from_plugin(vstfx);
399         
400         save_vstfx_info_file(info, infofile);
401         fclose (infofile);
402         
403         vstfx_close(vstfx);
404         vstfx_unload(h);
405         
406         return info;
407 }
408
409 void
410 vstfx_free_info (VSTInfo *info)
411 {
412         for (int i = 0; i < info->numParams; i++) {
413                 free (info->ParamNames[i]);
414                 free (info->ParamLabels[i]);
415         }
416         
417         free (info->name);
418         free (info->creator);
419         free (info->Category);
420         free (info);
421 }
422
423