#include "ninox.h" /* * Compress .fta archive files in the most efficient means possible * * Usage: * ./compress -o archive.ftz file1.fta file2.fta ... */ static int Compress(char *archive_name, char **input_list, int count); static int DeCompress(char *archive_fname); static int AddDirectory(char *dir, char *list[], int *count); static FILE * StartOutput(char *name); static int Error(int,char *,...); static int _do_compress(struct Image *img, char *archive_fname, FILE *out); static int _encode_frame(struct Image *img, char *buffer); static int _encode_frame_8(struct Image *img, unsigned char *buffer); static int _encode_frame_16(struct Image *img, unsigned short *buffer); static int _write_output(struct Image *img, char *archive_fname, unsigned char *buffer, int nbytes, FILE *out); static int _ftz_decode_frame_8(struct Image *img, unsigned char *buffer, int zbytes); static int _ftz_decode_frame_16(struct Image *img, unsigned short *buffer, int zbytes); static char *Output = NULL; static FILE *Output_z = NULL; #define ZRLE 1 #define Z12_16 2 #ifdef FTACOMPRESS_STANDALONE void Usage() { Print("Usage: compress -o archive.ftz file1.fta file2.fta ...\n"); Print("Usage: compress -d archive.ftz ...\n"); Print("Usage: compress -z -o ... (additionally, use bzip2 to further compress the archive)\n"); } int main(int argc, char *argv[]) { char *archive_name = NULL; char *input_list[128],*tmp_archive_name; int i,count,decompress=0,do_bzip=0; count=0; for(i=1; ival - p2->val; } static int AddDirectory(char *dir, char *list[], int *count) { DIR *D; struct dirent *e; struct __d dlist[32]; int i,dcount=0,len=strlen(dir); D = opendir(dir); if (!D) Error(1,"Cannot open directory '%s'\n",dir); if (dir[len-1] == '/' || dir[len-1]=='\\') dir[len-1]=0; while(e = readdir(D)) { if (e->d_name[0] == '.') continue; if (strlen(e->d_name) < 5) continue; if (!strcmp(e->d_name + strlen(e->d_name)-4, ".fta")) { dlist[dcount].name = Malloc(strlen(dir) + strlen(e->d_name) + 2); sprintf(dlist[dcount].name,"%s/%s",dir,e->d_name); dcount++; } } closedir(D); // Set the values for sorting for(i=0; i %s\n",input_list[i],tmp_archive_name); while(1) { struct Image *img = ArchiveLoadNextImage(in,&err,ARCHIVE_LOAD); if (err<0) { Print("Fatal error reading archive '%s'\n",input_list[i]); exit(1); } else if (err==0) { break; } else if (err==1) { if (!_do_compress(img,input_list[i],out)) Error(1,"Error during compress, abort!\n"); } else Error(1,"Unknown return value %d\n",err); if (n && (n%50)==0) { printf("."); fflush(stdout); } ++n; } } fflush(out); fclose(out); Print("Finished, renaming %s -> %s\n",tmp_archive_name,archive_name); unlink(archive_name); rename(tmp_archive_name,archive_name); return(0); } static int Error(int errval, char *fmt, ...) { int n, size = 100; char *p; va_list ap; static int start_of_line = 1; FILE *fd = stdout; if (Output_z) fclose(Output_z); if (! errval) exit(errval); Print("Error %d occurred while writing to '%s'\n",errval,Output); unlink(Output); if ((p = (char *)malloc (size)) == NULL) { fprintf(fd,"Print: Out of memory!\n"); fflush(fd); exit(1); } while (1) { /* Try to print in the allocated space. */ va_start(ap, fmt); n = vsnprintf (p, size, fmt, ap); va_end(ap); /* If that worked, return the string. */ if (n > -1 && n < size) { for(n=0; n -1) /* glibc 2.1 */ size = n+1; /* precisely what is needed */ else /* glibc 2.0 */ size *= 2; /* twice the old size */ if ((p = realloc (p, size)) == NULL) { fprintf(fd,"Print: Out of memory!\n"); fflush(fd); exit(1); } } // notreached fflush(fd); exit(errval); } static FILE * StartOutput(char *name) { Output_z = fopen(name,"wb"); if (Output_z == NULL) return NULL; Output = strdup(name); return Output_z; } static int _do_compress(struct Image *img, char *archive_fname, FILE *out) { static char *buffer = NULL; static int bsize = 0; int width = img->width; int height = img->height; int bpp = img->depth/8; int nbytes = width * height * bpp; //Print("[%s]: ",img->dst_fname); if (bsize>0 && bsize != nbytes) { Print("Buffer size change, not supported - abort!\n"); return 0; } // Init the buffer if (buffer == NULL) buffer = Malloc(nbytes); nbytes = _encode_frame(img,buffer); if (! nbytes) Error(1,"Encode %s/%s failed\n",archive_fname,img->dst_fname); if (! _write_output(img,archive_fname,buffer,nbytes,out)) Error(1,"Error writing output to %s (%s/%s)\n",Output,archive_fname,img->dst_fname); DestroyImage(img); return nbytes; } static int _encode_frame(struct Image *img, char *buffer) { switch(img->depth) { case 8: return _encode_frame_8(img,buffer); break; case 16: return _encode_frame_16(img,(unsigned short *)buffer); break; default: Print("_encode_frame: Depth %d not handled\n"); return 0; break; } Error(1,"Error, _encode_frame, notreached\n"); } static int _encode_frame_8(struct Image *img, unsigned char *buffer) { Error(1,"_encode_frame_8: not implemented\n"); } static int _estimate_background_16(struct Image *img) { unsigned short *ibuf = (unsigned short *)img->data; int width = img->width; int height = img->height; int npixels = width * height; double bgtotal; int bgcount; int tilesize = 5; // Use 5x5 tiles int zmin = 2; // Must find at least zmin pixels with value 0 // to count this tile towards the background int x1,y1,x,y,i; bgtotal=0; bgcount=0; for(y1=0; y1<=height-tilesize; y1+=tilesize) for(x1=0; x1<=width-tilesize; x1+=tilesize) { int zcount=0; unsigned int total=0; for(y=y1,zcount=0; y= zmin) { bgtotal += total; bgcount += tilesize<<1; } } return (int)(bgtotal/bgcount); } static int _encode_frame_16(struct Image *img, unsigned short *buffer) { unsigned short *ibuf = (unsigned short *)img->data; struct fits_info *fi = (struct fits_info *)img->info; int npixels = img->width * img->height; int x,y,n=0,total,ltotal; int width = img->width; int height = img->height; int maxval=0,minval,minval1; int i,zcount,do_shift,tmpval; int state,mode,bgval; // Estimate the background value and store it in the header minval = _estimate_background_16(img); minval1 = minval * 2; //Print("Background level: %d (%d/256)\n",minval,minval/256); for(i=0; i<2880; i += 80) if (! strncmp(fi->header+i,"END ",4)) { char strtmp[32]; sprintf(strtmp,"(bg=%d)",minval); strncpy(fi->header+i+4,strtmp,strlen(strtmp)); } // Detect false zeroes and map to zero. // Use buffer[] as the target for(y=0; y=width-1 || y>=height-1) n=0; else if (n<=minval) n=0; else if (n<=minval1) { int zcount=0; if (ibuf[i-width-1]<=minval) ++zcount; if (ibuf[i-width]<=minval) ++zcount; if (ibuf[i-width+1]<=minval) ++zcount; if (ibuf[i-1]<=minval) ++zcount; if (ibuf[i+1]<=minval) ++zcount; if (ibuf[i+width-1]<=minval) ++zcount; if (ibuf[i+width]<=minval) ++zcount; if (ibuf[i+width+1]<=minval) ++zcount; if (zcount>=4) n=0; } if (n>maxval) maxval=n; buffer[i] = n; } for(i=0; i=4096) { tmpval>>=1; ++do_shift; } //printf("%d bit data shift required\n",do_shift); // Step 4 - scan the input buffer and encode pixels using // either ZRLE or Z12_16 schemes. The magic 16 bit value 0 switches // between the schemes. total=ltotal=zcount=state=0; mode = ZRLE; buffer[total++] = do_shift; for(i=0; i=2047) { if (zcount<15) buffer[total++] = (zcount<<12) | (ibuf[i]>>do_shift); else { buffer[total++] = (15<<12) | zcount; buffer[total++] = ibuf[i]; } zcount=0; if (ibuf[i+1] && ibuf[i+2] && ibuf[i+3] && ibuf[i+4]) { buffer[total++] = 0; mode=Z12_16; } } else ++zcount; } else { // mode is Z12_16 if (state==0) { tmpval=ibuf[i]>>do_shift; if (tmpval&0xf000) Error(1,"tmpval %d too large (%d >> %d)\n",tmpval,ibuf[i],do_shift); state++; } else if (state==1) { unsigned short v = ibuf[i]; v>>=do_shift; buffer[total++] = v | ((tmpval&0xf)<<12); state++; } else if (state==2) { unsigned short v = ibuf[i]; v>>=do_shift; buffer[total++] = v | ((tmpval&0xf0)<<8); state++; } else if (state==3) { unsigned short v = ibuf[i]; v>>=do_shift; buffer[total++] = v | ((tmpval&0xf00)<<4); state=0; if (i>do_shift)==0)) {mode=ZRLE; buffer[total++]=0; } } } } //printf("%d/%d data bytes = %lf\n",total,npixels,(double)total/npixels); //if (mode==ZRLE) Print("%d trailing black pixels skipped\n",zcount); return total * 2; // return number of bytes } static int _write_output(struct Image *img, char *archive_fname,unsigned char *buffer, int nbytes, FILE *out) { struct fits_info *fi = img->info; if (! fwrite((void *)archive_fname,strlen(archive_fname)+1,1,out)) Error(1,"Write failed on %s\n",Output); if (! fwrite((void *)img->src_fname,strlen(img->src_fname)+1,1,out)) Error(1,"Write failed on %s\n",Output); if (! fwrite((void *)fi->header,2880,1,out)) Error(1,"Write failed on %s\n",Output); if (! fwrite((void *)&nbytes,sizeof(nbytes),1,out)) Error(1,"Write failed on %s\n",Output); if (! fwrite((void *)buffer,nbytes,1,out)) Error(1,"Write failed on %s\n",Output); fflush(out); return nbytes; } //------------------------------------------------------------------------------------------ static int DeCompress(char *fname) { struct Image *img = CreateImage(); struct fits_info *fi; unsigned char *buffer = NULL; unsigned char *obuf = NULL; char archive_fname[1024],tmpstr[128],*ptr; char current_archive_fname[1024]; int ntmp,nbytes,zbytes; FILE *in,*out=NULL; extern int HistoStretch; HistoStretch = 0; Print("Decompressing from '%s'\n",fname); if ((in = fopen(fname,"rb")) == NULL) Error(1,"Error opening file '%s'\n",fname); current_archive_fname[0]=0; while(1) { // read null-terminated real filename ptr = archive_fname; *ptr=0; while(1) { int ch = ReadByte(in); if (ch==EOF) { Print("Finished reading input\n"); return 1; } if (ch<0) { return 0;} if (strlen(archive_fname) >= 1023) Error(1,"Archive entry '%s/%s' error: filename too long\n",archive_fname); *(ptr++) = ch; *ptr=0; if (ch==0) break; } // read null-terminated filename inside // the archive ptr = tmpstr; *ptr=0; while(strlen(tmpstr)<32) { int ch = ReadByte(in); if (ch==EOF) { return 0;} if (ch<0) { return 0;} *(ptr++) = ch; *ptr=0; if (ch==0) break; } img->type = IMG_FIT; img->src_fname = strdup(tmpstr); img->dst_fname = strdup(tmpstr); Print("Decoding Image: [%s/%s] ",archive_fname,img->src_fname); // Read the 2880 (36 x 80) header records if (! load_image_header(in,img)) Error(1,"End of input data\n"); if (! obuf) { obuf = Malloc(img->width*img->height*2); } if (! buffer) { buffer = Malloc(img->width*img->height*2); } if (img->dst_width==0) img->dst_width = img->width; if (img->dst_height==0) img->dst_height = img->height; Print("%dx%d (%d bpp)\n",img->width,img->height,img->depth); if (Fread((unsigned char *)&zbytes,sizeof(zbytes),1,in) != 1) Error(1,"Error: Short read on zbytes\n"); // Print("%d bytes of compressed data\n",zbytes); // Print("File Offset %ld\n",lseek(fileno(in),0,SEEK_CUR)); if (Fread(buffer, zbytes, 1, in) != 1) Error(1,"Short read on data\n"); img->data = obuf; nbytes = _ftz_decode_frame(img,buffer,zbytes); if (! nbytes) Error(1,"Error decoding data\n"); if (strcmp(archive_fname,current_archive_fname)) { char *ptr = archive_fname; Print("Opening %s for Writing\n",archive_fname); strcpy(current_archive_fname,archive_fname); if (out) fclose(out); // Check and make sure parent directories exist while(*ptr) { if (*(ptr+1) == '/' || *(ptr+1)=='\\') { *(ptr+1)=0; if (! isDirectory(archive_fname)) { Print("Mkdir(%s)\n",archive_fname); Mkdir(archive_fname); } *(ptr+1) = '/'; } ++ptr; } out = fopen(archive_fname,"wb"); if (! out) Error(1,"Error: cannot open '%s' for writing\n",archive_fname); } fwrite(img->src_fname,strlen(img->src_fname)+1,1,out); fi = (struct fits_info *)img->info; fwrite(fi->header,2880,1,out); ntmp = write_image_data(out,img); //printf("wrote %d/%d bytes for %s\n",ntmp,nbytes,img->dst_fname); } // notreached return 0; } int _ftz_decode_frame(struct Image *img, char *buffer,int zbytes) { switch(img->depth) { case 8: return _ftz_decode_frame_8(img,buffer,zbytes); break; case 16: return _ftz_decode_frame_16(img,(unsigned short *)buffer,zbytes); break; default: Print("_ftz_decode_frame: Depth %d not handled\n"); return 0; break; } Error(1,"Error, _ftz_decode_frame, notreached\n"); return 0; } static int _ftz_decode_frame_8(struct Image *img, unsigned char *buffer, int zbytes) { Error(1,"_decode_frame_8: not implemented\n"); return 0; } static int _ftz_decode_frame_16(struct Image *img, unsigned short *ibuf, int zwords) { struct fits_info *fi = (struct fits_info *)img->info; unsigned short *buffer = (unsigned short *)img->data; unsigned short *tmpbuf; int width = img->width; int npixels = width * img->height; int x,y,n,total; int i,do_shift,tmpval; int mode,black=ZBlack<<8,black2; unsigned int zcount; // See if the default bg is encoded into the header if (! black) for(i=0; i<80*36; i+=80) if (!strncmp(fi->header+i,"END (",5)) { sscanf(fi->header+i+4,"(bg=%d)",&black); } // Step 1 - recover the shift and nbytes data from the start // of the stream i=0; zwords >>= 1; do_shift=ibuf[i++]; //printf("%d zwords, %d bit data shift required\n",zwords,do_shift); // Step 2 - scan the input buffer and decode pixels using // either ZRLE or Z12_16 schemes. The magic 16 bit value 0 switches // between the schemes. total=zcount=0; mode = ZRLE;; while(i>12; if (zcount==15) {zcount = ibuf[i++]&0xfff; n = ibuf[i]; } else n = (ibuf[i]&0xfff) << do_shift;; while(zcount--) buffer[total++]=black; buffer[total++] = n; } else { // mode is Z12_16 int v; tmpval=ibuf[i]>>12; v = (ibuf[i++]&0xfff) << do_shift; if (0 && v>8; v = (ibuf[i++]&0xfff) << do_shift; if (0 && v>4; v = (ibuf[i++]&0xfff) << do_shift; if (0 && v npixels) Error(1,"oops, too many decode pixels\n"); } //Print("reconstituted %d/%d pixels (%lf)\n",total,npixels,(double)total/npixels); if (total < npixels) { //Print("Padding with %d black pixels\n",npixels-total); while(totalheight-1; ++y) for(x=1,i=y*width+x; xheight-1; ++y) for(x=1,i=y*width+x; x