OSDN Git Service

LinGui: create a presets translator that reads mac gui xml file and
[handbrake-jp/handbrake-jp-git.git] / gtk / src / preset_xlat.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <glib.h>
4 #include <fcntl.h>
5
6 #define BUF_SIZ (128*1024)
7 #define IS_TAG(a,b)     (strcmp((a),(b)) == 0)
8 #define IS_KEY(a,b)     (strcmp((a),(b)) == 0)
9 #define IS_VAL(a,b)     (strcmp((a),(b)) == 0)
10
11 enum
12 {
13         NONE,
14         START,
15         ARRAY,
16         DICT,
17         KEY,
18         INT,
19         STR,
20         REAL,
21 };
22
23 typedef struct
24 {
25         gint state;
26         gchar *preset;
27         gchar *key;
28         gchar *value;
29         GHashTable *settings;
30         GHashTable *xlat_key;
31         GHashTable *xlat_value;
32 } parse_data_t;
33
34 static void
35 start_element(
36         GMarkupParseContext *ctx, 
37         const gchar *name, 
38         const gchar **attr_names,
39         const gchar **attr_values,
40         gpointer ud,
41         GError **error)
42 {
43         parse_data_t *pd = (parse_data_t*)ud;
44
45         if (IS_TAG(name, "array"))
46         {
47                 pd->state = ARRAY;
48         }
49         else if (IS_TAG(name, "dict"))
50         {
51                 g_hash_table_remove_all(pd->settings);
52                 pd->state = DICT;
53         }
54         else if (IS_TAG(name, "key"))
55         {
56                 pd->state = KEY;
57         }
58         else if (IS_TAG(name, "string"))
59         {
60                 pd->state = STR;
61         }
62         else if (IS_TAG(name, "integer"))
63         {
64                 pd->state = INT;
65         }
66         else if (IS_TAG(name, "real"))
67         {
68                 pd->state = REAL;
69         }
70         else
71         {
72                 g_debug("start unrecognized (%s)", name);
73         }
74 }
75
76 gchar *settings[] = 
77 {
78         "preset_description",
79         "subtitle_lang",
80         "forced_subtitles",
81         "source_audio_lang",
82         "pref_audio_codec",
83         "pref_audio_bitrate",
84         "pref_audio_rate",
85         "pref_audio_mix",
86         "pref_audio_drc",
87         "chapter_markers",
88         "container",
89         "ipod_file",
90         "large_mp4",
91         "autocrop",
92         "autoscale",
93         "max_width",
94         "max_height",
95         "anamorphic",
96         "round_dimensions",
97         "keep_aspect",
98         "detelecine",
99         "decomb",
100         "deinterlace",
101         "denoise",
102         "grayscale",
103         "deblock",
104         "video_codec",
105         "two_pass",
106         "turbo",
107         "constant_rate_factor",
108         "variable_frame_rate",
109         "framerate",
110         "vquality_type_constant",
111         "vquality_type_bitrate",
112         "vquality_type_target",
113         "video_bitrate",
114         "video_target_size",
115         "video_quality",
116         "x264_options",
117         "directqp",
118         NULL
119 };
120
121 static void
122 verify_keys(parse_data_t *pd)
123 {
124         GList *keys, *link;
125
126         link = keys = g_hash_table_get_keys(pd->settings);
127         while (link)
128         {
129                 gboolean found = FALSE;
130                 gchar *key = (gchar*)link->data;
131                 gint ii;
132                 for (ii = 0; settings[ii] != NULL; ii++)
133                 {
134                         if (IS_KEY(settings[ii], key))
135                         {
136                                 found = TRUE;
137                         }
138                 }
139                 if (!found)
140                 {
141                         g_message("bad key (%s)", key);
142                 }
143                 link = link->next;
144         }
145         g_list_free(keys);
146 }
147
148 GKeyFile *presets;
149
150 static void
151 save_preset(parse_data_t *pd)
152 {
153         gint ii;
154         if (pd->preset == NULL)
155         {
156                 g_message("failed to save preset");
157                 return;
158         }
159         for (ii = 0; settings[ii] != NULL; ii++)
160         {
161                 const gchar *value;
162                 value = (const gchar*)g_hash_table_lookup( pd->settings, settings[ii]);
163                 if (value)
164                 {
165                         g_key_file_set_value(presets, pd->preset, settings[ii], value);
166                 }
167         }
168         verify_keys(pd);
169 }
170
171 gchar *audio_track[2];
172 gchar *audio_enc[2];
173 gchar *audio_bitrate[2];
174 gchar *audio_rate[2];
175 gchar *audio_mix[2];
176 gchar *audio_drc[2];
177
178 static void
179 do_one(gchar **strs, GString *res)
180 {
181         gint ii;
182         for (ii = 0; ii < 2 && strs[ii]; ii++)
183         {
184                 if (audio_track[ii] == NULL) break;
185                 if (ii)
186                         g_string_append_c(res, ',');
187                 g_string_append_printf(res, "%s", strs[ii]);
188         }
189 }
190
191 static void
192 do_audio(parse_data_t *pd)
193 {
194         gint ii;
195         GString *enc, *br, *rate, *mix, *drc;
196         gchar *res;
197
198         enc = g_string_new("");
199         br = g_string_new("");
200         rate = g_string_new("");
201         mix = g_string_new("");
202         drc = g_string_new("");
203         do_one(audio_enc, enc);
204         do_one(audio_bitrate, br);
205         do_one(audio_rate, rate);
206         do_one(audio_mix, mix);
207         do_one(audio_drc, drc);
208         res = g_string_free(enc, FALSE);
209         g_hash_table_insert(pd->settings, g_strdup("pref_audio_codec"), res);
210         res = g_string_free(br, FALSE);
211         g_hash_table_insert(pd->settings, g_strdup("pref_audio_bitrate"), res);
212         res = g_string_free(rate, FALSE);
213         g_hash_table_insert(pd->settings, g_strdup("pref_audio_rate"), res);
214         res = g_string_free(mix, FALSE);
215         g_hash_table_insert(pd->settings, g_strdup("pref_audio_mix"), res);
216         res = g_string_free(drc, FALSE);
217         g_hash_table_insert(pd->settings, g_strdup("pref_audio_drc"), res);
218 }
219
220 static void
221 null_audio()
222 {
223         gint ii;
224         for (ii = 0; ii < 2; ii++)
225         {
226                 audio_track[ii] = NULL;
227                 audio_enc[ii] = NULL;
228                 audio_bitrate[ii] = NULL;
229                 audio_rate[ii] = NULL;
230                 audio_mix[ii] = NULL;
231                 audio_drc[ii] = NULL;
232         }
233 }
234
235 static void
236 clear_audio()
237 {
238         gint ii;
239         for (ii = 0; ii < 2; ii++)
240         {
241                 if (audio_track[ii]) g_free(audio_track[ii]);
242                 if (audio_enc[ii]) g_free(audio_enc[ii]);
243                 if (audio_bitrate[ii]) g_free(audio_bitrate[ii]);
244                 if (audio_rate[ii]) g_free(audio_rate[ii]);
245                 if (audio_mix[ii]) g_free(audio_mix[ii]);
246                 if (audio_drc[ii]) g_free(audio_drc[ii]);
247                 audio_track[ii] = NULL;
248                 audio_enc[ii] = NULL;
249                 audio_bitrate[ii] = NULL;
250                 audio_rate[ii] = NULL;
251                 audio_mix[ii] = NULL;
252                 audio_drc[ii] = NULL;
253         }
254 }
255
256 static void
257 end_element(
258         GMarkupParseContext *ctx, 
259         const gchar *name, 
260         gpointer ud,
261         GError **error)
262 {
263         parse_data_t *pd = (parse_data_t*)ud;
264
265         if (IS_TAG(name, "string") ||
266                 IS_TAG(name, "integer") ||
267                 IS_TAG(name, "real"))
268         {
269                 if (IS_KEY(pd->key, "PresetName"))
270                 {
271                         if (pd->preset)
272                         {
273                                 g_message("Preset named twice");
274                         }
275                         else
276                                 pd->preset = g_strdup(pd->value);
277                         pd->state = NONE;
278                         return;
279                 }
280                 const gchar *my_key;
281                 my_key = (const gchar*)g_hash_table_lookup(pd->xlat_key, pd->key);
282                 if (my_key != NULL)
283                 { // Do something with it
284                         if (my_key[0] != 0) // intentionally ignored keys
285                         {
286                                 if (pd->value != NULL)
287                                 {
288                                         g_hash_table_insert(pd->settings, 
289                                                 g_strdup(my_key), g_strdup(pd->value));
290                                 }
291                                 else
292                                 {
293                                         g_message("NULL value");
294                                 }
295                         }
296                 }
297                 else if (IS_KEY(pd->key, "Audio1Encoder"))
298                 {
299                         if (audio_enc[0]) g_free(audio_enc[0]);
300                         audio_enc[0] = g_strdup(pd->value);
301                 }
302                 else if (IS_KEY(pd->key, "Audio1Bitrate"))
303                 {
304                         if (audio_bitrate[0]) g_free(audio_bitrate[0]);
305                         audio_bitrate[0] = g_strdup(pd->value);
306                 }
307                 else if (IS_KEY(pd->key, "Audio1Samplerate"))
308                 {
309                         if (audio_rate[0]) g_free(audio_rate[0]);
310                         audio_rate[0] = g_strdup(pd->value);
311                 }
312                 else if (IS_KEY(pd->key, "Audio1Mixdown"))
313                 {
314                         if (audio_mix[0]) g_free(audio_mix[0]);
315                         audio_mix[0] = g_strdup(pd->value);
316                 }
317                 else if (IS_KEY(pd->key, "Audio1TrackDRCSlider"))
318                 {
319                         if (audio_drc[0]) g_free(audio_drc[0]);
320                         audio_drc[0] = g_strdup(pd->value);
321                 }
322                 else if (IS_KEY(pd->key, "Audio1Track"))
323                 {
324                         if (audio_track[0]) g_free(audio_track[0]);
325                         audio_track[0] = g_strdup(pd->value);
326                 }
327                 else if (IS_KEY(pd->key, "Audio2Encoder"))
328                 {
329                         if (audio_enc[1]) g_free(audio_enc[1]);
330                         audio_enc[1] = g_strdup(pd->value);
331                 }
332                 else if (IS_KEY(pd->key, "Audio2Bitrate"))
333                 {
334                         if (audio_bitrate[1]) g_free(audio_bitrate[1]);
335                         audio_bitrate[1] = g_strdup(pd->value);
336                 }
337                 else if (IS_KEY(pd->key, "Audio2Samplerate"))
338                 {
339                         if (audio_rate[1]) g_free(audio_rate[1]);
340                         audio_rate[1] = g_strdup(pd->value);
341                 }
342                 else if (IS_KEY(pd->key, "Audio2Mixdown"))
343                 {
344                         if (audio_mix[1]) g_free(audio_mix[1]);
345                         audio_mix[1] = g_strdup(pd->value);
346                 }
347                 else if (IS_KEY(pd->key, "Audio2TrackDRCSlider"))
348                 {
349                         if (audio_drc[1]) g_free(audio_drc[1]);
350                         audio_drc[1] = g_strdup(pd->value);
351                 }
352                 else if (IS_KEY(pd->key, "Audio2Track"))
353                 {
354                         if (audio_track[1]) g_free(audio_track[1]);
355                         audio_track[1] = g_strdup(pd->value);
356                 }
357                 else if (IS_KEY(pd->key, "VideoQualityType"))
358                 {
359                         // VideoQualityType/0/1/2 - vquality_type_/target/bitrate/constant
360                         if (IS_VAL(pd->value, "0"))
361                         {
362                                 g_hash_table_insert(pd->settings, 
363                                                 g_strdup("vquality_type_target"), 
364                                                 g_strdup("1"));
365                                 g_hash_table_remove(pd->settings, "vquality_type_bitrate");
366                                 g_hash_table_remove(pd->settings, "vquality_type_constant");
367                         }
368                         else if (IS_VAL(pd->value, "1"))
369                         {
370                                 g_hash_table_remove(pd->settings, "vquality_type_target");
371                                 g_hash_table_insert(pd->settings, 
372                                                 g_strdup("vquality_type_bitrate"), 
373                                                 g_strdup("1"));
374                                 g_hash_table_remove(pd->settings, "vquality_type_constant");
375                         }
376                         else if (IS_VAL(pd->value, "2"))
377                         {
378                                 g_hash_table_remove(pd->settings, "vquality_type_target");
379                                 g_hash_table_remove(pd->settings, "vquality_type_bitrate");
380                                 g_hash_table_insert(pd->settings, 
381                                                 g_strdup("vquality_type_constant"), 
382                                                 g_strdup("1"));
383                         }
384                 }
385                 else
386                 {
387                         g_message("Key not found (%s)", pd->key);
388                 }
389         }
390         else if (IS_TAG(name, "dict"))
391         {
392                 gint ii;
393                 do_audio(pd);
394                 clear_audio();
395                 save_preset(pd);
396
397                 if (pd->preset) 
398                 {
399                         g_free(pd->preset);
400                         pd->preset = NULL;
401                 }
402                 else
403                         g_message("Preset has no name");
404                 g_hash_table_remove_all(pd->settings);
405         }
406         pd->state = NONE;
407 }
408
409 static gboolean
410 is_number(const gchar *str)
411 {
412         gboolean result = TRUE;
413         gint ii;
414         for (ii = 0; str[ii] != 0; ii++)
415         {
416                 if (!g_ascii_isdigit(str[ii]) && str[ii] != '.')
417                         result = FALSE;
418         }
419         return result;
420 }
421
422 static void
423 text_data(
424         GMarkupParseContext *ctx, 
425         const gchar *text, 
426         gsize len,
427         gpointer ud,
428         GError **error)
429 {
430         gboolean is_value = FALSE;
431         parse_data_t *pd = (parse_data_t*)ud;
432         const gchar *val = NULL;
433
434         if (pd->state == KEY)
435         {
436                 if (pd->key) g_free(pd->key);
437                 pd->key = g_strdup(text);
438                 return;
439         }
440         if (pd->state == STR)
441         {
442                 val = (gchar*)g_hash_table_lookup(pd->xlat_value, text);
443                 if (val != NULL)
444                 { // Do something with it
445                 }
446                 else if (IS_KEY(pd->key, "PresetName") ||
447                                 IS_KEY(pd->key, "PresetDescription") ||
448                                 IS_KEY(pd->key, "x264Option") ||
449                                 is_number(text))
450                 {
451                         val = text;
452                 }
453                 else
454                 {
455                         g_message("Unrecognized val (%s)", text);
456                 }
457         }
458         if (pd->state == INT || pd->state == REAL)
459         {
460                 val = text;
461         }
462
463         // Some keys need further translation of their values
464         if (val)
465         {
466                 if (IS_KEY(pd->key, "PictureDeinterlace"))
467                 {
468                         if (IS_VAL(val, "0"))
469                         {
470                                 val = "none";
471                         }
472                         else if (IS_VAL(val, "1"))
473                         {
474                                 val = "fast";
475                         }
476                         else if (IS_VAL(val, "2"))
477                         {
478                                 val = "slow";
479                         }
480                         else if (IS_VAL(val, "3"))
481                         {
482                                 val = "slower";
483                         }
484                 }
485                 else if (IS_KEY(pd->key, "Audio1Samplerate") ||
486                                 IS_KEY(pd->key, "Audio2Samplerate"))
487                 {
488                         if (IS_VAL(val, "auto"))
489                         {
490                                 val = "source";
491                         }
492                 }
493                 else if (IS_KEY(pd->key, "Audio1Mixdown") ||
494                                 IS_KEY(pd->key, "Audio2Mixdown"))
495                 {
496                         if (IS_VAL(val, "ac3"))
497                         {
498                                 val = "none";
499                         }
500                 }
501                 if (pd->value) g_free(pd->value);
502                 pd->value = g_strdup(val);
503         }
504 }
505
506 static void
507 passthrough(
508         GMarkupParseContext *ctx, 
509         const gchar *text, 
510         gsize len,
511         gpointer ud,
512         GError **error)
513 {
514         parse_data_t *pd = (parse_data_t*)ud;
515
516         g_debug("passthrough %s", text);
517 }
518
519 static void
520 delete_key(gpointer str)
521 {
522         g_free(str);
523 }
524
525 static void
526 delete_value(gpointer str)
527 {
528         g_free(str);
529 }
530
531 typedef struct
532 {
533         gchar *from;
534         gchar *to;
535 } xlat_t;
536
537 static xlat_t keys[] =
538 {
539         {"VFR", "variable_frame_rate"},
540         {"ChapterMarkers", "chapter_markers"},
541         {"Default", ""},
542         {"FileFormat", "container"},
543         {"PictureAutoCrop", "autocrop"},
544         {"PictureBottomCrop", ""},
545         {"PictureTopCrop", ""},
546         {"PictureLeftCrop", ""},
547         {"PictureRightCrop", ""},
548         {"PictureDeblock", "deblock"},
549         {"PictureDeinterlace", "deinterlace"}, // v
550         {"PictureDenoise", "denoise"}, // v
551         {"PictureDetelecine", "detelecine"},
552         {"PictureHeight", "max_height"},
553         {"PictureWidth", "max_width"},
554         {"PictureKeepRatio", "keep_aspect"},
555         {"PicturePAR", "anamorphic"}, // v
556         {"PresetDescription", "preset_description"},
557         {"Subtitles", "subtitle_lang"},
558         {"Subtitles", "subtitle_lang"},
559         {"Type", ""}, // preset type builtin/custom
560         {"UsesMaxPictureSettings", "autoscale"},
561         {"UsesPictureFilters", ""},
562         {"UsesPictureSettings", ""},
563         {"VideoAvgBitrate", "video_bitrate"},
564         {"VideoEncoder", "video_codec"},
565         {"VideoFramerate", "framerate"},
566         {"VideoGrayScale", "grayscale"},
567         {"VideoQualitySlider", "video_quality"},
568         {"VideoTargetSize", "video_target_size"},
569         {"VideoTurboTwoPass", "turbo"},
570         {"VideoTwoPass", "two_pass"},
571         {"x264Option", "x264_options"},
572         {"Mp4LargeFile", "large_mp4"},
573         {"Mp4iPodCompatible", "ipod_file"},
574         {NULL, NULL}
575 };
576
577 // VideoQualityType/0/1/2 - vquality_type_/target/bitrate/constant
578 // Audio1Bitrate - pref_audio_bitrate
579 // Audio1Encoder - pref_audio_codec
580 // Audio1Mixdown - pref_audio_mix
581 // Audio1Samplerate - pref_audio_rate
582 // Audio1Track - na
583 // Audio1DRCSlider - pref_audio_drc
584
585 static xlat_t values[] =
586 {
587         {"AAC (faac)", "faac"},
588         {"AC3 Passthru", "ac3"},
589         {"H.264 (x264)", "x264"},
590         {"MPEG-4 (FFmpeg)", "x264"},
591         {"Dolby Pro Logic II", "dpl2"},
592         {"Auto", "auto"},
593         {"MKV file", "mkv"},
594         {"MP4 file", "mp4"},
595         {"None", "none"},
596         {"Same as source", "source"},
597         {"160", "160"},
598         {NULL, NULL}
599 };
600
601 static void
602 store_key_file(GKeyFile *key_file, const gchar *name)
603 {
604     gchar *settingsString;
605     gsize length;
606     gint fd;
607
608     settingsString = g_key_file_to_data(key_file, &length, NULL);
609
610     fd = g_open(name, O_RDWR|O_CREAT|O_TRUNC, 0777);
611     write(fd, settingsString, length);
612     close(fd);
613     g_free(settingsString);
614 }
615
616 static void
617 parse_it(gchar *buf, gssize len)
618 {
619         GMarkupParseContext *ctx;
620         GMarkupParser parser;
621         parse_data_t pd;
622
623         null_audio();
624         presets = g_key_file_new();
625         pd.state = START;
626         pd.key = NULL;
627         pd.value = NULL;
628         pd.preset = NULL;
629         pd.settings = g_hash_table_new_full(g_str_hash, g_str_equal, 
630                                                                           delete_key, delete_value);
631         pd.xlat_key = g_hash_table_new(g_str_hash, g_str_equal);
632         gint ii;
633         for (ii = 0; keys[ii].from != NULL; ii++)
634         {
635                 g_hash_table_insert(pd.xlat_key, keys[ii].from, keys[ii].to);
636         }
637         pd.xlat_value = g_hash_table_new(g_str_hash, g_str_equal);
638         for (ii = 0; values[ii].from != NULL; ii++)
639         {
640                 g_hash_table_insert(pd.xlat_value, values[ii].from, values[ii].to);
641         }
642         parser.start_element = start_element;
643         parser.end_element = end_element;
644         parser.text = text_data;
645         parser.passthrough = passthrough;
646         ctx = g_markup_parse_context_new(&parser, 0, &pd, NULL);
647         g_markup_parse_context_parse(ctx, buf, len, NULL);
648         store_key_file(presets, "xlat_presets");
649 }
650
651 gint
652 main(gint argc, gchar *argv[])
653 {
654         FILE *fd;
655         gchar buffer[BUF_SIZ];
656         size_t size;
657
658         fd = fopen(argv[1], "r");
659         size = fread(buffer, 1, BUF_SIZ, fd);
660         if (size >= BUF_SIZ)
661         {
662                 g_error("buffer too small");
663                 exit(1);
664         }
665         buffer[size] = 0;
666         parse_it(buffer, (gssize)size);
667 }
668