1 /*
2 Copyright (C) 2001, 2005 Herbert Valerio Riedel <hvr@gnu.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <sys/types.h>
24
25 #include <dirent.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <popt.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #ifdef HAVE_SYS_MMAN_H
33 #include <sys/mman.h>
34 #endif
35 #include <sys/stat.h>
36 #include <unistd.h>
37
38 #include <libvcd/sector.h>
39
40 /* Private headers */
41 #include "data_structures.h"
42 #include "pbc.h"
43 #include "util.h"
44 #include "vcd.h"
45
46 #include "vcd_xml_dtd.h"
47 #include "vcd_xml_dump.h"
48 #include "vcdxml.h"
49 #include "vcd_xml_common.h"
50
51 /* defaults */
52 #define DEFAULT_SYSTEM_ID "CD-RTOS CD-BRIDGE"
53 #define DEFAULT_VOLUME_ID "VIDEOCD"
54 #define DEFAULT_APPLICATION_ID ""
55 #define DEFAULT_ALBUM_ID ""
56 #define DEFAULT_TYPE "vcd2"
57 #define DEFAULT_XML_FNAME "videocd.xml"
58
59 static int _verbose_flag = 0;
60 static int _quiet_flag = 0;
61
62 /****************************************************************************/
63
64 static vcd_type_t
_parse_type_arg(const char arg[])65 _parse_type_arg (const char arg[])
66 {
67 struct {
68 const char *str;
69 vcd_type_t id;
70 } type_str[] =
71 {
72 { "vcd10", VCD_TYPE_VCD },
73 { "vcd11", VCD_TYPE_VCD11 },
74 { "vcd2", VCD_TYPE_VCD2 },
75 { "vcd20", VCD_TYPE_VCD2 },
76 { "svcd", VCD_TYPE_SVCD },
77 { "hqvcd", VCD_TYPE_HQVCD },
78 { NULL, VCD_TYPE_INVALID }
79 };
80
81 int i = 0;
82
83 while (type_str[i].str)
84 if (strcasecmp(arg, type_str[i].str))
85 i++;
86 else
87 break;
88
89 if (!type_str[i].str)
90 fprintf (stderr, "invalid type given\n");
91
92 return type_str[i].id;
93 }
94
95 static int
_parse_file_arg(const char * arg,char ** fname1,char ** fname2)96 _parse_file_arg (const char *arg, char **fname1, char **fname2)
97 {
98 int rc = 0;
99 char *tmp, *arg_cpy = strdup (arg);
100
101 *fname1 = *fname2 = NULL;
102
103 tmp = strtok(arg_cpy, ",");
104 if (tmp)
105 *fname1 = strdup (tmp);
106 else
107 rc = -1;
108
109 tmp = strtok(NULL, ",");
110 if (tmp)
111 *fname2 = strdup (tmp);
112 else
113 rc = -1;
114
115 tmp = strtok(NULL, ",");
116 if (tmp)
117 rc = -1;
118
119 free (tmp);
120
121 if(rc)
122 {
123 free (*fname1);
124 free (*fname2);
125
126 *fname1 = *fname2 = NULL;
127 }
128
129 return rc;
130 }
131
132 static void
_add_dir(vcdxml_t * obj,const char pathname[])133 _add_dir (vcdxml_t *obj, const char pathname[])
134 {
135 vcd_assert (pathname != NULL);
136
137 {
138 struct filesystem_t *_file = calloc(1, sizeof (struct filesystem_t));
139
140 _file->name = strdup (pathname);
141 _file->file_src = NULL;
142 _file->file_raw = false;
143
144 _cdio_list_append (obj->filesystem, _file);
145 }
146 }
147
148 static void
_add_dirtree(vcdxml_t * obj,const char pathname[],const char iso_pathname[])149 _add_dirtree (vcdxml_t *obj, const char pathname[],
150 const char iso_pathname[])
151 {
152 DIR *dir = NULL;
153 struct dirent *dentry = NULL;
154
155 vcd_assert (pathname != NULL);
156 vcd_assert (iso_pathname != NULL);
157
158 dir = opendir (pathname);
159
160 if (!dir)
161 {
162 perror ("--add-dirtree: opendir()");
163 exit (EXIT_FAILURE);
164 }
165
166 while ((dentry = readdir (dir)))
167 {
168 char buf[1024] = { 0, };
169 char iso_name[1024] = { 0, };
170 struct stat st;
171
172 if (!strcmp (dentry->d_name, "."))
173 continue;
174
175 if (!strcmp (dentry->d_name, ".."))
176 continue;
177
178 strcat (buf, pathname);
179 strcat (buf, "/");
180 strcat (buf, dentry->d_name);
181
182 strcat (iso_name, dentry->d_name);
183
184 if (stat (buf, &st))
185 perror ("stat()");
186
187 if (S_ISDIR(st.st_mode))
188 {
189 strcat (iso_name, "/");
190 _add_dirtree (obj, buf, iso_name);
191 }
192 else if (S_ISREG(st.st_mode))
193 {
194 struct filesystem_t *_file = calloc(1, sizeof (struct filesystem_t));
195
196 _file->name = strdup (iso_name);
197 _file->file_src = strdup (buf);
198 _file->file_raw = false;
199
200 _cdio_list_append (obj->filesystem, _file);
201 }
202 else
203 fprintf (stdout, "ignoring %s\n", buf);
204 }
205
206 closedir (dir);
207 }
208
209 static const char *
_lid_str(int n)210 _lid_str (int n)
211 {
212 static char buf[16];
213
214 snprintf (buf, sizeof (buf), "lid-%3.3d", n);
215
216 return buf;
217 }
218
219 static const char *
_sequence_str(int n)220 _sequence_str (int n)
221 {
222 static char buf[16];
223
224 snprintf (buf, sizeof (buf), "sequence-%2.2d", n);
225
226 return buf;
227 }
228
229 int
main(int argc,const char * argv[])230 main (int argc, const char *argv[])
231 {
232 vcdxml_t obj;
233 int n;
234
235 char *xml_fname = strdup (DEFAULT_XML_FNAME);
236 char *type_str = strdup (DEFAULT_TYPE);
237 int broken_svcd_mode_flag = 0;
238 int update_scan_offsets_flag = 0;
239 int nopbc_flag = 0;
240
241 vcd_xml_progname = "vcdxgen";
242
243 vcd_xml_init (&obj);
244
245 vcd_xml_log_init ();
246
247 obj.pvd.system_id = strdup (DEFAULT_SYSTEM_ID);
248 obj.pvd.volume_id = strdup (DEFAULT_VOLUME_ID);
249 obj.pvd.application_id = strdup (DEFAULT_APPLICATION_ID);
250 obj.info.album_id = strdup (DEFAULT_ALBUM_ID);
251
252 obj.info.volume_count = 1;
253 obj.info.volume_number = 1;
254
255 {
256 const char **args = NULL;
257 int opt = 0;
258
259 enum {
260 CL_VERSION = 1,
261 CL_ADD_DIR,
262 CL_ADD_DIRTREE,
263 CL_ADD_FILE,
264 CL_ADD_FILE_RAW
265 };
266
267 struct poptOption optionsTable[] =
268 {
269 {"output-file", 'o', POPT_ARG_STRING, &xml_fname, 0,
270 "specify xml file for output (default: '" DEFAULT_XML_FNAME "')",
271 "FILE"},
272
273 {"type", 't', POPT_ARG_STRING, &type_str, 0,
274 "select VideoCD type ('vcd11', 'vcd2', 'svcd' or 'hqvcd')"
275 " (default: '" DEFAULT_TYPE "')", "TYPE"},
276
277 {"iso-volume-label", 'l', POPT_ARG_STRING, &obj.pvd.volume_id, 0,
278 "specify ISO volume label for video cd (default: '" DEFAULT_VOLUME_ID
279 "')", "LABEL"},
280
281 {"iso-application-id", '\0', POPT_ARG_STRING, &obj.pvd.application_id, 0,
282 "specify ISO application id for video cd (default: '" DEFAULT_APPLICATION_ID
283 "')", "LABEL"},
284
285 {"info-album-id", '\0', POPT_ARG_STRING, &obj.info.album_id, 0,
286 "specify album id for video cd set (default: '" DEFAULT_ALBUM_ID
287 "')", "LABEL"},
288
289 {"volume-count", '\0', POPT_ARG_INT, &obj.info.volume_count, 0,
290 "specify number of volumes in album set", "NUMBER"},
291
292 {"volume-number", '\0', POPT_ARG_INT, &obj.info.volume_number, 0,
293 "specify album set sequence number (< volume-count)", "NUMBER"},
294
295 {"broken-svcd-mode", '\0', POPT_ARG_NONE, &broken_svcd_mode_flag, 0,
296 "enable non-compliant compatibility mode for broken devices"},
297
298 {"update-scan-offsets", '\0', POPT_ARG_NONE, &update_scan_offsets_flag, 0,
299 "update scan data offsets in video mpeg2 stream"},
300
301 {"nopbc", '\0', POPT_ARG_NONE, &nopbc_flag, 0, "don't create PBC"},
302
303 {"add-dirtree", '\0', POPT_ARG_STRING, NULL, CL_ADD_DIRTREE,
304 "add directory contents recursively to ISO fs root", "DIR"},
305
306 {"add-dir", '\0', POPT_ARG_STRING, NULL, CL_ADD_DIR,
307 "add empty dir to ISO fs", "ISO_DIRNAME"},
308
309 {"add-file", '\0', POPT_ARG_STRING, NULL, CL_ADD_FILE,
310 "add single file to ISO fs", "FILE,ISO_FILENAME"},
311
312 {"add-file-2336", '\0', POPT_ARG_STRING, NULL, CL_ADD_FILE_RAW,
313 "add file containing full 2336 byte sectors to ISO fs",
314 "FILE,ISO_FILENAME"},
315
316 { "filename-encoding", '\0', POPT_ARG_STRING, &vcd_xml_filename_charset, 0,
317 "use given charset encoding for filenames instead of UTF8" },
318
319 {"verbose", 'v', POPT_ARG_NONE, &_verbose_flag, 0, "be verbose"},
320
321 {"quiet", 'q', POPT_ARG_NONE, &_quiet_flag, 0, "show only critical messages"},
322
323 {"version", 'V', POPT_ARG_NONE, NULL, CL_VERSION,
324 "display version and copyright information and exit"},
325
326 POPT_AUTOHELP
327
328 {NULL, 0, 0, NULL, 0}
329 };
330
331 poptContext optCon = poptGetContext ("vcdimager", argc, argv, optionsTable, 0);
332 poptSetOtherOptionHelp (optCon, "mpeg-track1 [mpeg-track2...]");
333
334 if (poptReadDefaultConfig (optCon, 0))
335 fprintf (stderr, "warning, reading popt configuration failed\n");
336
337 while ((opt = poptGetNextOpt (optCon)) != -1)
338 switch (opt)
339 {
340 case CL_VERSION:
341 /* vcd_xml_gui_mode = gl.gui_flag; */
342 vcd_xml_print_version ();
343 exit (EXIT_SUCCESS);
344 break;
345
346 case CL_ADD_DIRTREE:
347 {
348 const char *arg = poptGetOptArg (optCon);
349
350 vcd_assert (arg != NULL);
351
352 _add_dirtree (&obj, arg, "");
353 }
354 break;
355
356 case CL_ADD_DIR:
357 {
358 const char *arg = poptGetOptArg (optCon);
359
360 vcd_assert (arg != NULL);
361
362 _add_dir (&obj, arg);
363 }
364 break;
365
366 case CL_ADD_FILE:
367 case CL_ADD_FILE_RAW:
368 {
369 const char *arg = poptGetOptArg (optCon);
370 char *fname1 = NULL, *fname2 = NULL;
371
372 vcd_assert (arg != NULL);
373
374 if(!_parse_file_arg (arg, &fname1, &fname2))
375 {
376 struct filesystem_t *_file = calloc(1, sizeof (struct filesystem_t));
377
378 _file->name = strdup (fname2);
379 _file->file_src = strdup (fname1);
380 _file->file_raw = (opt == CL_ADD_FILE_RAW);
381
382 _cdio_list_append (obj.filesystem, _file);
383 }
384 else
385 {
386 fprintf (stderr, "file parsing of `%s' failed\n", arg);
387 exit (EXIT_FAILURE);
388 }
389 }
390 break;
391
392 default:
393 fprintf (stderr, "error while parsing command line - try --help\n");
394 exit (EXIT_FAILURE);
395 break;
396 }
397
398 if (_verbose_flag && _quiet_flag)
399 fprintf (stderr, "I can't be both, quiet and verbose... either one or another ;-)");
400
401 if ((args = poptGetArgs (optCon)) == NULL)
402 {
403 fprintf (stderr, "error: need at least one data track as argument "
404 "-- try --help\n");
405 exit (EXIT_FAILURE);
406 }
407
408 for (n = 0; args[n]; n++)
409 {
410 struct sequence_t *_seq = calloc(1, sizeof (struct sequence_t));
411
412 _seq->entry_point_list = _cdio_list_new ();
413 _seq->autopause_list = _cdio_list_new ();
414 _seq->src = strdup (args[n]);
415 _seq->id = strdup (_sequence_str (n));
416
417 _cdio_list_append (obj.sequence_list, _seq);
418 }
419
420 if (_cdio_list_length (obj.sequence_list) > CDIO_CD_MAX_TRACKS - 1)
421 {
422 fprintf (stderr, "error: maximal number of supported mpeg tracks (%d) reached",
423 CDIO_CD_MAX_TRACKS - 1);
424 exit (EXIT_FAILURE);
425 }
426
427 if ((obj.vcd_type = _parse_type_arg (type_str)) == VCD_TYPE_INVALID)
428 exit (EXIT_FAILURE);
429
430 poptFreeContext (optCon);
431 }
432
433 if (_quiet_flag)
434 vcd_xml_verbosity = VCD_LOG_WARN;
435 else if (_verbose_flag)
436 vcd_xml_verbosity = VCD_LOG_DEBUG;
437 else
438 vcd_xml_verbosity = VCD_LOG_INFO;
439
440 /* done with argument processing */
441
442 if (obj.vcd_type == VCD_TYPE_VCD11
443 || obj.vcd_type == VCD_TYPE_VCD)
444 nopbc_flag = true;
445
446 if (!nopbc_flag)
447 {
448 pbc_t *_pbc;
449 CdioListNode_t *node;
450
451 int n = 0;
452 _CDIO_LIST_FOREACH (node, obj.sequence_list)
453 {
454 struct sequence_t *_sequence = _cdio_list_node_data (node);
455
456 _pbc = vcd_pbc_new (PBC_PLAYLIST);
457
458 _pbc->id = strdup (_lid_str (n));
459
460 if (n)
461 _pbc->prev_id = strdup (_lid_str (n - 1));
462
463 if (_cdio_list_node_next (node))
464 _pbc->next_id = strdup (_lid_str (n + 1));
465 else
466 _pbc->next_id = strdup ("lid-end");
467
468 _pbc->retn_id = strdup ("lid-end");
469
470 _pbc->wait_time = 5;
471
472 _cdio_list_append (_pbc->item_id_list, strdup (_sequence->id));
473
474 _cdio_list_append (obj.pbc_list, _pbc);
475
476 n++;
477 }
478
479 /* create end list */
480
481 _pbc = vcd_pbc_new (PBC_END);
482 _pbc->id = strdup ("lid-end");
483 _pbc->rejected = true;
484
485 _cdio_list_append (obj.pbc_list, _pbc);
486 }
487
488 if ((obj.vcd_type == VCD_TYPE_SVCD
489 || obj.vcd_type == VCD_TYPE_HQVCD)
490 && update_scan_offsets_flag)
491 {
492 struct option_t *_opt = calloc(1, sizeof (struct option_t));
493
494 _opt->name = strdup (OPT_UPDATE_SCAN_OFFSETS);
495 _opt->value = strdup ("true");
496
497 _cdio_list_append (obj.option_list, _opt);
498 }
499
500 if (obj.vcd_type == VCD_TYPE_SVCD
501 && broken_svcd_mode_flag)
502 {
503 struct option_t *_opt = calloc(1, sizeof (struct option_t));
504
505 _opt->name = strdup (OPT_SVCD_VCD3_MPEGAV);
506 _opt->value = strdup ("true");
507
508 _cdio_list_append (obj.option_list, _opt);
509
510 _opt = calloc(1, sizeof (struct option_t));
511
512 _opt->name = strdup (OPT_SVCD_VCD3_ENTRYSVD);
513 _opt->value = strdup ("true");
514
515 _cdio_list_append (obj.option_list, _opt);
516 }
517
518 vcd_xml_dump (&obj, xml_fname);
519
520 fprintf (stdout, "(Super) VideoCD xml description created successfully as `%s'\n",
521 xml_fname);
522
523 return EXIT_SUCCESS;
524 }
525
526
527 /*
528 * Local variables:
529 * c-file-style: "gnu"
530 * tab-width: 8
531 * indent-tabs-mode: nil
532 * End:
533 */
534