1 /*
2     Copyright (C) 2002, 2003, 2004, 2005, 2006, 2018
3     Rocky Bernstein <rocky@gnu.org>
4     Copyright (C) 2001, 2002 Herbert Valerio Riedel <hvr@gnu.org>
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Foundation
18     Software, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 #include <stdio.h>
26 #ifdef HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #endif
32 #include <stddef.h>
33 
34 #include <popt.h>
35 /* Accomodate to older popt that doesn't support the "optional" flag */
36 #ifndef POPT_ARGFLAG_OPTIONAL
37 #define POPT_ARGFLAG_OPTIONAL 0
38 #endif
39 
40 /* We don't want to pull in cdio's config */
41 #define __CDIO_CONFIG_H__
42 #include <cdio/cdio.h>
43 #include <cdio/bytesex.h>
44 #include <cdio/iso9660.h>
45 
46 /* Eventually move above libvcd includes but having vcdinfo including. */
47 #include <libvcd/info.h>
48 #include <libvcd/files.h>
49 
50 /* FIXME */
51 #include <libvcd/files_private.h>
52 
53 /* Private headers */
54 #include "bitvec.h"
55 #include "pbc.h"
56 
57 static const char _rcsid[] = "$Id$";
58 
59 static const char DELIM[] = \
60 "----------------------------------------" \
61 "---------------------------------------\n";
62 
63 /* global static vars */
64 static struct gl_t
65 {
66   int  source_type;
67   char *access_mode;
68 
69   /* Boolean values set by command-line options to reduce output.
70      Note: because these are used by popt, the datatype here has to be
71      int, not bool.
72   */
73   uint32_t debug_level;
74   int      no_ext_psd_flag;  /* Ignore information in /EXT/PSD_X.VCD */
75   int      quiet_flag;
76   int      suppress_warnings;
77 
78   struct show_t
79   {
80     int all;     /* True makes all of the below "show" variables true. */
81 
82     struct no_t  /* Switches that are on by default and you turn off. */
83     {
84       int banner;     /* True supresses initial program banner and Id */
85       int delimiter;  /* True supresses delimiters between sections   */
86       int header;     /* True supresses the section headers           */
87     } no;
88 
89     struct entries_t  /* Switches for the ENTRIES section. */
90     {
91       int any;   /* True if any of the below variables are set true. */
92       int all;   /* True makes all of the below variables set true.  */
93       int count; /* Show total number of entries.                    */
94       int data;  /* Show all of the entry points .                   */
95       int id;    /* Show INFO Id */
96       int prof;  /* Show system profile tag. */
97       int vers;  /* Show version */
98     } entries;
99 
100     struct info_t     /* Switches for the INFO section. */
101     {
102       int any;
103       int all;
104       int album; /* Show album description info. */
105       int cc;    /* Show data cc. */
106       int count; /* Show volume count */
107       int id;    /* Show ID */
108       int ofm;   /* Show offset multiplier */
109       int lid2;  /* Show start LID #2 */
110       int lidn;  /* Show maximum LID */
111       int pal;   /* Show PAL flags and reserved1 */
112       int pbc;   /* Show reserved2 or extended pbc */
113       int prof;  /* Show system profile flag */
114       int psds;  /* Show PSD size. */
115       int res;   /* Show restriction */
116       int seg;   /* Show first segment address */
117       int segn;  /* Show number of segments */
118       int segs;  /* Show segments */
119       int spec;  /* Show special info */
120       int start; /* Show volume start times */
121       int st2;   /* Show start track #2 */
122       int vers;  /* Show INFO version. */
123       int vol;   /* Show volume number */
124     } info;
125 
126     struct pvd_t  /* Switches for the PVD section. */
127     {
128       int any;    /* True if any of the below variables are set true. */
129       int all;    /* True makes all of the below variables set true.  */
130       int app;    /* show application ID */
131       int id;     /* show PVD ID */
132       int iso;    /* Show ISO size */
133       int prep;   /* Show preparer ID */
134       int pub;    /* Show publisher ID */
135       int sys;    /* Show system id */
136       int vers;   /* Show version number */
137       int vol;    /* Show volume ID */
138       int volset; /* Show volumeset ID */
139       int xa;     /* Show if XA marker is present. */
140     } pvd;
141 
142     int format;   /* Show VCD format VCD 1.1, VCD 2.0, SVCD, ... */
143     int fs;
144     int lot;
145     int psd;      /* Show PSD group -- needs to be broken out. */
146     int scandata; /* Show scan data group -- needs to broken out. */
147     int search;
148     int source;   /* Show image source and size. */
149     int tracks;
150   } show;
151 
152 }
153 gl;                             /* global variables */
154 
155 poptContext optCon;
156 
157 /* end of vars */
158 
159 
160 #define PRINTED_POINTS 15
161 
162 
163 static bool
_bitset_get_bit(const uint8_t bitvec[],int bit)164 _bitset_get_bit (const uint8_t bitvec[], int bit)
165 {
166   bool result = false;
167 
168   if (_vcd_bit_set_p (bitvec[bit / 8], (bit % 8)))
169     result = true;
170 
171   return result;
172 }
173 
174 /******************************************************************************/
175 static void
_hexdump(const void * data,unsigned len)176 _hexdump (const void *data, unsigned len)
177 {
178   unsigned n;
179   const uint8_t *bytes = data;
180 
181   for (n = 0; n < len; n++)
182     {
183       if (n % 8 == 0)
184         fprintf (stdout, " ");
185       fprintf (stdout, "%2.2x ", bytes[n]);
186     }
187 }
188 
189 typedef enum {
190   PBC_VCD2_NO_PBC,      /* NO PBC */
191   PBC_VCD2_EXT,         /* Has extended PBC for VCD 2.0 */
192   PBC_VCD2_NOPE,        /* Is not VCD 2.0 */
193   PBC_VCD2_NO_LOT_X,    /* EXT/LOT_X.VCD doesn't exist */
194   PBC_VCD2_NO_PSD_X,    /* EXT/PSD_X.VCD doesn't exist */
195   PBC_VCD2_BAD_LOT_SIZE /* LOT_VCD_SIZE*BLOCKSIZE != size */
196 } vcd2_ext_pbc_status_t;
197 
198 static vcd2_ext_pbc_status_t
_has_vcd2_ext_pbc(const vcdinfo_obj_t * p_vcdinfo)199 _has_vcd2_ext_pbc (const vcdinfo_obj_t *p_vcdinfo)
200 {
201   iso9660_stat_t *statbuf;
202   CdIo_t *p_cdio;
203   vcd2_ext_pbc_status_t ret_status;
204 
205   if (!vcdinfo_has_pbc(p_vcdinfo))
206     return PBC_VCD2_NO_PBC;
207 
208   if (vcdinfo_get_VCD_type(p_vcdinfo) != VCD_TYPE_VCD2)
209     return PBC_VCD2_NOPE;
210 
211   p_cdio = vcdinfo_get_cd_image(p_vcdinfo);
212   statbuf = iso9660_fs_stat (p_cdio, "EXT/LOT_X.VCD;1");
213   if (NULL == statbuf)
214     return PBC_VCD2_NO_LOT_X;
215   if (statbuf->size != ISO_BLOCKSIZE * LOT_VCD_SIZE) {
216     ret_status = PBC_VCD2_BAD_LOT_SIZE;
217   } else {
218     free(statbuf);
219     statbuf = iso9660_fs_stat (p_cdio, "EXT/PSD_X.VCD;1");
220     if (NULL != statbuf) {
221       ret_status = PBC_VCD2_EXT;
222     } else {
223       ret_status = PBC_VCD2_NO_PSD_X;
224     }
225   }
226   free(statbuf);
227   return ret_status;
228 }
229 
230 
231 
232 static void
dump_lot(const vcdinfo_obj_t * p_vcdinfo,bool ext)233 dump_lot (const vcdinfo_obj_t *p_vcdinfo, bool ext)
234 {
235   const LotVcd_t *lot = ext
236     ? vcdinfo_get_lot_x(p_vcdinfo) : vcdinfo_get_lot(p_vcdinfo);
237 
238   unsigned n, tmp;
239   const uint16_t max_lid = vcdinfo_get_num_LIDs(p_vcdinfo);
240   unsigned int mult = vcdinfo_get_offset_mult(p_vcdinfo);
241 
242   if (!gl.show.no.header)
243     fprintf (stdout,
244              (vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_SVCD
245               || vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_HQVCD)
246              ? "SVCD/LOT.SVD\n"
247              : (ext ? "EXT/LOT_X.VCD\n": "VCD/LOT.VCD\n"));
248 
249   if (lot->reserved)
250     fprintf (stdout, " RESERVED = 0x%4.4x (should be 0x0000)\n",
251              uint16_from_be (lot->reserved));
252 
253   for (n = 0; n < LOT_VCD_OFFSETS; n++)
254     {
255       if ((tmp = uint16_from_be (lot->offset[n])) != PSD_OFS_DISABLED)
256         {
257           if (!n && tmp)
258             fprintf (stdout, "warning, LID[1] should have offset = 0!\n");
259 
260           if (n >= max_lid)
261             fprintf (stdout,
262                      "warning, the following entry is greater than the maximum lid field in info\n");
263           fprintf (stdout, " LID[%d]: offset = %d (0x%4.4x)\n",
264                    n + 1, tmp * mult, tmp);
265         }
266       else if (n < max_lid)
267         fprintf (stdout, " LID[%d]: rejected\n", n + 1);
268     }
269 }
270 
271 static void
dump_psd(const vcdinfo_obj_t * p_vcdinfo,bool ext)272 dump_psd (const vcdinfo_obj_t *p_vcdinfo, bool ext)
273 {
274   CdioListNode_t *node;
275   unsigned n = 0;
276   unsigned int mult;
277   uint8_t *psd;
278   CdioList_t *offset_list;
279 
280   if (!p_vcdinfo) return;
281 
282   mult = vcdinfo_get_offset_mult(p_vcdinfo);
283   psd  = ext ? vcdinfo_get_psd_x(p_vcdinfo) : vcdinfo_get_psd(p_vcdinfo);
284   offset_list = ext
285     ? vcdinfo_get_offset_x_list(p_vcdinfo)
286     : vcdinfo_get_offset_list(p_vcdinfo);
287 
288   fprintf (stdout,
289            (vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_SVCD
290             || vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_HQVCD)
291            ? "SVCD/PSD.SVD\n"
292            : (ext ? "EXT/PSD_X.VCD\n": "VCD/PSD.VCD\n"));
293 
294   _CDIO_LIST_FOREACH (node, offset_list)
295     {
296       vcdinfo_offset_t *ofs = _cdio_list_node_data (node);
297       unsigned _rofs = ofs->offset * mult;
298 
299       uint8_t type;
300 
301       type = psd[_rofs];
302 
303       switch (type)
304         {
305         case PSD_TYPE_PLAY_LIST:
306           {
307             const PsdPlayListDescriptor_t *pld = (const void *) (psd + _rofs);
308 
309             int i;
310             uint16_t lid = vcdinf_pld_get_lid(pld);
311 
312             fprintf (stdout,
313                      " PSD[%.2d] (%s): play list descriptor\n"
314                      "  NOI: %d | LID#: %d (rejected: %s)\n"
315                      "  prev: %s | next: %s | return: %s\n"
316                      "  playtime: %d/15s | wait: %ds | autowait: %ds\n",
317                      n, vcdinfo_ofs2str (p_vcdinfo, ofs->offset, ext),
318                      pld->noi, lid,
319                      _vcd_bool_str(vcdinfo_is_rejected(uint16_from_be(pld->lid))),
320                      vcdinfo_ofs2str(p_vcdinfo,
321                                      vcdinf_pld_get_prev_offset(pld), ext),
322                      vcdinfo_ofs2str(p_vcdinfo,
323                                      vcdinf_pld_get_next_offset(pld), ext),
324                      vcdinfo_ofs2str(p_vcdinfo,
325                                      vcdinf_pld_get_return_offset(pld),
326                                      ext),
327                      vcdinf_get_play_time(pld), vcdinf_get_wait_time (pld),
328                      vcdinf_get_autowait_time(pld));
329 
330             for (i = 0; i < pld->noi; i++) {
331               fprintf (stdout, "  play-item[%d]: %s\n", i,
332                        vcdinfo_pin2str(vcdinf_pld_get_play_item(pld,i)));
333             }
334             fprintf (stdout, "\n");
335           }
336           break;
337 
338         case PSD_TYPE_END_LIST:
339           {
340             const PsdEndListDescriptor_t *d = (const void *) (psd + _rofs);
341             fprintf (stdout, " PSD[%.2d] (%s): end list descriptor\n", n,
342                      vcdinfo_ofs2str (p_vcdinfo, ofs->offset, ext));
343             if (vcdinfo_get_VCD_type(p_vcdinfo) != VCD_TYPE_VCD2)
344               {
345                 fprintf (stdout,
346                          "  next disc number: %d (if 0 stop PBC handling)\n",
347                          d->next_disc);
348                 fprintf (stdout,
349                          "  change picture item: %s\n",
350                          vcdinfo_pin2str (uint16_from_be (d->change_pic)));
351               }
352             fprintf (stdout, "\n");
353           }
354           break;
355 
356         case PSD_TYPE_EXT_SELECTION_LIST:
357         case PSD_TYPE_SELECTION_LIST:
358           {
359             const PsdSelectionListDescriptor_t *d =
360               (const void *) (psd + _rofs);
361             int i;
362             const unsigned int lid=vcdinf_psd_get_lid(d);
363 
364             fprintf (stdout,
365                      "  PSD[%.2d] (%s): %sselection list descriptor\n"
366                      "  Flags: 0x%.2x | NOS: %d | BSN: %d | LID: %d (rejected: %s)\n"
367                      "  prev: %s | next: %s | return: %s\n"
368                      "  default: %s | timeout: %s\n"
369                      "  wait: %d secs | loop: %d (delayed: %s)\n"
370                      "  play-item: %s\n",
371                      n, vcdinfo_ofs2str (p_vcdinfo, ofs->offset, ext),
372                      (type == PSD_TYPE_EXT_SELECTION_LIST ? "extended " : ""),
373                      *(uint8_t *) &d->flags,
374                      vcdinf_get_num_selections(d),
375                      vcdinf_get_bsn(d),
376                      lid,
377                      _vcd_bool_str (vcdinf_psd_get_lid_rejected(d)),
378                      vcdinfo_ofs2str(p_vcdinfo,
379                                      vcdinf_psd_get_prev_offset(d),   ext),
380                      vcdinfo_ofs2str(p_vcdinfo,
381                                      vcdinf_psd_get_next_offset(d),   ext),
382                      vcdinfo_ofs2str(p_vcdinfo,
383                                      vcdinf_psd_get_return_offset(d), ext),
384                      vcdinfo_ofs2str(p_vcdinfo,
385                                      vcdinf_psd_get_default_offset(d),ext),
386                      vcdinfo_ofs2str(p_vcdinfo, vcdinf_get_timeout_offset(d),
387                                      ext),
388                      vcdinf_get_timeout_time(d),
389                      vcdinf_get_loop_count(d),
390                      _vcd_bool_str (vcdinf_has_jump_delay(d)),
391                      vcdinfo_pin2str (vcdinf_psd_get_itemid(d)));
392 
393             for (i = 0; i < vcdinf_get_num_selections(d); i++)
394               fprintf (stdout, "  ofs[%d]: %s\n", i,
395                        vcdinfo_ofs2str (p_vcdinfo,
396                                         vcdinf_psd_get_offset(d, i),
397                                         ext));
398 
399             if (type == PSD_TYPE_EXT_SELECTION_LIST
400                 || d->flags.SelectionAreaFlag)
401               {
402                 const PsdSelectionListDescriptorExtended_t *d2 =
403                   (const void *) &(d->ofs[d->nos]);
404 
405                 fprintf (stdout, "  prev_area: %s | next_area: %s\n",
406                          vcdinf_area_str (&d2->prev_area),
407                          vcdinf_area_str (&d2->next_area));
408 
409 
410                 fprintf (stdout, "  retn_area: %s | default_area: %s\n",
411                          vcdinf_area_str (&d2->return_area),
412                          vcdinf_area_str (&d2->default_area));
413 
414                 for (i = 0; i < vcdinf_get_num_selections(d); i++)
415                   fprintf (stdout, "  area[%d]: %s\n", i,
416                            vcdinf_area_str (&d2->area[i]));
417               }
418 
419             fprintf (stdout, "\n");
420           }
421           break;
422         default:
423           fprintf (stdout, " PSD[%2d] (%s): unkown descriptor type (0x%2.2x)\n",
424                    n, vcdinfo_ofs2str (p_vcdinfo, ofs->offset, ext), type);
425 
426           fprintf (stdout, "  hexdump: ");
427           _hexdump (&psd[_rofs], 24);
428           fprintf (stdout, "\n");
429           break;
430         }
431 
432       n++;
433     }
434 }
435 
436 static void
dump_info(vcdinfo_obj_t * p_vcdinfo)437 dump_info (vcdinfo_obj_t *p_vcdinfo)
438 {
439   const InfoVcd_t *info = vcdinfo_get_infoVcd(p_vcdinfo);
440   segnum_t num_segments = vcdinfo_get_num_segments(p_vcdinfo);
441   int n;
442 
443   if (!gl.show.no.header)
444     fprintf (stdout,
445              (vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_SVCD
446               || vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_HQVCD)
447              ? "SVCD/INFO.SVD\n"
448              : "VCD/INFO.VCD\n");
449 
450   if (gl.show.info.id)
451     fprintf (stdout, " ID: `%.8s'\n", info->ID);
452   if (gl.show.info.vers)
453     fprintf (stdout, " version: 0x%2.2x\n", info->version);
454   if (gl.show.info.prof)
455     fprintf (stdout, " system profile tag: 0x%2.2x\n", info->sys_prof_tag);
456   if (gl.show.info.album)
457     fprintf (stdout, " album id: `%.16s'\n", vcdinfo_get_album_id(p_vcdinfo));
458   if (gl.show.info.count)
459     fprintf (stdout, " volume count: %d\n",
460              vcdinfo_get_volume_count(p_vcdinfo));
461   if (gl.show.info.vol)
462     fprintf (stdout, " volume number: %d\n",
463              vcdinfo_get_volume_num(p_vcdinfo));
464 
465   if (gl.show.info.pal)
466     {
467       fprintf (stdout, " pal flags:");
468       for (n = 0; n < 98; n++)
469         {
470           if (n == 48)
471             fprintf (stdout, "\n  (bslbf)  ");
472 
473           fprintf (stdout, n % 8 ? "%d" : " %d",
474                    _bitset_get_bit (info->pal_flags, n));
475         }
476       fprintf (stdout, "\n");
477 
478       fprintf (stdout, " flags:\n");
479       fprintf (stdout,
480                ((vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_SVCD
481                  || vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_HQVCD)
482                 ? "  reserved1: %s\n"
483                 : "  karaoke area: %s\n"),
484                _vcd_bool_str (info->flags.reserved1));
485     }
486 
487   if (gl.show.info.res)
488     fprintf (stdout, "  restriction: %d\n", info->flags.restriction);
489   if (gl.show.info.spec)
490     fprintf (stdout, "  special info: %s\n", _vcd_bool_str (info->flags.special_info));
491   if (gl.show.info.cc)
492     fprintf (stdout, "  user data cc: %s\n", _vcd_bool_str (info->flags.user_data_cc));
493   if (gl.show.info.lid2)
494     fprintf (stdout, "  start lid #2: %s\n", _vcd_bool_str (info->flags.use_lid2));
495   if (gl.show.info.st2)
496     fprintf (stdout, "  start track #2: %s\n", _vcd_bool_str (info->flags.use_track3));
497   if (gl.show.info.pbc) {
498     fprintf (stdout,
499              ((vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_SVCD
500                || vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_HQVCD)
501               ? "  reserved2: %s\n"
502               : "  extended pbc: %s\n"),
503              _vcd_bool_str (info->flags.pbc_x));
504     switch (_has_vcd2_ext_pbc(p_vcdinfo))
505       {
506       case PBC_VCD2_NO_PBC:
507         fprintf(stdout, " No PBC info.\n");
508         break;
509       case PBC_VCD2_NO_LOT_X:
510         fprintf(stdout, " Missing EXT/LOT_X.VCD for extended PBC info.\n");
511         break;
512       case PBC_VCD2_NO_PSD_X:
513         fprintf(stdout, " Missing EXT/PSD_X.VCD for extended PBC info.\n");
514         break;
515       case PBC_VCD2_BAD_LOT_SIZE:
516         fprintf(stdout,
517                 " Size of EXT/LOT_X.VCD != LOT_VCD_SIZE*ISO_BLOCKSIZE\n");
518         break;
519       case PBC_VCD2_EXT:
520         fprintf(stdout, " Detected extended VCD2.0 PBC files.\n");
521         break;
522       case PBC_VCD2_NOPE:
523         break;
524       }
525   }
526 
527 
528   if (gl.show.info.psds)
529     fprintf (stdout, " psd size: %u\n",
530              (unsigned int) uint32_from_be (info->psd_size));
531 
532   if (gl.show.info.seg) {
533     char *psz_msf = cdio_msf_to_str(&info->first_seg_addr);
534     fprintf (stdout, " first segment addr: %s\n", psz_msf);
535     free(psz_msf);
536   }
537 
538   if (gl.show.info.ofm)
539     fprintf (stdout, " offset multiplier: 0x%2.2x\n",
540              vcdinfo_get_offset_mult(p_vcdinfo));
541 
542   if (gl.show.info.lidn)
543     fprintf (stdout, " maximum lid: %d\n",
544              uint16_from_be (info->lot_entries));
545 
546   if (gl.show.info.segn)
547     fprintf (stdout, " number of segments: %d\n", num_segments);
548 
549   if (gl.show.info.segs)
550     for (n = 0; n < num_segments; n++)
551       {
552         const lsn_t  lsn = vcdinfo_get_seg_lsn(p_vcdinfo, n);
553         msf_t  msf;
554         char *psz_msf;
555 
556         cdio_lsn_to_msf(lsn, &msf);
557         psz_msf = cdio_msf_to_str(&msf);
558         fprintf (stdout, " SEGMENT[%4.4d]: track# 0, LSN %6u "
559                  "(MSF %s), %2u sectors\n",
560                  n, (unsigned int) lsn, psz_msf,
561                  (unsigned int) vcdinfo_get_seg_sector_count(p_vcdinfo, n));
562         free(psz_msf);
563 
564         fprintf (stdout, "   audio: %s, video: %s, continuation %s%s %s\n",
565                  vcdinfo_audio_type2str(p_vcdinfo,
566                                         vcdinfo_get_seg_audio_type(p_vcdinfo, n)),
567                  vcdinfo_video_type2str(p_vcdinfo, n),
568                  _vcd_bool_str (info->spi_contents[n].item_cont),
569                  (vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_VCD2)
570                    ? "" : ",\n   SVCD subtitle (OGT) substream:",
571                  (vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_VCD2)
572                    ? "" : vcdinfo_ogt2str(p_vcdinfo, n));
573       }
574 
575   if (gl.show.info.start)
576     if (vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_SVCD
577         || vcdinfo_get_VCD_type(p_vcdinfo) == VCD_TYPE_HQVCD)
578       for (n = 0; n < 5; n++)
579         fprintf (stdout, " volume start time[%d]: %d secs\n",
580                  n, uint16_from_be (info->playing_time[n]));
581 }
582 
583 static void
dump_entries(vcdinfo_obj_t * obj)584 dump_entries (vcdinfo_obj_t *obj)
585 {
586   const EntriesVcd_t *entries = vcdinfo_get_entriesVcd(obj);
587   int num_entries, n;
588 
589   num_entries = vcdinfo_get_num_entries(obj);
590 
591   if (!gl.show.no.header)
592     fprintf (stdout,
593              (vcdinfo_get_VCD_type(obj) == VCD_TYPE_SVCD
594               || vcdinfo_get_VCD_type(obj) == VCD_TYPE_HQVCD)
595              ? "SVCD/ENTRIES.SVD\n"
596              : "VCD/ENTRIES.VCD\n");
597 
598   if (!strncmp (entries->ID, ENTRIES_ID_VCD, sizeof (entries->ID)))
599     { /* noop */ }
600   else if (!strncmp (entries->ID, "ENTRYSVD", sizeof (entries->ID)))
601     vcd_warn ("found obsolete (S)VCD3.0 ENTRIES.SVD signature");
602   else
603     vcd_warn ("unexpected ID signature encountered");
604 
605   if (gl.show.entries.id)
606     fprintf (stdout, " ID: `%.8s'\n", entries->ID);
607   if (gl.show.entries.vers)
608     fprintf (stdout, " version: 0x%2.2x\n", entries->version);
609   if (gl.show.entries.prof)
610     fprintf (stdout, " system profile tag: 0x%2.2x\n", entries->sys_prof_tag);
611 
612   if (gl.show.entries.count)
613     fprintf (stdout, " entries: %d\n", num_entries);
614 
615   if (gl.show.entries.data)
616     for (n = 0; n < num_entries; n++)
617       {
618         const lsn_t  lsn = vcdinfo_get_entry_lsn(obj, n);
619         msf_t  msf;
620         char *psz_msf;
621 
622         cdio_lsn_to_msf(lsn, &msf);
623         psz_msf = cdio_msf_to_str(&msf);
624         fprintf (stdout, " ENTRY[%2.2d]: track# %2d (SEQUENCE[%d]), LSN %6u "
625                  "(MSF %s)\n",
626                  n, vcdinfo_get_track(obj, n),
627                  vcdinfo_get_track(obj, n) - 1,
628                  (unsigned int) lsn, psz_msf);
629         free(psz_msf);
630       }
631 }
632 
633 /*
634    Dump the track contents using information from TRACKS.SVCD.
635    See also dump_tracks which gives similar information but doesn't
636    need TRACKS.SVCD
637 */
638 static void
dump_tracks_svd(vcdinfo_obj_t * obj)639 dump_tracks_svd (vcdinfo_obj_t *obj)
640 {
641   const TracksSVD_t     *tracks = vcdinfo_get_tracksSVD(obj);
642   const TracksSVD2_t    *tracks2 = (const void *) &(tracks->playing_time[tracks->tracks]);
643   const TracksSVD_v30_t *tracks_v30 = (TracksSVD_v30_t *) tracks;
644 
645   unsigned j;
646 
647   if (!gl.show.no.header)
648     fprintf (stdout, "SVCD/TRACKS.SVD\n");
649 
650   fprintf (stdout, " ID: `%.8s'\n", tracks->file_id);
651   fprintf (stdout, " version: 0x%2.2x\n", tracks->version);
652 
653   fprintf (stdout, " tracks: %d\n", tracks->tracks);
654 
655   for (j = 0; j < tracks->tracks; j++)
656     {
657       const char *video_types[] =
658         {
659           "no stream",
660           "reserved (0x1)",
661           "reserved (0x2)",
662           "NTSC stream",
663           "reserved (0x4)",
664           "reserved (0x5)",
665           "reserved (0x6)",
666           "PAL stream",
667         };
668 
669       const char *ogt_str[] =
670         {
671           "None",
672           "0 available",
673           "0 & 1 available",
674           "all available"
675         };
676       char *psz_msf = cdio_msf_to_str(&(tracks->playing_time[j]));
677 
678       fprintf (stdout,
679                " track[%.2d]: %s,"
680                " audio: %s, video: %s,\n"
681                "            SVCD subtitle (OGT) stream: %s\n",
682                j, psz_msf,
683                vcdinfo_audio_type2str(obj,
684                                       vcdinfo_get_track_audio_type(obj, j+1)),
685                video_types[tracks2->contents[j].video],
686                ogt_str[tracks2->contents[j].ogt]);
687       free(psz_msf);
688     }
689 
690   fprintf (stdout, "\nCVD interpretation (probably)\n");
691   for (j = 0;j < tracks->tracks; j++)
692     {
693 
694       char *psz_msf = cdio_msf_to_str(&(tracks_v30->track[j].cum_playing_time));
695       fprintf (stdout, "(track[%.2d]: %s (cumulated),"
696                " audio: %.2x, ogt: %.2x)\n",
697                j, psz_msf,
698                tracks_v30->track[j].audio_info,
699                tracks_v30->track[j].ogt_info);
700       free(psz_msf);
701 
702     }
703 }
704 
705 /*
706    Dump the track contents based on low-level CD datas.
707    See also dump_tracks which gives more information but requires
708    TRACKS.SVCD to exist on the medium.
709 */
710 static void
dump_tracks(const CdIo_t * cdio)711 dump_tracks (const CdIo_t *cdio)
712 {
713   track_t i;
714   track_t num_tracks = cdio_get_num_tracks(cdio);
715   track_t first_track_num = cdio_get_first_track_num(cdio);
716 
717   if (!gl.show.no.header)
718     fprintf (stdout, "CD-ROM TRACKS (%i - %i)\n", first_track_num, num_tracks);
719 
720   fprintf(stdout, "  #: MSF       LSN     Type\n");
721 
722   /* Read and possibly print track information. */
723   for (i = first_track_num; i <= CDIO_CDROM_LEADOUT_TRACK; i++) {
724     msf_t msf;
725     char *psz_msf;
726 
727     if (!cdio_get_track_msf(cdio, i, &msf)) {
728       vcd_error("Error getting information for track %i.\n", i);
729       continue;
730     }
731 
732     psz_msf = cdio_msf_to_str(&msf);
733     if (i == CDIO_CDROM_LEADOUT_TRACK) {
734       printf("%3d: %s  %06u  leadout\n",
735              (int) i, psz_msf,
736              (unsigned int) cdio_msf_to_lsn(&msf));
737       free(psz_msf);
738       break;
739       } else {
740       printf("%3d: %s  %06u  %s\n",
741              (int) i, psz_msf,
742              (unsigned int) cdio_msf_to_lsn(&msf),
743              track_format2str[cdio_get_track_format(cdio, i)]);
744 
745     }
746     free(psz_msf);
747     /* skip to leadout? */
748     if (i == num_tracks) i = CDIO_CDROM_LEADOUT_TRACK-1;
749   }
750 }
751 
752 static void
dump_scandata_dat(vcdinfo_obj_t * obj)753 dump_scandata_dat (vcdinfo_obj_t *obj)
754 {
755   const ScandataDat1_t *_sd1 = vcdinfo_get_scandata(obj);
756   const uint16_t scandata_count = uint16_from_be (_sd1->scandata_count);
757   const uint16_t track_count = uint16_from_be (_sd1->track_count);
758   const uint16_t spi_count = uint16_from_be (_sd1->spi_count);
759 
760   fprintf (stdout, "EXT/SCANDATA.DAT\n");
761   fprintf (stdout, " ID: `%.8s'\n", _sd1->file_id);
762 
763   fprintf (stdout, " version: 0x%2.2x\n", _sd1->version);
764   fprintf (stdout, " reserved: 0x%2.2x\n", _sd1->reserved);
765   fprintf (stdout, " scandata_count: %d\n", scandata_count);
766 
767   if (_sd1->version == SCANDATA_VERSION_VCD2)
768     {
769       const ScandataDat_v2_t *_sd_v2 = (ScandataDat_v2_t *) _sd1;
770 
771       int n;
772 
773       for (n = 0; n < scandata_count; n++)
774         {
775           const msf_t *msf = &_sd_v2->points[n];
776           const uint32_t lsn = cdio_msf_to_lsn(msf);
777           char *psz_msf;
778 
779           if (!(gl.debug_level >= 1)
780               && n > PRINTED_POINTS
781               && n < scandata_count - PRINTED_POINTS)
782             continue;
783 
784           psz_msf = cdio_msf_to_str(msf);
785           fprintf (stdout, "  scanpoint[%.4d]: LSN %lu (msf %s)\n",
786                    n, (long unsigned int) lsn, psz_msf);
787 
788           free(psz_msf);
789 
790           if (!(gl.debug_level >= 1)
791               && n == PRINTED_POINTS
792               && scandata_count > (PRINTED_POINTS * 2))
793             fprintf (stdout, " [..skipping...]\n");
794         }
795     }
796   else if (_sd1->version == SCANDATA_VERSION_SVCD)
797     {
798       const ScandataDat2_t *_sd2 =
799         (const void *) &_sd1->cum_playtimes[track_count];
800 
801       const ScandataDat3_t *_sd3 =
802         (const void *) &_sd2->spi_indexes[spi_count];
803 
804       const ScandataDat4_t *_sd4 =
805         (const void *) &_sd3->mpeg_track_offsets[track_count];
806 
807       const int scandata_ofs0 =
808         __cd_offsetof (ScandataDat3_t, mpeg_track_offsets[track_count])
809         - __cd_offsetof (ScandataDat3_t, mpeg_track_offsets);
810 
811       int n;
812 
813       fprintf (stdout, " sequence_count: %d\n", track_count);
814       fprintf (stdout, " segment_count: %d\n", spi_count);
815 
816       for (n = 0; n < track_count; n++)
817         {
818           const msf_t *msf = &_sd1->cum_playtimes[n];
819           char *psz_msf = cdio_msf_to_str(msf);
820 
821           fprintf (stdout, "  cumulative_playingtime[%d]: %s\n", n, psz_msf);
822           free(psz_msf);
823         }
824 
825       for (n = 0; n < spi_count; n++)
826         {
827           const int _ofs = uint16_from_be (_sd2->spi_indexes[n]);
828 
829           fprintf (stdout, "  segment scandata ofs[n]: %d\n", _ofs);
830         }
831 
832       fprintf (stdout, " sequence scandata ofs: %d\n",
833                uint16_from_be (_sd3->mpegtrack_start_index));
834 
835       for (n = 0; n < track_count; n++)
836         {
837           const int _ofs =
838             uint16_from_be (_sd3->mpeg_track_offsets[n].table_offset);
839           const int _toc = _sd3->mpeg_track_offsets[n].track_num;
840 
841           fprintf (stdout, "  track [%d]: TOC num: %d, sd offset: %d\n",
842                    n, _toc, _ofs);
843         }
844 
845       fprintf (stdout, " (scanpoint[0] offset = %d)\n", scandata_ofs0);
846 
847       for (n = 0; n < scandata_count; n++)
848         {
849           const msf_t *msf = &_sd4->scandata_table[n];
850           const uint32_t lsn = cdio_msf_to_lsn(msf);
851           char *psz_msf = cdio_msf_to_str(msf);
852 
853           if (!(gl.debug_level >= 1)
854               && n > PRINTED_POINTS
855               && n < scandata_count - PRINTED_POINTS) {
856             free(psz_msf);
857             continue;
858           }
859 
860 
861           fprintf (stdout,
862                    "  scanpoint[%.4d] (ofs:%5d): LSN %lu (MSF %s)\n",
863                    n, scandata_ofs0 + (n * 3), (unsigned long int) lsn,
864                    psz_msf);
865           free(psz_msf);
866 
867           if (!(gl.debug_level >= 1)
868               && n == PRINTED_POINTS
869               && scandata_count > (PRINTED_POINTS * 2))
870             fprintf (stdout, " [..skipping...]\n");
871         }
872     }
873   else
874     fprintf (stdout, "!unsupported version!\n");
875 }
876 
877 static void
dump_search_dat(vcdinfo_obj_t * obj)878 dump_search_dat (vcdinfo_obj_t *obj)
879 {
880   const SearchDat_t *searchdat = vcdinfo_get_searchDat(obj);
881   unsigned m;
882   uint32_t scan_points = uint16_from_be (searchdat->scan_points);
883 
884   fprintf (stdout, "SVCD/SEARCH.DAT\n");
885   fprintf (stdout, " ID: `%.8s'\n", searchdat->file_id);
886   fprintf (stdout, " version: 0x%2.2x\n", searchdat->version);
887   fprintf (stdout, " scanpoints: %u\n", (unsigned int) scan_points);
888   fprintf (stdout, " scaninterval: %lu (in 0.5sec units -- must be `1')\n",
889            (unsigned long int) searchdat->time_interval);
890 
891   for (m = 0; m < scan_points;m++)
892     {
893       unsigned hh, mm, ss, ss2;
894       const msf_t *msf = &(searchdat->points[m]);
895       const uint32_t lsn = cdio_msf_to_lsn(msf);
896       char *psz_msf;
897 
898       if (!(gl.debug_level >= 1)
899           && m > PRINTED_POINTS
900           && m < (scan_points - PRINTED_POINTS))
901         continue;
902 
903       psz_msf = cdio_msf_to_str(msf);
904       ss2 = m * searchdat->time_interval;
905 
906       hh = ss2 / (2 * 60 * 60);
907       mm = (ss2 / (2 * 60)) % 60;
908       ss = (ss2 / 2) % 60;
909       ss2 = (ss2 % 2) * 5;
910 
911       fprintf (stdout, " scanpoint[%.4d]: (real time: %.2d:%.2d:%.2d.%.1d) "
912                " sector: LSN %lu (MSF %s)\n", m, hh, mm, ss, ss2,
913                (unsigned long int) lsn, psz_msf);
914       free(psz_msf);
915 
916       if (!(gl.debug_level >= 1)
917           && m == PRINTED_POINTS && scan_points > (PRINTED_POINTS * 2))
918         fprintf (stdout, " [..skipping...]\n");
919     }
920 }
921 
922 
923 static void
_dump_fs_recurse(const vcdinfo_obj_t * obj,const char pathname[])924 _dump_fs_recurse (const vcdinfo_obj_t *obj, const char pathname[])
925 {
926   CdioList_t *entlist;
927   CdioList_t *dirlist =  _cdio_list_new ();
928   CdioListNode_t *entnode;
929   CdIo_t *cdio = vcdinfo_get_cd_image(obj);
930 
931   entlist = iso9660_fs_readdir (cdio, pathname);
932 
933   fprintf (stdout, " %s:\n", pathname);
934 
935   vcd_assert (entlist != NULL);
936 
937   /* just iterate */
938 
939   _CDIO_LIST_FOREACH (entnode, entlist)
940     {
941       iso9660_stat_t *statbuf = _cdio_list_node_data (entnode);
942       char *_name = statbuf->filename;
943       char _fullname[4096] = { 0, };
944 
945       snprintf (_fullname, sizeof (_fullname), "%s%s", pathname, _name);
946 
947       strncat (_fullname, "/", sizeof (_fullname)-strlen(_fullname)-1);
948 
949       if (statbuf->type == _STAT_DIR
950           && strcmp (_name, ".")
951           && strcmp (_name, ".."))
952         _cdio_list_append (dirlist, strdup (_fullname));
953 
954       fprintf (stdout,
955                "  %c %s %d %d [fn %.2d] [LSN %6lu] ",
956                (statbuf->type == _STAT_DIR) ? 'd' : '-',
957                iso9660_get_xa_attr_str (statbuf->xa.attributes),
958                uint16_from_be (statbuf->xa.user_id),
959                uint16_from_be (statbuf->xa.group_id),
960                statbuf->xa.filenum,
961                (unsigned long int) statbuf->lsn);
962 
963       if (uint16_from_be(statbuf->xa.attributes) & XA_ATTR_MODE2FORM2) {
964         fprintf (stdout, "%9lu (%9lu)",
965                  (unsigned long int) statbuf->secsize * M2F2_SECTOR_SIZE,
966                  (unsigned long int) statbuf->size);
967       } else {
968         fprintf (stdout, "%9lu", (unsigned long int) statbuf->size);
969       }
970       fprintf (stdout, "  %s\n", _name);
971 
972     }
973 
974   _cdio_list_free (entlist, true, NULL);
975 
976   fprintf (stdout, "\n");
977 
978   /* now recurse */
979 
980   _CDIO_LIST_FOREACH (entnode, dirlist)
981     {
982       char *_fullname = _cdio_list_node_data (entnode);
983 
984       _dump_fs_recurse (obj, _fullname);
985     }
986 
987   _cdio_list_free (dirlist, true, NULL);
988 }
989 
990 static void
dump_fs(vcdinfo_obj_t * obj)991 dump_fs (vcdinfo_obj_t *obj)
992 {
993   const iso9660_pvd_t *pvd = vcdinfo_get_pvd(obj);
994   const lsn_t extent = iso9660_get_root_lsn(pvd);
995 
996   fprintf (stdout, "ISO9660 filesystem dump\n");
997   fprintf (stdout, " root directory in PVD set to LSN %lu\n\n",
998            (unsigned long int) extent);
999 
1000   _dump_fs_recurse (obj, "/");
1001 }
1002 
1003 static void
dump_pvd(vcdinfo_obj_t * p_vcdinfo)1004 dump_pvd (vcdinfo_obj_t *p_vcdinfo)
1005 {
1006   const iso9660_pvd_t *pvd = vcdinfo_get_pvd(p_vcdinfo);
1007 
1008   if (!gl.show.no.header)
1009     fprintf (stdout, "ISO9660 primary volume descriptor\n");
1010 
1011 
1012   if (iso9660_get_pvd_type(pvd) != ISO_VD_PRIMARY)
1013     vcd_warn ("unexpected descriptor type");
1014 
1015   if (strncmp (iso9660_get_pvd_id(pvd), ISO_STANDARD_ID,
1016                strlen (ISO_STANDARD_ID)))
1017     vcd_warn ("unexpected ID encountered (expected `" ISO_STANDARD_ID "'");
1018 
1019   if (gl.show.pvd.id)
1020     fprintf (stdout, " ID: `%.5s'\n", iso9660_get_pvd_id(pvd));
1021 
1022   if (gl.show.pvd.vers)
1023     fprintf (stdout, " version: %d\n", iso9660_get_pvd_version(pvd));
1024 
1025   if (gl.show.pvd.sys) {
1026     char *psz = vcdinfo_get_system_id(p_vcdinfo);
1027     fprintf (stdout, " system id: `%s'\n",    psz);
1028     free(psz);
1029   }
1030 
1031 
1032   if (gl.show.pvd.vol)
1033     fprintf (stdout, " volume id: `%s'\n",
1034              vcdinfo_get_volume_id(p_vcdinfo));
1035 
1036   if (gl.show.pvd.volset)
1037     fprintf (stdout, " volumeset id: `%s'\n",
1038              vcdinfo_get_volumeset_id(p_vcdinfo));
1039 
1040   if (gl.show.pvd.pub) {
1041     char *psz = vcdinfo_get_publisher_id(p_vcdinfo);
1042     fprintf (stdout, " publisher id: `%s'\n", psz);
1043     free(psz);
1044   }
1045 
1046   if (gl.show.pvd.prep) {
1047     char *psz = vcdinfo_get_preparer_id(p_vcdinfo);
1048     fprintf (stdout, " preparer id: `%s'\n",  psz);
1049     free(psz);
1050   }
1051 
1052   if (gl.show.pvd.app) {
1053     char *psz = vcdinfo_get_application_id(p_vcdinfo);
1054     fprintf (stdout, " application id: `%s'\n", psz);
1055     free(psz);
1056   }
1057 
1058   if (gl.show.pvd.iso)
1059     fprintf (stdout, " ISO size: %d blocks (logical blocksize: %d bytes)\n",
1060              iso9660_get_pvd_space_size(pvd),
1061              iso9660_get_pvd_block_size(pvd));
1062 
1063   if (gl.show.pvd.xa)
1064     fprintf (stdout, " XA marker present: %s\n",
1065              _vcd_bool_str (vcdinfo_has_xa(p_vcdinfo)));
1066 }
1067 
1068 static void
dump_all(vcdinfo_obj_t * p_vcdinfo)1069 dump_all (vcdinfo_obj_t *p_vcdinfo)
1070 {
1071   CdIo_t *p_cdio;
1072 
1073   if (!p_vcdinfo) return;
1074 
1075   p_cdio = vcdinfo_get_cd_image(p_vcdinfo);
1076 
1077   if (gl.show.pvd.any)
1078     {
1079       if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1080       dump_pvd (p_vcdinfo);
1081     }
1082 
1083   if (gl.show.fs)
1084     {
1085       if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1086       dump_fs (p_vcdinfo);
1087     }
1088 
1089   if (gl.show.info.any)
1090     {
1091       if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1092       dump_info (p_vcdinfo);
1093     }
1094 
1095   if (gl.show.entries.any)
1096     {
1097       if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1098       dump_entries (p_vcdinfo);
1099     }
1100 
1101   if (gl.show.psd)
1102     {
1103       if (vcdinfo_get_psd_size (p_vcdinfo))
1104         {
1105           vcdinfo_visit_lot (p_vcdinfo, false);
1106           if (gl.show.lot)
1107             {
1108               if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1109               dump_lot (p_vcdinfo, false);
1110             }
1111 
1112           if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1113           dump_psd (p_vcdinfo, false);
1114         }
1115 
1116       if (vcdinfo_get_psd_x_size(p_vcdinfo) && ! gl.no_ext_psd_flag )
1117         {
1118           vcdinfo_visit_lot (p_vcdinfo, true);
1119           if (gl.show.lot)
1120             {
1121               if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1122               dump_lot (p_vcdinfo, true);
1123             }
1124           if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1125           dump_psd (p_vcdinfo, true);
1126         }
1127     }
1128 
1129   if (gl.show.tracks)
1130     {
1131       if (vcdinfo_get_tracksSVD(p_vcdinfo))
1132         {
1133           if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1134           dump_tracks_svd (p_vcdinfo);
1135         }
1136       if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1137       dump_tracks (p_cdio);
1138     }
1139 
1140   if (gl.show.search)
1141     {
1142       if (vcdinfo_get_searchDat(p_vcdinfo))
1143         {
1144           if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1145           dump_search_dat (p_vcdinfo);
1146         }
1147     }
1148 
1149 
1150   if (gl.show.scandata)
1151     {
1152       if (vcdinfo_get_scandata(p_vcdinfo))
1153         {
1154           if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1155           dump_scandata_dat (p_vcdinfo);
1156         }
1157 
1158     }
1159   if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1160 
1161 }
1162 
1163 static void
dump(char * image_fname[])1164 dump (char *image_fname[])
1165 {
1166   unsigned size, psd_size;
1167   vcdinfo_obj_t *obj = NULL;
1168   CdIo_t *p_cdio;
1169   iso9660_stat_t *p_statbuf;
1170   vcdinfo_open_return_t open_rc;
1171 
1172   if (!gl.show.no.banner)
1173     {
1174       if (!gl.show.no.delimiter)
1175         fprintf (stdout, DELIM);
1176 
1177       fprintf (stdout, "vcd-info - GNU VCDImager - (Super) Video CD Report\n"
1178                "%s\n\n", _rcsid);
1179     }
1180 
1181   open_rc = vcdinfo_open(&obj, image_fname, gl.source_type, gl.access_mode);
1182 
1183   if (NULL == obj) {
1184     if (*image_fname == NULL) {
1185       fprintf (stdout, "Couldn't automatically find a Video CD.\n");
1186     } else {
1187       fprintf (stdout, "Error opening requested Video CD object %s\n",
1188                *image_fname);
1189       fprintf (stdout, "Perhaps this is not a Video CD\n");
1190       free(*image_fname);
1191     }
1192     goto err_exit;
1193   }
1194 
1195   p_cdio = vcdinfo_get_cd_image(obj);
1196   if (open_rc==VCDINFO_OPEN_ERROR || p_cdio == NULL) {
1197     vcd_error ("Error determining place to read from");
1198     free(*image_fname);
1199     goto err_exit;
1200   }
1201 
1202   size = cdio_get_disc_last_lsn (p_cdio);
1203 
1204   if (gl.show.source)
1205     {
1206       if (NULL == *image_fname) {
1207         *image_fname = vcdinfo_get_default_device(obj);
1208         fprintf (stdout, "Source: default image file `%s'\n", *image_fname);
1209       } else {
1210         fprintf (stdout, "Source: image file `%s'\n", *image_fname);
1211       }
1212       fprintf (stdout, "Image size: %d sectors\n", size);
1213     }
1214 
1215   if (open_rc == VCDINFO_OPEN_OTHER) {
1216     vcd_warn ("Medium is not VCD image");
1217     if (gl.show.fs)
1218       {
1219         if (vcdinfo_has_xa(obj))
1220         {
1221           /* Suppress XA warnings */
1222           int old_suppress_warnings = gl.suppress_warnings;
1223           if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1224           gl.suppress_warnings=1;
1225           dump_fs (obj);
1226           gl.suppress_warnings=old_suppress_warnings;
1227         }
1228       }
1229 
1230     if (gl.show.tracks) {
1231       if (!gl.show.no.delimiter) fprintf (stdout, DELIM);
1232       dump_tracks (p_cdio);
1233     }
1234     goto err_exit;
1235   }
1236 
1237   if (vcdinfo_get_format_version (obj) == VCD_TYPE_INVALID) {
1238     vcd_error ("VCD detection failed - aborting");
1239     goto err_exit;
1240   } else if (gl.show.format) {
1241     fprintf (stdout, "%s detected\n", vcdinfo_get_format_version_str(obj));
1242   }
1243 
1244   psd_size = vcdinfo_get_psd_size (obj);
1245 
1246   if (vcdinfo_read_psd (obj))
1247     {
1248       /* ISO9660 crosscheck */
1249       p_statbuf = iso9660_fs_stat (p_cdio,
1250                                  ((vcdinfo_get_VCD_type(obj) == VCD_TYPE_SVCD
1251                              || vcdinfo_get_VCD_type(obj) == VCD_TYPE_HQVCD)
1252                                   ? "/SVCD/PSD.SVD;1"
1253                                   : "/VCD/PSD.VCD;1"));
1254       if (!p_statbuf)
1255         vcd_warn ("no PSD file entry found in ISO9660 fs");
1256       else {
1257         if (psd_size != p_statbuf->size)
1258           vcd_warn ("ISO9660 psd size != INFO psd size");
1259         if (p_statbuf->lsn != PSD_VCD_SECTOR)
1260           vcd_warn ("psd file entry in ISO9660 not at fixed LSN");
1261         free(p_statbuf);
1262       }
1263 
1264     }
1265 
1266   dump_all (obj);
1267   vcdinfo_close(obj);
1268   return;
1269 
1270  err_exit:
1271   poptFreeContext(optCon);
1272   exit (EXIT_FAILURE);
1273 }
1274 
1275 static vcd_log_handler_t  gl_default_vcd_log_handler  = NULL;
1276 static cdio_log_handler_t gl_default_cdio_log_handler = NULL;
1277 
1278 static void
_vcd_log_handler(vcd_log_level_t level,const char message[])1279 _vcd_log_handler (vcd_log_level_t level, const char message[])
1280 {
1281   if (level == VCD_LOG_DEBUG && !(gl.debug_level >= 1))
1282     return;
1283 
1284   if (level == VCD_LOG_INFO && gl.quiet_flag)
1285     return;
1286 
1287   if (level == VCD_LOG_WARN && gl.suppress_warnings)
1288     return;
1289 
1290   gl_default_vcd_log_handler (level, message);
1291 }
1292 
1293 /* Configuration option codes */
1294 enum {
1295 
1296   /* These correspond to driver_id_t in cdio.h and have to MATCH! */
1297   OP_SOURCE_UNDEF       = DRIVER_UNKNOWN,
1298   OP_SOURCE_BINCUE      = DRIVER_BINCUE,
1299   OP_SOURCE_NRG         = DRIVER_NRG,
1300   OP_SOURCE_CDRDAO      = DRIVER_CDRDAO,
1301   OP_SOURCE_DEVICE      = DRIVER_DEVICE,
1302   OP_SOURCE_SECTOR_2336,
1303 
1304   /* These are the remaining configuration options */
1305   OP_VERSION,       OP_ENTRIES,   OP_INFO,      OP_PVD,       OP_SHOW,
1306   OP_ACCESS_MODE
1307 
1308 };
1309 
1310 /* Initialize global variables. */
1311 static void
init()1312 init()
1313 {
1314   gl.debug_level      = 0;
1315   gl.quiet_flag       = false;
1316   gl.source_type      = DRIVER_UNKNOWN;
1317   gl.access_mode      = NULL;
1318 
1319   /* Set all of show-flag entries false in one go. */
1320   memset(&gl.show, false, sizeof(gl.show));
1321   /* Actually this was a little too much. :-) The default behaviour is
1322      to show everything. So the one below assignment (if it persists
1323      until after options processing) negates, all of the work of the
1324      memset above! */
1325   gl.show.all = true;
1326 }
1327 
1328 /* Structure used so we can binary sort and set a --show-xxx flag switch. */
1329 typedef struct
1330 {
1331   char name[30];
1332   int *flag;
1333 } subopt_entry_t;
1334 
1335 /* Comparison function called by bearch() to find sub-option record. */
1336 static int
compare_subopts(const void * key1,const void * key2)1337 compare_subopts(const void *key1, const void *key2)
1338 {
1339   subopt_entry_t *a = (subopt_entry_t *) key1;
1340   subopt_entry_t *b = (subopt_entry_t *) key2;
1341   return (strncmp(a->name, b->name, 30));
1342 }
1343 
1344 /* Do processing of a --show-xxx sub option.
1345    Basically we find the option in the array, set it's corresponding
1346    flag variable to true as well as the "show.all" false.
1347 */
1348 static void
process_suboption(const char * subopt,subopt_entry_t * sublist,const int num,const char * subopt_name,int * any_flag)1349 process_suboption(const char *subopt, subopt_entry_t *sublist, const int num,
1350                   const char *subopt_name, int *any_flag)
1351 {
1352   subopt_entry_t *subopt_rec =
1353     bsearch(subopt, sublist, num, sizeof(subopt_entry_t),
1354             &compare_subopts);
1355   if (subopt_rec != NULL) {
1356     if (strcmp(subopt_name, "help") != 0) {
1357       gl.show.all         = false;
1358       *(subopt_rec->flag) = true;
1359       *any_flag           = true;
1360       return;
1361     }
1362   } else {
1363     unsigned int i;
1364     bool is_help=strcmp(subopt, "help")==0;
1365     if (is_help) {
1366       fprintf (stderr, "The list of sub options for \"%s\" are:\n",
1367                subopt_name);
1368     } else {
1369       fprintf (stderr, "Invalid option following \"%s\": %s.\n",
1370                subopt_name, subopt);
1371       fprintf (stderr, "Should be one of: ");
1372     }
1373     for (i=0; i<num-1; i++) {
1374       fprintf(stderr, "%s, ", sublist[i].name);
1375     }
1376     fprintf(stderr, "or %s.\n", sublist[num-1].name);
1377     exit (is_help ? EXIT_SUCCESS : EXIT_FAILURE);
1378   }
1379 }
1380 
1381 
1382 int
main(int argc,const char * argv[])1383 main (int argc, const char *argv[])
1384 {
1385   int terse_flag       = false;
1386   int sector_2336_flag = 0;
1387   char *source_name    = NULL;
1388   const char **args    = NULL;
1389 
1390   int opt;
1391   char *opt_arg;
1392 
1393   /* Command-line options */
1394   struct poptOption optionsTable[] = {
1395 
1396     {"access-mode", 'a',
1397      POPT_ARG_STRING, &gl.access_mode,
1398      OP_ACCESS_MODE,
1399      "set CD-ROM access mode (IOCTL, READ_10, READ_CD)", "ACCESS"},
1400 
1401     {"bin-file", 'b', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name,
1402      OP_SOURCE_BINCUE, "set \"bin\" CD-ROM disk image file as source", "FILE"},
1403 
1404     {"cue-file", 'c', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name,
1405      OP_SOURCE_BINCUE, "set \"cue\" CD-ROM disk image file as source", "FILE"},
1406 
1407     {"nrg-file", 'N', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name,
1408      OP_SOURCE_NRG, "set Nero CD-ROM disk image file as source", "FILE"},
1409 
1410     {"toc-file", '\0', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name,
1411      OP_SOURCE_CDRDAO, "set \"toc\" CD-ROM disk image file as source", "FILE"},
1412 
1413     {"input", 'i', POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name,
1414      OP_SOURCE_UNDEF,
1415      "set source and determine if \"bin\" image or device", "FILE"},
1416 
1417     {"no-ext-psd", '\0', POPT_ARG_NONE, &gl.no_ext_psd_flag, 0,
1418      "ignore information in /EXT/PSD_X.VCD"},
1419 
1420     {"sector-2336", '\0',
1421      POPT_ARG_NONE, &sector_2336_flag,
1422      OP_SOURCE_SECTOR_2336,
1423      "use 2336 byte sector mode for image file"},
1424 
1425     {"cdrom-device", 'C',
1426      POPT_ARG_STRING|POPT_ARGFLAG_OPTIONAL, &source_name,
1427      OP_SOURCE_DEVICE,
1428      "set CD-ROM device as source", "DEVICE"},
1429 
1430     {"debug", 'd', POPT_ARG_INT, &gl.debug_level, 0,
1431      "Set debugging output to LEVEL"},
1432 
1433     {"terse", 't', POPT_ARG_NONE, &terse_flag, 0,
1434      "same as --no-header --no-banner --no-delimiter"},
1435 
1436     {"no-banner", 'B', POPT_ARG_NONE, &gl.show.no.banner, 0,
1437      "do not show program banner header and RCS version string"},
1438 
1439     {"no-delimiter", 'D', POPT_ARG_NONE, &gl.show.no.delimiter, 0,
1440      "do not show delimiter lines around various sections of output"},
1441 
1442     {"no-header", 'H', POPT_ARG_NONE, &gl.show.no.header, 0,
1443      "do not show section header titles"},
1444 
1445     {"show-entries", '\0', POPT_ARG_STRING, &opt_arg, OP_ENTRIES,
1446      "show specific entry of the ENTRIES section "},
1447 
1448     {"show-entries-all", 'E', POPT_ARG_NONE, &gl.show.entries.all, OP_SHOW,
1449      "show ENTRIES section"},
1450 
1451     {"show-filesystem", 'F', POPT_ARG_NONE, &gl.show.fs, OP_SHOW,
1452      "show filesystem info"},
1453 
1454     {"show-info", '\0', POPT_ARG_STRING, &opt_arg, OP_INFO,
1455      "show specific entry of the INFO section "},
1456 
1457     {"show-info-all", 'I', POPT_ARG_NONE, &gl.show.info.all, OP_SHOW,
1458      "show INFO section"},
1459 
1460     {"show-lot", 'L', POPT_ARG_NONE, &gl.show.lot, OP_SHOW,
1461      "show LOT section"},
1462 
1463     {"show-psd", 'p', POPT_ARG_NONE, &gl.show.psd, OP_SHOW,
1464      "show PSD section(s)"},
1465 
1466     {"show-pvd-all", 'P', POPT_ARG_NONE, &gl.show.pvd.all, OP_SHOW,
1467      "show PVD section(s)"},
1468 
1469     {"show-pvd", '\0', POPT_ARG_STRING, &opt_arg, OP_PVD,
1470      "show a specific entry of the Primary Volume Descriptor (PVD) section"},
1471 
1472     {"show-scandata", 's', POPT_ARG_NONE, &gl.show.scandata, OP_SHOW,
1473      "show scan data"},
1474 
1475     {"show-search", 'X', POPT_ARG_NONE, &gl.show.search, OP_SHOW,
1476      "show search data"},
1477 
1478     {"show-source", 'S', POPT_ARG_NONE, &gl.show.source, OP_SHOW,
1479      "show source image filename and size"},
1480 
1481     {"show-tracks", 'T', POPT_ARG_NONE, &gl.show.tracks, OP_SHOW,
1482      "show tracks"},
1483 
1484     {"show-format", 'f', POPT_ARG_NONE, &gl.show.format, OP_SHOW,
1485      "show VCD format (VCD 1.1, VCD 2.0, SVCD, ...)"},
1486 
1487     {"quiet", 'q', POPT_ARG_NONE, &gl.quiet_flag, 0,
1488      "show only critical messages"},
1489 
1490     {"version", 'V', POPT_ARG_NONE, NULL, OP_VERSION,
1491      "display version and copyright information and exit"},
1492     POPT_AUTOHELP {NULL, 0, 0, NULL, 0}
1493   };
1494 
1495   /* Sub-options of for --show-entries. Note: entries must be sorted! */
1496   subopt_entry_t entries_sublist[] = {
1497     {"count", &gl.show.entries.count},
1498     {"data",  &gl.show.entries.data},
1499     {"id",    &gl.show.entries.id},
1500     {"prof",  &gl.show.entries.prof},
1501     {"vers",  &gl.show.entries.vers}
1502   };
1503 
1504   /* Sub-options of for --show-info.  Note: entries must be sorted! */
1505   subopt_entry_t info_sublist[] = {
1506     {"album", &gl.show.info.album},
1507     {"cc",    &gl.show.info.cc},
1508     {"count", &gl.show.info.count},
1509     {"id",    &gl.show.info.id},
1510     {"ofm",   &gl.show.info.ofm},
1511     {"lid2",  &gl.show.info.lid2},
1512     {"lidn",  &gl.show.info.lidn},
1513     {"pal",   &gl.show.info.pal},
1514     {"pbc",   &gl.show.info.pbc},
1515     {"prof",  &gl.show.info.prof},
1516     {"psds",  &gl.show.info.psds},
1517     {"res",   &gl.show.info.res},
1518     {"seg",   &gl.show.info.seg},
1519     {"segn",  &gl.show.info.segn},
1520     {"segs",  &gl.show.info.segs},
1521     {"spec",  &gl.show.info.spec},
1522     {"start", &gl.show.info.start},
1523     {"st2",   &gl.show.info.st2},
1524     {"vers",  &gl.show.info.vers},
1525     {"vol",   &gl.show.info.vol},
1526   };
1527 
1528   /* Sub-options of for --show-pvd.  Note: entries must be sorted! */
1529   subopt_entry_t pvd_sublist[] = {
1530     {"app",   &gl.show.pvd.app},
1531     {"id",    &gl.show.pvd.id},
1532     {"iso",   &gl.show.pvd.iso},
1533     {"prep",  &gl.show.pvd.prep},
1534     {"pub",   &gl.show.pvd.pub},
1535     {"sys",   &gl.show.pvd.sys},
1536     {"vers",  &gl.show.pvd.vers},
1537     {"vol",   &gl.show.pvd.vol},
1538     {"volset",&gl.show.pvd.volset},
1539     {"xa",    &gl.show.pvd.xa},
1540   };
1541 
1542   optCon = poptGetContext (NULL, argc, argv, optionsTable, 0);
1543 
1544   init();
1545 
1546   /* end of local declarations */
1547 
1548   while ((opt = poptGetNextOpt (optCon)) != -1)
1549     switch (opt)
1550       {
1551       case OP_ENTRIES:
1552         {
1553           process_suboption(opt_arg, entries_sublist,
1554                             sizeof(entries_sublist) / sizeof(subopt_entry_t),
1555                             "--show-entries", &gl.show.entries.any);
1556           break;
1557         }
1558       case OP_INFO:
1559         {
1560           process_suboption(opt_arg, info_sublist,
1561                             sizeof(info_sublist) / sizeof(subopt_entry_t),
1562                             "--show-info", &gl.show.info.any);
1563           break;
1564         }
1565       case OP_PVD:
1566         {
1567           process_suboption(opt_arg, pvd_sublist,
1568                             sizeof(pvd_sublist) / sizeof(subopt_entry_t),
1569                             "--show-pvd", &gl.show.pvd.any);
1570           break;
1571         }
1572       case OP_SHOW:
1573         gl.show.all = false;
1574         break;
1575       case OP_VERSION:
1576         fprintf (stdout, vcd_version_string (true), "vcd-info");
1577         fflush (stdout);
1578         poptFreeContext(optCon);
1579         exit (EXIT_SUCCESS);
1580         break;
1581 
1582       case OP_ACCESS_MODE:
1583         /* Make sure a we do only once? */
1584         break;
1585 
1586       case OP_SOURCE_UNDEF:
1587       case OP_SOURCE_BINCUE:
1588       case OP_SOURCE_CDRDAO:
1589       case OP_SOURCE_NRG:
1590       case OP_SOURCE_DEVICE:
1591       case OP_SOURCE_SECTOR_2336:
1592         {
1593           /* Check that we didn't speciy both DEVICE and SECTOR */
1594           bool okay = false;
1595           switch (gl.source_type) {
1596           case OP_SOURCE_UNDEF:
1597             /* Nothing was set before - okay. */
1598             okay = true;
1599             gl.source_type = opt;
1600             break;
1601           case OP_SOURCE_BINCUE:
1602             /* Going from 2352 (default) to 2336 is okay. */
1603             okay = OP_SOURCE_SECTOR_2336 == opt;
1604             if (okay)
1605               gl.source_type = OP_SOURCE_SECTOR_2336;
1606             break;
1607           case OP_SOURCE_SECTOR_2336:
1608             /* Make sure a we didn't do a second device. FIX:
1609                This also allows two -bin options if we had -2336 in the middle
1610              */
1611             okay = OP_SOURCE_DEVICE != opt;
1612             break;
1613           case OP_SOURCE_NRG:
1614           case OP_SOURCE_CDRDAO:
1615           case OP_SOURCE_DEVICE:
1616             /* This case is implied, but we'll make it explicit anyway. */
1617             okay = false;
1618             break;
1619           }
1620 
1621           if (!okay)
1622           {
1623             fprintf (stderr, "only one source allowed! - try --help\n");
1624             poptFreeContext(optCon);
1625             exit (EXIT_FAILURE);
1626           }
1627           break;
1628         }
1629 
1630       default:
1631         fprintf (stderr, "%s: %s\n",
1632                  poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
1633                  poptStrerror(opt));
1634         fprintf (stderr, "error while parsing command line - try --help\n");
1635         poptFreeContext(optCon);
1636         exit (EXIT_FAILURE);
1637       }
1638 
1639   if ((args = poptGetArgs (optCon)) != NULL)
1640     {
1641       if (args[1]) {
1642         fprintf ( stderr, "too many arguments - try --help");
1643         poptFreeContext(optCon);
1644         exit (EXIT_FAILURE);
1645       }
1646 
1647       if (source_name) {
1648         fprintf ( stderr,
1649                   "source file specified as an option and without "
1650                   " - try --help\n");
1651         poptFreeContext(optCon);
1652         exit (EXIT_FAILURE);
1653       }
1654 
1655       source_name    = strdup(args[0]);
1656       gl.source_type = OP_SOURCE_UNDEF;
1657     }
1658 
1659   if (gl.debug_level == 3) {
1660     vcd_loglevel_default = VCD_LOG_INFO;
1661     cdio_loglevel_default = CDIO_LOG_INFO;
1662   } else if (gl.debug_level >= 4) {
1663     vcd_loglevel_default = VCD_LOG_DEBUG;
1664     cdio_loglevel_default = CDIO_LOG_INFO;
1665   }
1666 
1667   /* Handle massive show flag reversals below. */
1668   if (gl.show.all) {
1669     gl.show.entries.all  = gl.show.pvd.all  = gl.show.info.all
1670       = gl.show.format   = gl.show.fs       = gl.show.lot    = gl.show.psd
1671       = gl.show.scandata = gl.show.search   = gl.show.source
1672       = gl.show.tracks   = true;
1673   }
1674 
1675   if (gl.show.entries.all)
1676     memset(&gl.show.entries, true, sizeof(gl.show.entries));
1677 
1678   if (gl.show.pvd.all)
1679     memset(&gl.show.pvd, true, sizeof(gl.show.pvd));
1680 
1681   if (gl.show.info.all)
1682     memset(&gl.show.info, true, sizeof(gl.show.info));
1683 
1684   if (terse_flag)
1685     memset(&gl.show.no, true, sizeof(gl.show.no));
1686 
1687   gl_default_vcd_log_handler  = vcd_log_set_handler (_vcd_log_handler);
1688   gl_default_cdio_log_handler =
1689     cdio_log_set_handler ( (cdio_log_handler_t) _vcd_log_handler);
1690 
1691   dump (&source_name);
1692 
1693   free(source_name);
1694   poptFreeContext(optCon);
1695   return EXIT_SUCCESS;
1696 }
1697 
1698 /*
1699  * Local variables:
1700  *  c-file-style: "gnu"
1701  *  tab-width: 8
1702  *  indent-tabs-mode: nil
1703  * End:
1704  */
1705