home *** CD-ROM | disk | FTP | other *** search
/ Chip 2003 April (Special) / Chip-Special_2003-04_Vypalujeme-cd-i-dvd.bin / soft / pditool / PDITOOL.exe / pdixtract.c < prev    next >
C/C++ Source or Header  |  2003-02-27  |  17KB  |  497 lines

  1. /*$Id: pdixtract.c,v 1.5 2003/02/27 15:08:50 smurf Exp $*/
  2. /* Convert Pinnacle Disk Images (i.e. .pdi file sets) to ISO9660 files */
  3. /* or extract files from the file sets */
  4.  
  5. /*
  6. pdixtract is distributed in the hope that it will be useful, but WITHOUT ANY
  7. WARRANTY.  No author or distributor accepts responsibility to anyone for the
  8. consequences of using it or for whether it serves any particular purpose or
  9. works at all, unless he says so in writing.  Refer to the GNU General Public
  10. License (the "GPL") for full details.
  11.  
  12. Everyone is granted permission to copy, modify and redistribute pdixtract,
  13. but only under the conditions described in the GPL.  A copy of this license
  14. is supposed to have been given to you along with pdixtract so you can know
  15. your rights and responsibilities.  It should be in a file named COPYLEFT,
  16. or, if there is no file named COPYLEFT, a file named COPYING.  Among other
  17. things, the copyright notice and this notice must be preserved on all
  18. copies.
  19.  
  20. We explicitly state here what we believe is already implied by the GPL: if
  21. the pdixtract program is distributed as a separate set of sources and a
  22. separate executable file which are aggregated on a storage medium together
  23. with another program, this in itself does not bring the other program under
  24. the GPL, nor does the mere fact that such a program or the procedures for
  25. constructing it invoke the pdixtract executable bring any other part of the
  26. program under the GPL.
  27. */
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <stdlib.h>
  31. #include <unistd.h>
  32. #include <sys/types.h>
  33. #include <sys/stat.h>
  34. #include <fcntl.h>
  35.  
  36. #if defined(__WINDOWS__) || defined(__NT__)
  37. #include <io.h>
  38. #include <direct.h>
  39. #define MAKE_DIR(x) mkdir(x)
  40. #define PATH_SEP '\\'
  41. #define STRCMP(a,b) strcmpi(a,b)
  42. #define STRNCMP(a,b,l) strnicmp(a,b,l)
  43. #else
  44. #define MAKE_DIR(x) mkdir(x,0775)
  45. #define STRCMP(a,b) strcasecmp(a,b)
  46. #define STRNCMP(a,b,l) strncasecmp(a,b,l)
  47. #define PATH_SEP '/'
  48. #endif
  49.  
  50. #ifndef O_BINARY
  51. #define O_BINARY 0
  52. #endif
  53.  
  54. #define DEBUGP(a) /* fprintf a */
  55. #define PDI_HEADER     0x130
  56. #define PDI_FIRST_DESC (0x22800+PDI_HEADER)
  57. #define PDI_DIR_MAX    20480
  58. #define MAX_FILES      400
  59. #define BUFFSIZE       20*1024*1024
  60.  
  61. /*
  62.  * The isofs filesystem constants/structures
  63.  */
  64.  
  65. /* This part borrowed from the bsd386 isofs */
  66. #define ISODCL(from, to) (to - from + 1)
  67.  
  68. struct iso_directory_record {
  69.         unsigned char length                    [ISODCL (1, 1)]; /* length of descriptor */
  70.         unsigned char ext_attr_length           [ISODCL (2, 2)]; /* length of extended attributes, 0 in .pdi-files */
  71.         unsigned char extent                    [ISODCL (3, 10)]; /* position of file in image in multiple of sector size, 32Bit-LSB, 32Bit-MSB */
  72.         unsigned char size                      [ISODCL (11, 18)]; /* size of file, 32Bit-LSB, 32Bit-MSB */
  73.         unsigned char date                      [ISODCL (19, 25)]; /* Year-1900, month, day, hour,min,sec */
  74.         unsigned char flags                     [ISODCL (26, 26)]; /* 00 for files */
  75.         unsigned char file_unit_size            [ISODCL (27, 27)]; /* 00 in .pdi */
  76.         unsigned char interleave                [ISODCL (28, 28)]; /* 00 in .pdi */
  77.         unsigned char volume_sequence_number    [ISODCL (29, 32)]; /* 01 in .pdi, 16Bit-LSB, 16Bit-MSB */
  78.         unsigned char name_len          [ISODCL (33, 33)]; /* length of name */
  79.         char name                       [1];
  80. };
  81.  
  82.  
  83. char buffer[BUFFSIZE];
  84. struct filedesc files[MAX_FILES];
  85.  
  86. char id[]="$Id: pdixtract.c,v 1.5 2003/02/27 15:08:50 smurf Exp $";
  87.  
  88. /* append [ext] to [in] if not already present */
  89. char *
  90. file_with_ext(const char*in,const char*ext){
  91.   size_t in_l=strlen(in);
  92.   size_t ext_l=strlen(ext);
  93.   char * out;
  94.   if(STRCMP(in+in_l-ext_l,ext)){
  95.     out=malloc(in_l+ext_l+1);
  96.     memcpy(out,in,in_l);
  97.     memcpy(out+in_l,ext,ext_l+1);
  98.     return out;
  99.   }
  100.   return strdup(in);
  101. }
  102.  
  103. /* errexit is called after a fatal error that prevents us from continuing */
  104. void errexit(const char*err){
  105.   perror(err);
  106.   exit(1);
  107. }
  108.  
  109. struct filedesc {
  110.   char *name;
  111.   size_t size; /* size in extents */
  112.   size_t extent;
  113.   int hits; /* flag for individual file selection */
  114. };
  115.  
  116.  
  117. /* helper for qsort, compares extents of 2 filedescs */
  118. int
  119. filedesc_cmp(struct filedesc*a,struct filedesc*b){
  120.   return a->extent-b->extent;
  121. }
  122.  
  123. /* read directory from first pdi-file into files, returns no of files */
  124. int 
  125. pdi_read_dir(int fin){
  126.   size_t off=0; /* offset into buffer for current dir-record */
  127.   size_t size,extent;
  128.   size_t fdscno=0;
  129.   struct iso_directory_record *dirp;
  130.   lseek(fin,PDI_FIRST_DESC,SEEK_SET);
  131.   if(read(fin,buffer,PDI_DIR_MAX)<0)
  132.     errexit("during read of directory");
  133.   off=0;
  134.   printf("[extent ] File Name    Size       Date       Time\n");
  135.   while(buffer[off]){
  136.     dirp=(struct iso_directory_record*)&buffer[off];
  137.     if(!dirp->flags[0]&&dirp->name[0]&&dirp->name_len[0]){
  138.       size=dirp->size[0]+0x100*dirp->size[1]+
  139.         0x10000*dirp->size[2]+0x1000000*dirp->size[3];
  140.       extent=dirp->extent[0]+0x100*dirp->extent[1]+
  141.         0x10000*dirp->extent[2]+0x1000000*dirp->extent[3];
  142.       printf("[%7i] %12s %10i %2i.%2i.%4i %02i:%02i:%02i\n",
  143.              extent,dirp->name,size,
  144.              dirp->date[2],dirp->date[1],dirp->date[0]+1900,
  145.              dirp->date[3],dirp->date[4],dirp->date[5]);
  146.       files[fdscno].size=size/0x800;
  147.       files[fdscno].name=strdup(dirp->name);
  148.       files[fdscno].extent=extent;
  149.       files[fdscno].hits=1;
  150.       fdscno++;
  151.     }
  152.     off+=dirp->length[0];
  153.     if(!buffer[off]){ /* next directory record to large for current extent */
  154.       off=(off+0x7ff)&0xfffff800; /* skip to next extent */
  155.     }
  156.   }
  157.   //  qsort(files,fdscno,sizeof(struct filedesc),filedesc_cmp);
  158.   qsort(files,fdscno,sizeof(struct filedesc),
  159.         (int (*) (const void *,const void *))filedesc_cmp);
  160.   return fdscno;
  161. }
  162.  
  163. void
  164. usage(){
  165.   fprintf(stderr,
  166.           "usage: pdixtract [options] <pdi-file-name>[.pdi] [files...]\n"
  167.           "\n"
  168.           "options:\n"
  169.           "-l        list only (directory mode)\n"
  170.           "-i <iso>  convert .pdi-file-set into .iso-image <iso>[.iso]\n"
  171.           /*
  172.           "-n <name> (in combination with -i) set the iso volume name\n" 
  173.           */
  174.           "-r        remove .pdi files as soon as possible\n"
  175.           "-d <dir>  extract files to directory <dir>\n"
  176.           "-m <dir>  create directory <dir> and extract files int it\n"
  177.           "-v        create VIDEO_TS directory and extract files into it\n"
  178.           "-x        same as -r -m <pdi-file-name> -v\n"
  179.           "\n"
  180.           "a trailing '*' may be used as wildcard for file selection,\n"
  181.           "e.g. pdixtract -v movie vts_01*\n"
  182.           "extracts Video Title Set 1 from movie.pdi* to dir VIDEO_TS\n"
  183.           );
  184.   exit(1);
  185. }
  186.  
  187. /* global data describing the input data */
  188. int current_segment=0;    /* number of current input file */
  189. int delete_source=0;      /* flag for deletion of un-needed sources */ 
  190. char current_file[0x300]; /* file name of current input */
  191. char *infile; /* name of very first input file */
  192. int fin; /* os file descriptor of current input */
  193. struct {
  194.   size_t first_extent; /* first whole extent in current file */
  195.   size_t no_extents; /* number of whole extents in current file */
  196.   size_t off; /* offset to first extent in current file */
  197.   size_t size; /* size of current file in bytes */
  198. } pdi_segment[6];
  199.  
  200. /* open a new .pdi segment and prepare it for reading */
  201. void
  202. load_new_source(){
  203.   long l;
  204.   close(fin);
  205.   if(delete_source&&unlink(current_file))
  206.     perror(current_file); /* failed deletion is not fatal */
  207.   current_segment++;
  208.   snprintf(current_file,sizeof(current_file)-1,
  209.            "%s%02i",infile,current_segment);
  210.   fin=open(current_file,O_RDONLY+O_BINARY);
  211.   if(fin<0)
  212.     errexit(current_file);
  213.   fprintf(stderr,"(%s)",current_file);
  214.   /* now check files' size */
  215.   l=lseek(fin,0,SEEK_END);
  216.   if(l<0)
  217.     errexit(current_file);
  218.   pdi_segment[current_segment].size=l;
  219.   pdi_segment[current_segment].off=
  220.     (pdi_segment[current_segment-1].size-pdi_segment[current_segment-1].off)%0x800;
  221.   pdi_segment[current_segment].first_extent=
  222.     pdi_segment[current_segment-1].first_extent+
  223.     (pdi_segment[current_segment-1].size-pdi_segment[current_segment-1].off)/0x800;
  224.   if(pdi_segment[current_segment].off){
  225.     pdi_segment[current_segment].off=0x800-pdi_segment[current_segment].off;
  226.     pdi_segment[current_segment].first_extent++;
  227.   }
  228.   pdi_segment[current_segment].no_extents=(l-pdi_segment[current_segment].off)/0x800;
  229.   DEBUGP((stderr,"\nseg %i:off(%i) first(%i) no(%i) size(%i)\n",
  230.           current_segment,
  231.           pdi_segment[current_segment].off,
  232.           pdi_segment[current_segment].first_extent,
  233.           pdi_segment[current_segment].no_extents,
  234.           pdi_segment[current_segment].size));
  235. }
  236. /* write no_extents blocks of 0x800 to fout, starting with first_extent */
  237. /* all requested extents are contained in current input */
  238. void
  239. copy_extents(int fout,size_t first_extent, long no_extents){
  240.   DEBUGP((stderr,"copy_segments: first=%i, no=%li\n",
  241.           first_extent,no_extents));
  242.   if(0>lseek(fin,
  243.              pdi_segment[current_segment].off + 
  244.              (first_extent-pdi_segment[current_segment].first_extent)*0x800,
  245.              SEEK_SET))
  246.     errexit("during skip of .pdi-data");
  247.   no_extents*=0x800;
  248.   while(no_extents>BUFFSIZE){
  249.     if(BUFFSIZE>read(fin,buffer,BUFFSIZE))
  250.       errexit("during read of extents");
  251.     if(BUFFSIZE>write(fout,buffer,BUFFSIZE))
  252.       errexit("during write of extents");
  253.     no_extents-=BUFFSIZE;
  254.     putc('.',stderr);
  255.   }
  256.   if(no_extents>read(fin,buffer,no_extents))
  257.     errexit("during read of extents");
  258.   if(no_extents>write(fout,buffer,no_extents))
  259.     errexit("during write of extents");
  260. }
  261. /* write no_extents blocks of 0x800 to fout, starting with first_extent */
  262. /* manage segment boundaries */
  263. void
  264. write_to_file(int fout,size_t first_extent, size_t no_extents)
  265. {
  266.   DEBUGP((stderr,"[%i..%i]",first_extent,first_extent+no_extents-1));
  267.   /* skip all input files, that do not contain parts of desired output */
  268.   while(pdi_segment[current_segment].first_extent+
  269.         pdi_segment[current_segment].no_extents+1<
  270.         first_extent)
  271.     load_new_source();
  272.   while(no_extents){
  273.     char small_buffer[0x800];
  274.     size_t extents_from_this_segment;
  275.     long bytes_in_buffer;
  276.     if(first_extent+no_extents <=
  277.        pdi_segment[current_segment].first_extent+pdi_segment[current_segment].no_extents)
  278.       extents_from_this_segment=no_extents;
  279.     else
  280.       extents_from_this_segment=
  281.         pdi_segment[current_segment].first_extent+
  282.         pdi_segment[current_segment].no_extents-first_extent;
  283.     if (extents_from_this_segment){ /* this file contains whole segments */
  284.       copy_extents(fout,first_extent,extents_from_this_segment);
  285.       first_extent+=extents_from_this_segment;
  286.       if(!(no_extents-=extents_from_this_segment))
  287.         return;
  288.     }
  289.     if(0>lseek(fin,
  290.                pdi_segment[current_segment].off + pdi_segment[current_segment].no_extents*0x800,
  291.                SEEK_SET))
  292.       errexit("during skip of .pdi-data");
  293.     bytes_in_buffer=read(fin,small_buffer,0x800);
  294.     if(bytes_in_buffer<0)
  295.       errexit("reading partial extent, first part");
  296.     load_new_source();
  297.     if(0>lseek(fin, 0, SEEK_SET))
  298.       errexit("during rewind of .pdi-data");
  299.     if(bytes_in_buffer){
  300.       bytes_in_buffer=read(fin,small_buffer+bytes_in_buffer,0x800-bytes_in_buffer);
  301.       if(bytes_in_buffer<(long)pdi_segment[current_segment].off)
  302.         errexit("reading partial extent, second part");
  303.       if(0x800>write(fout,small_buffer,0x800))
  304.         errexit("during write of segmented extent");
  305.       first_extent++;
  306.       no_extents--;
  307.     }
  308.   }
  309. }
  310.  
  311. int main(int argc,char **argv){
  312.   int fout=-1; /* handle to output file */
  313.   size_t fdscno; /* count of output files */
  314.   size_t i;
  315.   long l;
  316.   char outname[999];
  317.  
  318.     /* option flags */
  319.   int list_only=0;
  320.   int make_directory=0;
  321.   int make_VIDEO_TS=0;
  322.   char* dest_dir=0;
  323.   char* iso_name=0;
  324.   int argi=1;
  325.   char*iso_vol_name=0;
  326.   char*list_file=0; 
  327.   int list_format=0;
  328.  
  329.   fprintf(stderr,"%s\n\n",id);
  330.   /* option parsing */
  331.   while(argc>argi&&argv[argi][0]=='-'){
  332.     switch(argv[argi][1]){
  333.     case 'l': case 'L': list_only=1; break;
  334.     case 'r': case 'R': delete_source=1; break;
  335.     case 'm': case 'M': make_directory=1;
  336.     case 'd': case 'D': dest_dir=argv[++argi]; break;
  337.     case 'i': case 'I': iso_name=file_with_ext(argv[++argi],".iso"); break;
  338.     case 'n': case 'N': iso_vol_name=argv[++argi]; break;
  339.     case 'v': case 'V': make_VIDEO_TS=1; break; 
  340.     case 'g': case 'G': /* GUI options */
  341.       switch(argv[argi][2]) {
  342.       case 'b': case 'B': list_format++; /* format 3: <name> <size>\n */
  343.       case 'f': case 'F': list_format++; /* format 2: <name>\n<size>\n */
  344.       case 's': case 'S': list_format++; /* format 1: <name>,<size>\n */
  345.       case 'l': case 'L':                /* format 0: <name>\n */
  346.         list_only=1;list_file=argv[++argi]; break;
  347.       default: usage();
  348.       }
  349.       break;
  350.     case 'x': case 'X': if(argc-argi!=2) usage();
  351.       make_directory=1; make_VIDEO_TS=1; delete_source=1;
  352.       dest_dir=strdup(argv[argi+1]);
  353.       l=strlen(dest_dir)-4;
  354.       if(l>0&&!STRCMP(dest_dir+l,".pdi"))
  355.         dest_dir[l]=0; /* strip .pdi extension */
  356.       break;
  357.     default: usage();
  358.     }
  359.     argi++;
  360.   }
  361.  
  362.   if(argc==argi)
  363.     usage();
  364.   if(argc-argi>1&&(iso_name||list_only)){
  365.     fprintf(stderr,"individual file selection may be used only in file extraction mode\n");
  366.     usage();
  367.   }
  368.   infile=file_with_ext(argv[argi],".pdi");
  369.   fin=open(infile,O_RDONLY+O_BINARY);
  370.   if(fin<0)
  371.     errexit(infile);
  372.   fdscno=pdi_read_dir(fin);
  373.  
  374.   if(list_file){ /* generate listing for PDIgui */
  375.     FILE*f=fopen(list_file,"wt");
  376.     char* list_formats[]={"%s\n","%s,%i\n","%s\n%i\n","%s %i\n"};
  377.     if(!f)errexit(list_file);
  378.     for(i=0;i<fdscno;i++)
  379.       fprintf(f,list_formats[list_format],files[i].name,files[i].size*0x800);
  380.     fclose(f);
  381.   }
  382.   if(list_only)
  383.     exit(0);
  384.   if(!fdscno){
  385.     fprintf(stderr,"no files found in %s, exiting.\n",infile);
  386.     exit(0);
  387.   }
  388.   if(argc-argi>1){ /* there are file arguments specified */
  389.     /* now filter directory files through arguments */
  390.     int warning=0;
  391.     while(++argi<argc){
  392.       int found=0;
  393.       l=strlen(argv[argi]);
  394.       if(l&&argv[argi][--l]=='*'){ /* wild card match */
  395.         for(i=0;i<fdscno;i++)
  396.           if(!STRNCMP(argv[argi],files[i].name,l))
  397.             found=files[i].hits++;
  398.       }
  399.       else
  400.         for(i=0;i<fdscno;i++)
  401.           if(!STRCMP(argv[argi],files[i].name))
  402.             found=files[i].hits++;
  403.       if(!found){
  404.         fprintf(stderr,"Warning: file %s not found in .pdi set\n",argv[argi]);
  405.         warning++;
  406.       }
  407.     }
  408.     if(warning&&delete_source){
  409.       fprintf(stderr,"%i warnings issued during file selection.\n"
  410.               "cowardly clearing delete source flag\n",warning);
  411.       delete_source=0;
  412.     }
  413.     l=0;
  414.     for(i=0;i<fdscno;i++)
  415.       l+=--files[i].hits;
  416.     if(!l){
  417.       fprintf(stderr,"no files to extract, exiting.\n");
  418.       exit(0);
  419.     }
  420.   }
  421.   if(make_directory&&MAKE_DIR(dest_dir))
  422.     perror(dest_dir);
  423.   if(make_VIDEO_TS){
  424.     if(dest_dir){
  425.       snprintf(outname,sizeof(outname)-1,"%s%cVIDEO_TS",dest_dir,PATH_SEP);
  426.       dest_dir=strdup(outname);
  427.     }
  428.     else
  429.       dest_dir="VIDEO_TS";
  430.     if(MAKE_DIR(dest_dir))
  431.       perror(dest_dir);
  432.   }
  433.  
  434.   pdi_segment[0].off=PDI_HEADER;
  435.   l=lseek(fin,0,SEEK_END);
  436.   if(l<0)
  437.     errexit(current_file);
  438.   pdi_segment[0].size=l;
  439.   pdi_segment[0].first_extent=0;
  440.   pdi_segment[0].no_extents=(l-PDI_HEADER)/0x800;
  441.  
  442.   if(iso_name){ /* copy directory structure to iso */
  443.     fdscno--;
  444.     files[0].size=files[fdscno].extent+files[fdscno].size;
  445.     files[0].extent=0;
  446.     files[0].name=iso_name;
  447.     fdscno=1;
  448.   }
  449.   strncpy(current_file,infile,sizeof(current_file)-1);
  450.   for(i=0;i<fdscno;i++)
  451.     if(files[i].hits){
  452.       if(dest_dir)
  453.         snprintf(outname,sizeof(outname)-1,"%s%c%s",dest_dir,PATH_SEP,files[i].name);
  454.       else
  455.         strncpy(outname,files[i].name,sizeof(outname)-1);
  456.       fprintf(stderr,"writing %s (%i extents) ",outname,files[i].size);
  457.       fout=open(outname,O_TRUNC+O_WRONLY+O_CREAT+O_BINARY,S_IWUSR+S_IRUSR);
  458.       if(fout<0)
  459.         errexit(files[i].name);
  460.       write_to_file(fout,files[i].extent,files[i].size);
  461.       close(fout);
  462.       putc('\n',stderr);
  463.     }
  464.   close(fin);
  465.   if(delete_source&&unlink(current_file))
  466.     perror(current_file);
  467.   return 0;
  468. }
  469.  
  470. /* $Log: pdixtract.c,v $
  471.  * Revision 1.5  2003/02/27 15:08:50  smurf
  472.  * added different list formats to support PDIgui
  473.  *
  474.  * Revision 1.4  2003/02/25 19:25:00  smurf
  475.  * added -i option to extract iso image
  476.  * added -x "all in wonder" in options
  477.  * added support for extracting individual files
  478.  *
  479.  * Revision 1.3  2003/02/20 12:59:10  smurf
  480.  * improved error reporting
  481.  * checked for shadowed directory entries (reported by JJF007)
  482.  * added command line switches:
  483.  *     -v create VIDEO_TS
  484.  *     -x (all in wonder) is -r -m <pdi-file-name> -v
  485.  *
  486.  * Revision 1.2  2003/02/18 17:39:03  smurf
  487.  * Added new command-line switches:
  488.  * -l       list only
  489.  * -r       remove source files after extraction
  490.  * -d <dir> extract files into <dir> instead of current directory
  491.  * -m <dir> create directory <dir> and extract files into <dir>
  492.  *
  493.  * Revision 1.1  2003/02/17 15:08:50  smurf
  494.  * Initial revision
  495.  *
  496.  * */
  497.