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