/* pennovate2bmp - version 0.1 * Seb Wills 2005 * Quick, hacky program to convert from the PDB files which Pennovate's * PalmOS note-taking application (http://www.pennovate.com) saves (when * you 'save to handheld') into regular .bmp files. * Note that if you save to external memory card * from Pennovate then I believe it saves bmp directly. This program * is useful if you retrieve the pdb files direct from your handheld's * internal memory. * * Notes: * * 1. I output bmp because that's how the data is stored in the pdb file, * not because I think it's a sensible format to use. * 2. The bmp file is flipped vertically, as a result of the way data is stored. * I could flip it back in this program, but since I'm going to want to * feed the output through 'convert' (ImageMagick) to get it into a more * sensible format in any case, I'll just use the '-flip' convert option * at that stage. * * I used http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html * and http://www.daubnet.com/formats/BMP.html for info about bmp format, * and reverse-engineered the Pennovate database file * with help from http://www.nicholson.com/rhn/palm/pdb.txt * * Essentially, the input is a PDB file containing a number of "records", each * containing a block of binary data. Assembling together the data payloads from * all the records gives you a block of data consisting of 20 bytes of header, * followed by the image data in the same format used for the image data part * of BMP files (except that the rows are not in reverse order as they are in * bmp files). * * Seb Wills 2005 * Freely distributable and modifiable, but if you improve it I would appreciate * you sending me a copy of your improvements. */ #include unsigned int read_twobytes_be(FILE *stream); unsigned int read_fourbytes_be(FILE *stream); void check_header(FILE *in, char *str); void make_bmp_header(unsigned char *d, unsigned int *hdrsize, unsigned int width, unsigned int height); int bmp_bytes_per_row(width,height) { /* returns the number of bytes in a "row" of bmp data for a 2 colour bmp */ int rowlen; rowlen=width/8; /* 8 bits(pixels) per byte */ if(rowlen % 4 !=0) { rowlen=rowlen+ 4-(rowlen % 4); /* pad out to multiple of 4 bytes */ } return rowlen; } int main(int argc, char **argv) { FILE *in, *out; char fourc[5]; int numrecords,i; unsigned int *offsets; unsigned int width, height, hdrsize; unsigned char cptr[32*1024]; unsigned int count, bytecount, imagebytes,rowlen; if (argc < 3) { fprintf(stderr,"\nUsage: %s filename.pdb outfile.bmp\n\ \n\ Converts Pennovate's note format (as saved to handheld as .pdb) to a\n\ (vertical mirror-image) .bmp file.\ \n\ Can use - instead of filename(s) for stdin/stdout.\n\n", argv[0]); exit(1); } if (strcmp(argv[1],"-")==0) { in=stdin; } else { if((in=fopen(argv[1],"rb"))==NULL) { fprintf(stderr,"Error opening file %s.",argv[1]); exit(1); } } if (strcmp(argv[2],"-")==0) { out=stdout; } else { if((out=fopen(argv[2],"wb"))==NULL) { fprintf(stderr,"Error opening file %s.",argv[2]); exit(1); } } /* read header stuff */ safe_fseek(in, 0x40, SEEK_SET); fread(fourc,4,1,in); fourc[4]=0; if(strcmp(fourc,"PNVT") != 0) { fprintf(stderr,"Creator seems to be %s; expecting PNVT. Aborting.",fourc); exit(1); } safe_fseek(in, 0x4c, SEEK_SET); numrecords=read_twobytes_be(in); fprintf(stderr,"Contains %d records.\n",numrecords); offsets=(unsigned int *) calloc(numrecords,sizeof(unsigned int)); if(offsets==NULL) { fprintf(stderr,"calloc failed."); exit(1); } /* already at correct seek position for first record entry */ for(i=0; i< numrecords; i++) { offsets[i]=read_fourbytes_be(in); safe_fseek(in,4,SEEK_CUR); fprintf(stderr,"Block %d at 0x%x\n",i+1,offsets[i]); } /* seek to first data block */ safe_fseek(in,offsets[0],SEEK_SET); /* each record starts with DBLK and then four other bytes. The * remaining bytes until the start of the next record are data */ check_header(in,"DBLK"); safe_fseek(in,4,SEEK_CUR); /* skip four unknown */ /* first two dwords in the data are width and height */ width=read_twobytes_be(in); height=read_twobytes_be(in); fprintf(stderr,"Image size: %dx%d\n",width,height); safe_fseek(in,16,SEEK_CUR); /* unknown remainder of header */ /* now at start of image data */ fprintf(stderr,"Writing bmp header and data from record 1...\n"); make_bmp_header(cptr, &hdrsize, width,height); count=fwrite(cptr, 1, hdrsize, out); fprintf(stderr,"Wrote %d bytes\n",count); if(count !=hdrsize) { fprintf(stderr,"fwrite didn't work!\n"); exit(1); } /* write remainder of first record (the first chunk of actual bmp image data */ if(offsets[1]-offsets[0] > 24) { count=fread(cptr, 1, offsets[1]-offsets[0]-28, in); fwrite(cptr, 1, offsets[1]-offsets[0]-28, out); if(count != offsets[1]-offsets[0]-28) { fprintf(stderr,"fwrite failed!\n"); exit(1); } bytecount=count; } /* write other records */ for(i=1;i31) { fprintf(stderr,"arg too long in check_header.\n"); exit(1); } fread(c,1,strlen(str),in); c[strlen(str)]=0; if (strcmp(c,str) != 0) { fprintf(stderr,"Didn't find expected header string '%s'\n",str); exit(1); } } void make_bmp_header(unsigned char *d, unsigned int *hdrsize, unsigned int width, unsigned int height) { /* makes a suitable header for a BMP file for a 2-color bmp file */ /* This assumes we're running on a Little Endian architecture */ int i; unsigned int filesize; unsigned int rowlen; *hdrsize=54+4*2; /* 2 colour table entries */ rowlen=bmp_bytes_per_row(width,height); filesize=*hdrsize+height*rowlen; if (sizeof(unsigned short int)!=2) { fprintf(stderr,"Code requires platform/compiler with unsigned short int being 2 bytes!"); exit(1); } for(i=0;i<*hdrsize;i++) { d[i]=0; /* most values need be zero, so initialise everything that way */ } d[0]='B'; d[1]='M'; *((unsigned int *) (d+2)) = filesize; *((unsigned int *) (d+10)) = *hdrsize; /* offset to bitmap data */ *((unsigned int *) (d+14)) = 40; /* BITMAPINFOHEADER size */ *((unsigned int *) (d+18)) = width; *((unsigned int *) (d+22)) = height; *((unsigned short int *) (d+26)) = 1; /* number of planes */ *((unsigned int *) (d+28)) = 1; /* bits per pixel */ *((unsigned int *) (d+46)) = 2; /* number of colours */ /* now colour table */ /* Colour 0 is white for Pennovate: */ d[54]=255; d[55]=255; d[56]=255; /*bytes at offsets 58-60 are already zero, for the colour 1 RGB*/ }