/* * xsa.c : decompresses XelaSoft Archives. * * Sean Young */ #include "osdepend.h" #include "imgtoolx.h" #include "utils.h" typedef struct { imgtool_image base; char *file_name; imgtool_stream *file_handle; int size; } XSA_IMAGE; typedef struct { imgtool_directory base; XSA_IMAGE *image; int index; } XSA_ITERATOR; static int xsa_extract (imgtool_stream *in, imgtool_stream *out); static int xsa_image_init(const imgtool_module *mod, imgtool_stream *f, imgtool_image **outimg); static void xsa_image_exit(imgtool_image *img); static int xsa_image_beginenum(imgtool_image *img, imgtool_directory **outenum); static int xsa_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent); static void xsa_image_closeenum(imgtool_directory *enumeration); static int xsa_image_readfile(imgtool_image *img, const char *fname, imgtool_stream *destf); IMAGEMODULE( xsa, "XelaSoft Archive", /* human readable name */ "xsa", /* file extension */ NULL, /* crcfile */ NULL, /* crc system name */ NULL, /* eoln */ 0, /* flags */ xsa_image_init, /* init function */ xsa_image_exit, /* exit function */ NULL, /* info function */ xsa_image_beginenum, /* begin enumeration */ xsa_image_nextenum, /* enumerate next */ xsa_image_closeenum, /* close enumeration */ NULL, /* free space on image */ xsa_image_readfile, /* read file */ NULL, /* write file */ NULL, /* delete file */ NULL, /* create image */ NULL, /* read sector */ NULL, /* write sector */ NULL, /* file options */ NULL /* create options */ ) static int xsa_image_init(const imgtool_module *mod, imgtool_stream *f, imgtool_image **outimg) { XSA_IMAGE *image; int pos, len, size; UINT8 header[4], byt; char *file_name, *file_name_new; size = stream_size (f); if (size < 5) return IMGTOOLERR_MODULENOTFOUND; if (4 != stream_read (f, header, 4) ) return IMGTOOLERR_READERROR; if (memcmp (header, "PCK\010", 4) ) return IMGTOOLERR_MODULENOTFOUND; if (4 != stream_read (f, &size, 4) ) return IMGTOOLERR_READERROR; size = LITTLE_ENDIANIZE_INT32 (size); if (4 != stream_read (f, header, 4) ) return IMGTOOLERR_READERROR; /* get name from XSA header, can't be longer than 8.3 really */ /* but could be, it's zero-terminated */ file_name = NULL; pos = len = 0; while (1) { if (1 != stream_read (f, &byt, 1) ) { if (file_name) free (file_name); *outimg=NULL; return IMGTOOLERR_READERROR; } if (len <= pos) { len += 8; /* why 8? */ file_name_new = realloc (file_name, len); if (!file_name_new) { if (file_name) free (file_name); *outimg=NULL; return IMGTOOLERR_OUTOFMEMORY; } file_name = file_name_new; } file_name[pos++] = byt; if (!byt) break; } image = (XSA_IMAGE*)malloc (sizeof (XSA_IMAGE) ); if (!image) return IMGTOOLERR_OUTOFMEMORY; *outimg = (imgtool_image*)image; memset(image, 0, sizeof(XSA_IMAGE)); image->base.module = mod; image->file_handle = f; image->size = size; image->file_name = file_name; return 0; } static void xsa_image_exit(imgtool_image *img) { XSA_IMAGE *image=(XSA_IMAGE*)img; stream_close(image->file_handle); free(image->file_name); free(image); } static int xsa_image_beginenum(imgtool_image *img, imgtool_directory **outenum) { XSA_IMAGE *image=(XSA_IMAGE*)img; XSA_ITERATOR *iter; iter=*(XSA_ITERATOR**)outenum = (XSA_ITERATOR*) malloc(sizeof(XSA_ITERATOR)); if (!iter) return IMGTOOLERR_OUTOFMEMORY; iter->base.module = img->module; iter->image=image; iter->index = 0; return 0; } static int xsa_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent) { XSA_ITERATOR *iter=(XSA_ITERATOR*)enumeration; ent->eof = iter->index; if (!ent->eof) { strcpy (ent->fname, iter->image->file_name); ent->corrupt=0; ent->filesize = iter->image->size; iter->index++; } return 0; } static void xsa_image_closeenum(imgtool_directory *enumeration) { free(enumeration); } static int xsa_image_readfile(imgtool_image *img, const char *fname, imgtool_stream *destf) { XSA_IMAGE *image=(XSA_IMAGE*)img; /* check file name */ if (mame_stricmp (fname, image->file_name) ) return IMGTOOLERR_MODULENOTFOUND; return xsa_extract (image->file_handle, destf); } /* * .xsa decompression. Code stolen from : * * http://web.inter.nl.net/users/A.P.Wulms/ */ /****************************************************************/ /* LZ77 data decompression */ /* Copyright (c) 1994 by XelaSoft */ /* version history: */ /* version 0.9, start date: 11-27-1994 */ /****************************************************************/ #define SHORT unsigned #define slwinsnrbits (13) #define maxstrlen (254) #define maxhufcnt (127) #define logtblsize (4) #define tblsize (1<weight=1+(tblsizes[count] >>= 1); } for (count=tblsize; count != 2*tblsize-1; count++) (hufpos++)->weight=-1; /* Place the nodes in the correct manner in the tree */ while (huftbl[2*tblsize-2].weight == -1) { for (hufpos=huftbl; !(hufpos->weight); hufpos++) ; l1pos = hufpos++; while (!(hufpos->weight)) hufpos++; if (hufpos->weight < l1pos->weight) { l2pos = l1pos; l1pos = hufpos++; } else l2pos = hufpos++; while ((tempw=(hufpos)->weight) != -1) { if (tempw) { if (tempw < l1pos->weight) { l2pos = l1pos; l1pos = hufpos; } else if (tempw < l2pos->weight) l2pos = hufpos; } hufpos++; } hufpos->weight = l1pos->weight+l2pos->weight; (hufpos->child1 = l1pos)->weight = 0; (hufpos->child2 = l2pos)->weight = 0; } updhufcnt = maxhufcnt; } /****************************************************************/ /* initialize the huffman info tables */ /****************************************************************/ static void inithufinfo( void ) { unsigned offs, count; for (offs=1, count=0; count != tblsize; count++) { cpdist[count] = offs; offs += (cpdbmask[count] = 1<child1) if (bitin()) { hufpos = hufpos->child2; } else { hufpos = hufpos->child1; } cpdindex = hufpos-huftbl; tblsizes[cpdindex]++; if (cpdbmask[cpdindex] >= 256) { UINT8 strposmsb, strposlsb; strposlsb = (unsigned)charin(); /* lees LSB van strpos */ strposmsb = 0; for (nrbits = cpdext[cpdindex]-8; nrbits--; strposmsb |= bitin()) strposmsb <<= 1; strpos = (unsigned)strposlsb | (((unsigned)strposmsb)<<8); } else { strpos=0; for (nrbits = cpdext[cpdindex]; nrbits--; strpos |= bitin()) strpos <<= 1; } if (!(updhufcnt--)) mkhuftbl(); /* make the huffman table */ return strpos+cpdist[cpdindex]; } /****************************************************************/ /* read a bit from the input file */ /****************************************************************/ static UINT8 bitin( void ) { UINT8 temp; if (!bitcnt) { bitflg = charin(); /* lees de bitflg in */ bitcnt = 8; /* nog 8 bits te verwerken */ } temp = bitflg & 1; bitcnt--; /* weer 1 bit verwerkt */ bitflg >>= 1; return temp; } /****************************************************************/ /* Get the next character from the input buffer. */ /****************************************************************/ static UINT8 charin( void ) { UINT8 byte; if (error_occ) return 0; else { if (1 != stream_read (in_stream, &byte, 1) ) { error_occ = IMGTOOLERR_READERROR; return 0; } else return byte; } }