/*$Id: pdixtract.c,v 1.5 2003/02/27 15:08:50 smurf Exp $*/ /* Convert Pinnacle Disk Images (i.e. .pdi file sets) to ISO9660 files */ /* or extract files from the file sets */ /* pdixtract is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the GNU General Public License (the "GPL") for full details. Everyone is granted permission to copy, modify and redistribute pdixtract, but only under the conditions described in the GPL. A copy of this license is supposed to have been given to you along with pdixtract so you can know your rights and responsibilities. It should be in a file named COPYLEFT, or, if there is no file named COPYLEFT, a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. We explicitly state here what we believe is already implied by the GPL: if the pdixtract program is distributed as a separate set of sources and a separate executable file which are aggregated on a storage medium together with another program, this in itself does not bring the other program under the GPL, nor does the mere fact that such a program or the procedures for constructing it invoke the pdixtract executable bring any other part of the program under the GPL. */ #include #include #include #include #include #include #include #if defined(__WINDOWS__) || defined(__NT__) #include #include #define MAKE_DIR(x) mkdir(x) #define PATH_SEP '\\' #define STRCMP(a,b) strcmpi(a,b) #define STRNCMP(a,b,l) strnicmp(a,b,l) #else #define MAKE_DIR(x) mkdir(x,0775) #define STRCMP(a,b) strcasecmp(a,b) #define STRNCMP(a,b,l) strncasecmp(a,b,l) #define PATH_SEP '/' #endif #ifndef O_BINARY #define O_BINARY 0 #endif #define DEBUGP(a) /* fprintf a */ #define PDI_HEADER 0x130 #define PDI_FIRST_DESC (0x22800+PDI_HEADER) #define PDI_DIR_MAX 20480 #define MAX_FILES 400 #define BUFFSIZE 20*1024*1024 /* * The isofs filesystem constants/structures */ /* This part borrowed from the bsd386 isofs */ #define ISODCL(from, to) (to - from + 1) struct iso_directory_record { unsigned char length [ISODCL (1, 1)]; /* length of descriptor */ unsigned char ext_attr_length [ISODCL (2, 2)]; /* length of extended attributes, 0 in .pdi-files */ unsigned char extent [ISODCL (3, 10)]; /* position of file in image in multiple of sector size, 32Bit-LSB, 32Bit-MSB */ unsigned char size [ISODCL (11, 18)]; /* size of file, 32Bit-LSB, 32Bit-MSB */ unsigned char date [ISODCL (19, 25)]; /* Year-1900, month, day, hour,min,sec */ unsigned char flags [ISODCL (26, 26)]; /* 00 for files */ unsigned char file_unit_size [ISODCL (27, 27)]; /* 00 in .pdi */ unsigned char interleave [ISODCL (28, 28)]; /* 00 in .pdi */ unsigned char volume_sequence_number [ISODCL (29, 32)]; /* 01 in .pdi, 16Bit-LSB, 16Bit-MSB */ unsigned char name_len [ISODCL (33, 33)]; /* length of name */ char name [1]; }; struct filedesc { char *name; size_t size; /* size in extents */ size_t extent; int hits; /* flag for individual file selection */ }; char buffer[BUFFSIZE]; struct filedesc files[MAX_FILES]; char id[]="$Id: pdixtract.c,v 1.5 2003/02/27 15:08:50 smurf Exp $"; /* append [ext] to [in] if not already present */ char * file_with_ext(const char*in,const char*ext){ size_t in_l=strlen(in); size_t ext_l=strlen(ext); char * out; if(STRCMP(in+in_l-ext_l,ext)){ out=malloc(in_l+ext_l+1); memcpy(out,in,in_l); memcpy(out+in_l,ext,ext_l+1); return out; } return strdup(in); } /* errexit is called after a fatal error that prevents us from continuing */ void errexit(const char*err){ perror(err); exit(1); } /* helper for qsort, compares extents of 2 filedescs */ int filedesc_cmp(struct filedesc*a,struct filedesc*b){ return a->extent-b->extent; } /* read directory from first pdi-file into files, returns no of files */ int pdi_read_dir(int fin){ size_t off=0; /* offset into buffer for current dir-record */ size_t size,extent; size_t fdscno=0; struct iso_directory_record *dirp; lseek(fin,PDI_FIRST_DESC,SEEK_SET); if(read(fin,buffer,PDI_DIR_MAX)<0) errexit("during read of directory"); off=0; printf("[extent ] File Name Size Date Time\n"); while(buffer[off]){ dirp=(struct iso_directory_record*)&buffer[off]; if(!dirp->flags[0]&&dirp->name[0]&&dirp->name_len[0]){ size=dirp->size[0]+0x100*dirp->size[1]+ 0x10000*dirp->size[2]+0x1000000*dirp->size[3]; extent=dirp->extent[0]+0x100*dirp->extent[1]+ 0x10000*dirp->extent[2]+0x1000000*dirp->extent[3]; printf("[%7i] %12s %10i %2i.%2i.%4i %02i:%02i:%02i\n", extent,dirp->name,size, dirp->date[2],dirp->date[1],dirp->date[0]+1900, dirp->date[3],dirp->date[4],dirp->date[5]); files[fdscno].size=size/0x800; files[fdscno].name=strdup(dirp->name); files[fdscno].extent=extent; files[fdscno].hits=1; fdscno++; } off+=dirp->length[0]; if(!buffer[off]){ /* next directory record to large for current extent */ off=(off+0x7ff)&0xfffff800; /* skip to next extent */ } } // qsort(files,fdscno,sizeof(struct filedesc),filedesc_cmp); qsort(files,fdscno,sizeof(struct filedesc), (int (*) (const void *,const void *))filedesc_cmp); return fdscno; } void usage(){ fprintf(stderr, "usage: pdixtract [options] [.pdi] [files...]\n" "\n" "options:\n" "-l list only (directory mode)\n" "-i convert .pdi-file-set into .iso-image [.iso]\n" /* "-n (in combination with -i) set the iso volume name\n" */ "-r remove .pdi files as soon as possible\n" "-d extract files to directory \n" "-m create directory and extract files int it\n" "-v create VIDEO_TS directory and extract files into it\n" "-x same as -r -m -v\n" "\n" "a trailing '*' may be used as wildcard for file selection,\n" "e.g. pdixtract -v movie vts_01*\n" "extracts Video Title Set 1 from movie.pdi* to dir VIDEO_TS\n" ); exit(1); } /* global data describing the input data */ int current_segment=0; /* number of current input file */ int delete_source=0; /* flag for deletion of un-needed sources */ char current_file[0x300]; /* file name of current input */ char *infile; /* name of very first input file */ int fin; /* os file descriptor of current input */ struct { size_t first_extent; /* first whole extent in current file */ size_t no_extents; /* number of whole extents in current file */ size_t off; /* offset to first extent in current file */ size_t size; /* size of current file in bytes */ } pdi_segment[6]; /* open a new .pdi segment and prepare it for reading */ void load_new_source(){ long l; close(fin); if(delete_source&&unlink(current_file)) perror(current_file); /* failed deletion is not fatal */ current_segment++; snprintf(current_file,sizeof(current_file)-1, "%s%02i",infile,current_segment); fin=open(current_file,O_RDONLY+O_BINARY); if(fin<0) errexit(current_file); fprintf(stderr,"(%s)",current_file); /* now check files' size */ l=lseek(fin,0,SEEK_END); if(l<0) errexit(current_file); pdi_segment[current_segment].size=l; pdi_segment[current_segment].off= (pdi_segment[current_segment-1].size-pdi_segment[current_segment-1].off)%0x800; pdi_segment[current_segment].first_extent= pdi_segment[current_segment-1].first_extent+ (pdi_segment[current_segment-1].size-pdi_segment[current_segment-1].off)/0x800; if(pdi_segment[current_segment].off){ pdi_segment[current_segment].off=0x800-pdi_segment[current_segment].off; pdi_segment[current_segment].first_extent++; } pdi_segment[current_segment].no_extents=(l-pdi_segment[current_segment].off)/0x800; DEBUGP((stderr,"\nseg %i:off(%i) first(%i) no(%i) size(%i)\n", current_segment, pdi_segment[current_segment].off, pdi_segment[current_segment].first_extent, pdi_segment[current_segment].no_extents, pdi_segment[current_segment].size)); } /* write no_extents blocks of 0x800 to fout, starting with first_extent */ /* all requested extents are contained in current input */ void copy_extents(int fout,size_t first_extent, long no_extents){ DEBUGP((stderr,"copy_segments: first=%i, no=%li\n", first_extent,no_extents)); if(0>lseek(fin, pdi_segment[current_segment].off + (first_extent-pdi_segment[current_segment].first_extent)*0x800, SEEK_SET)) errexit("during skip of .pdi-data"); no_extents*=0x800; while(no_extents>BUFFSIZE){ if(BUFFSIZE>read(fin,buffer,BUFFSIZE)) errexit("during read of extents"); if(BUFFSIZE>write(fout,buffer,BUFFSIZE)) errexit("during write of extents"); no_extents-=BUFFSIZE; putc('.',stderr); } if(no_extents>read(fin,buffer,no_extents)) errexit("during read of extents"); if(no_extents>write(fout,buffer,no_extents)) errexit("during write of extents"); } /* write no_extents blocks of 0x800 to fout, starting with first_extent */ /* manage segment boundaries */ void write_to_file(int fout,size_t first_extent, size_t no_extents) { DEBUGP((stderr,"[%i..%i]",first_extent,first_extent+no_extents-1)); /* skip all input files, that do not contain parts of desired output */ while(pdi_segment[current_segment].first_extent+ pdi_segment[current_segment].no_extents+1< first_extent) load_new_source(); while(no_extents){ char small_buffer[0x800]; size_t extents_from_this_segment; long bytes_in_buffer; if(first_extent+no_extents <= pdi_segment[current_segment].first_extent+pdi_segment[current_segment].no_extents) extents_from_this_segment=no_extents; else extents_from_this_segment= pdi_segment[current_segment].first_extent+ pdi_segment[current_segment].no_extents-first_extent; if (extents_from_this_segment){ /* this file contains whole segments */ copy_extents(fout,first_extent,extents_from_this_segment); first_extent+=extents_from_this_segment; if(!(no_extents-=extents_from_this_segment)) return; } if(0>lseek(fin, pdi_segment[current_segment].off + pdi_segment[current_segment].no_extents*0x800, SEEK_SET)) errexit("during skip of .pdi-data"); bytes_in_buffer=read(fin,small_buffer,0x800); if(bytes_in_buffer<0) errexit("reading partial extent, first part"); load_new_source(); if(0>lseek(fin, 0, SEEK_SET)) errexit("during rewind of .pdi-data"); if(bytes_in_buffer){ bytes_in_buffer=read(fin,small_buffer+bytes_in_buffer,0x800-bytes_in_buffer); if(bytes_in_buffer<(long)pdi_segment[current_segment].off) errexit("reading partial extent, second part"); if(0x800>write(fout,small_buffer,0x800)) errexit("during write of segmented extent"); first_extent++; no_extents--; } } } int main(int argc,char **argv){ int fout=-1; /* handle to output file */ size_t fdscno; /* count of output files */ size_t i; long l; char outname[999]; /* option flags */ int list_only=0; int make_directory=0; int make_VIDEO_TS=0; char* dest_dir=0; char* iso_name=0; int argi=1; char*iso_vol_name=0; char*list_file=0; int list_format=0; fprintf(stderr,"%s\n\n",id); /* option parsing */ while(argc>argi&&argv[argi][0]=='-'){ switch(argv[argi][1]){ case 'l': case 'L': list_only=1; break; case 'r': case 'R': delete_source=1; break; case 'm': case 'M': make_directory=1; case 'd': case 'D': dest_dir=argv[++argi]; break; case 'i': case 'I': iso_name=file_with_ext(argv[++argi],".iso"); break; case 'n': case 'N': iso_vol_name=argv[++argi]; break; case 'v': case 'V': make_VIDEO_TS=1; break; case 'g': case 'G': /* GUI options */ switch(argv[argi][2]) { case 'b': case 'B': list_format++; /* format 3: \n */ case 'f': case 'F': list_format++; /* format 2: \n\n */ case 's': case 'S': list_format++; /* format 1: ,\n */ case 'l': case 'L': /* format 0: \n */ list_only=1;list_file=argv[++argi]; break; default: usage(); } break; case 'x': case 'X': if(argc-argi!=2) usage(); make_directory=1; make_VIDEO_TS=1; delete_source=1; dest_dir=strdup(argv[argi+1]); l=strlen(dest_dir)-4; if(l>0&&!STRCMP(dest_dir+l,".pdi")) dest_dir[l]=0; /* strip .pdi extension */ break; default: usage(); } argi++; } if(argc==argi) usage(); if(argc-argi>1&&(iso_name||list_only)){ fprintf(stderr,"individual file selection may be used only in file extraction mode\n"); usage(); } infile=file_with_ext(argv[argi],".pdi"); fin=open(infile,O_RDONLY+O_BINARY); if(fin<0) errexit(infile); fdscno=pdi_read_dir(fin); if(list_file){ /* generate listing for PDIgui */ FILE*f=fopen(list_file,"wt"); char* list_formats[]={"%s\n","%s,%i\n","%s\n%i\n","%s %i\n"}; if(!f)errexit(list_file); for(i=0;i1){ /* there are file arguments specified */ /* now filter directory files through arguments */ int warning=0; while(++argi -v * * Revision 1.2 2003/02/18 17:39:03 smurf * Added new command-line switches: * -l list only * -r remove source files after extraction * -d extract files into instead of current directory * -m create directory and extract files into * * Revision 1.1 2003/02/17 15:08:50 smurf * Initial revision * * */