int decrunch_xfd (FILE *f1, FILE *f2) { struct xfdBufferInfo *xfdobj; uint8 *packed; int plen,ret=-1; struct stat st; struct local_data data; if (f2 == NULL) return -1; fstat(fileno(f1), &st); plen = st.st_size; packed = AllocVec(plen,MEMF_CLEAR); if (!packed) return -1; fread(packed,plen,1,f1); if(xfdobj=open_xfd(&data)) { xfdobj->xfdbi_SourceBufLen = plen; xfdobj->xfdbi_SourceBuffer = packed; xfdobj->xfdbi_Flags = XFDFB_RECOGEXTERN | XFDFB_RECOGTARGETLEN; xfdobj->xfdbi_PackerFlags = XFDPFB_RECOGLEN; if(xfdRecogBuffer(xfdobj)) { xfdobj->xfdbi_TargetBufMemType = MEMF_ANY; if(xfdDecrunchBuffer(xfdobj)) { if(fwrite(xfdobj->xfdbi_TargetBuffer,1,xfdobj->xfdbi_TargetBufSaveLen,f2) == xfdobj->xfdbi_TargetBufSaveLen) ret=0; FreeMem(xfdobj->xfdbi_TargetBuffer,xfdobj->xfdbi_TargetBufLen); } else { ret=-1; } } close_xfd(xfdobj, &data); } FreeVec(packed); return(ret); }
char *test_xfd (unsigned char *buffer, int length) { char *ret = NULL; struct xfdBufferInfo *xfdobj; struct local_data data; if(xfdobj=open_xfd(&data)) { xfdobj->xfdbi_SourceBuffer = buffer; xfdobj->xfdbi_SourceBufLen = length; xfdobj->xfdbi_Flags = XFDFB_RECOGTARGETLEN | XFDFB_RECOGEXTERN; if(xfdRecogBuffer(xfdobj)) { ret = xfdobj->xfdbi_PackerName; } close_xfd(xfdobj, &data); } return(ret); }
/* Identify a file without attempting to decompress it */ libspectrum_error libspectrum_identify_file_raw( libspectrum_id_t *type, const char *filename, const unsigned char *buffer, size_t length ) { struct type { int type; const char *extension; int extension_score; const char *signature; size_t offset, length; int sig_score; }; struct type *ptr, types[] = { { LIBSPECTRUM_ID_RECORDING_RZX, "rzx", 3, "RZX!", 0, 4, 4 }, { LIBSPECTRUM_ID_SNAPSHOT_SNA, "sna", 3, NULL, 0, 0, 0 }, /* Peter McGavin's Spectrum Emulator on the Amiga used .snapshot for sna snaps */ { LIBSPECTRUM_ID_SNAPSHOT_SNA, "snapshot", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_SNAPSHOT_SNP, "snp", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_SNAPSHOT_SP, "sp", 3, "\x53\x50\0", 0, 3, 1 }, { LIBSPECTRUM_ID_SNAPSHOT_SZX, "szx", 3, "ZXST", 0, 4, 4 }, { LIBSPECTRUM_ID_SNAPSHOT_Z80, "z80", 3, "\0\0", 6, 2, 1 }, /* .slt files also dealt with by the .z80 loading code */ { LIBSPECTRUM_ID_SNAPSHOT_Z80, "slt", 3, "\0\0", 6, 2, 1 }, { LIBSPECTRUM_ID_SNAPSHOT_ZXS, "zxs", 3, "SNAP", 8, 4, 4 }, { LIBSPECTRUM_ID_SNAPSHOT_PLUSD,"mgtsnp", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_CARTRIDGE_DCK, "dck", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_CARTRIDGE_IF2, "rom", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_MICRODRIVE_MDR, "mdr", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_TAPE_TAP, "tap", 3, "\x13\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_SPC, "spc", 3, "\x11\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_STA, "sta", 3, "\x11\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_LTP, "ltp", 3, "\x11\0\0", 0, 3, 1 }, { LIBSPECTRUM_ID_TAPE_TZX, "tzx", 3, "ZXTape!", 0, 7, 4 }, { LIBSPECTRUM_ID_TAPE_WARAJEVO, "tap", 2, "\xff\xff\xff\xff", 8, 4, 2 }, { LIBSPECTRUM_ID_DISK_DSK, "dsk", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_DISK_SCL, "scl", 3, "SINCLAIR", 0, 8, 4 }, { LIBSPECTRUM_ID_DISK_TRD, "trd", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_HARDDISK_HDF, "hdf", 3, "RS-IDE\x1a", 0, 7, 4 }, { LIBSPECTRUM_ID_COMPRESSED_BZ2,"bz2", 3, "BZh", 0, 3, 4 }, { LIBSPECTRUM_ID_COMPRESSED_GZ, "gz", 3, "\x1f\x8b", 0, 2, 4 }, { LIBSPECTRUM_ID_TAPE_Z80EM, "raw", 1, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Raw tape sample", 0, 64, 0 }, { LIBSPECTRUM_ID_TAPE_CSW, "csw", 2, "Compressed Square Wave\x1a", 0, 23, 4 }, { LIBSPECTRUM_ID_TAPE_WAV, "wav", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_DISK_MGT, "mgt", 3, NULL, 0, 0, 0 }, { LIBSPECTRUM_ID_DISK_IMG, "img", 3, NULL, 0, 0, 0 }, { -1, NULL, 0, NULL, 0, 0, 0 }, /* End marker */ }; const char *extension = NULL; int score, best_score, best_guess, duplicate_best; /* Get the filename extension, if it exists */ if( filename ) { extension = strrchr( filename, '.' ); if( extension ) extension++; } best_guess = LIBSPECTRUM_ID_UNKNOWN; best_score = 0; duplicate_best = 0; /* Compare against known extensions and signatures */ for( ptr = types; ptr->type != -1; ptr++ ) { score = 0; if( extension && ptr->extension && !strcasecmp( extension, ptr->extension ) ) score += ptr->extension_score; if( ptr->signature && length >= ptr->offset + ptr->length && !memcmp( &buffer[ ptr->offset ], ptr->signature, ptr->length ) ) score += ptr->sig_score; if( score > best_score ) { best_guess = ptr->type; best_score = score; duplicate_best = 0; } else if( score == best_score && ptr->type != best_guess ) { duplicate_best = 1; } } #if defined AMIGA || defined __MORPHOS__ /* Compressed file check through xfdmaster.library */ /* this prevents most good matches and gz/bz2 from being run through xfd (xfd supports gz, but we want to use the internal code) */ if( best_score <= 3 ) { #ifndef __MORPHOS__ struct ExecIFace *IExec = (struct ExecIFace *)(*(struct ExecBase **)4)->MainInterface; #endif /* #ifndef __MORPHOS__ */ struct xfdBufferInfo *xfdobj; #ifndef __MORPHOS__ if( xfdMasterBase = IExec->OpenLibrary("xfdmaster.library",38 ) ) { if( IxfdMaster = (struct xfdMasterIFace *)IExec->GetInterface(xfdMasterBase,"main",1,NULL)){ if( xfdobj = (struct xfdBufferInfo *)IxfdMaster->xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #else /* #ifndef __MORPHOS__ */ if( xfdMasterBase = OpenLibrary("xfdmaster.library",38 ) ) { if( xfdobj = (struct xfdBufferInfo *)xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #endif /* #ifndef __MORPHOS__ */ xfdobj->xfdbi_SourceBuffer = buffer; xfdobj->xfdbi_SourceBufLen = length; xfdobj->xfdbi_Flags = XFDFB_RECOGTARGETLEN | XFDFB_RECOGEXTERN; #ifndef __MORPHOS__ if( IxfdMaster->xfdRecogBuffer( xfdobj ) ) { #else /* #ifndef __MORPHOS__ */ if( xfdRecogBuffer( xfdobj ) ) { #endif /* #ifndef __MORPHOS__ */ best_guess = LIBSPECTRUM_ID_COMPRESSED_XFD; duplicate_best=0; } #ifndef __MORPHOS__ IxfdMaster->xfdFreeObject( (APTR)xfdobj ); #else /* #ifndef __MORPHOS__ */ xfdFreeObject( (APTR)xfdobj ); #endif /* #ifndef __MORPHOS__ */ } #ifndef __MORPHOS__ IExec->DropInterface( (struct Interface *)IxfdMaster ); } IExec->CloseLibrary( xfdMasterBase ); #else /* #ifndef __MORPHOS__ */ CloseLibrary( xfdMasterBase ); #endif /* #ifndef __MORPHOS__ */ } } #endif /* #ifdef AMIGA */ /* If two things were equally good, we can't identify this. Otherwise, return our best guess */ if( duplicate_best ) { *type = LIBSPECTRUM_ID_UNKNOWN; } else { *type = best_guess; } return LIBSPECTRUM_ERROR_NONE; } /* What generic 'class' of file is this file */ libspectrum_error libspectrum_identify_class( libspectrum_class_t *libspectrum_class, libspectrum_id_t type ) { switch( type ) { case LIBSPECTRUM_ID_UNKNOWN: *libspectrum_class = LIBSPECTRUM_CLASS_UNKNOWN; return 0; case LIBSPECTRUM_ID_CARTRIDGE_DCK: *libspectrum_class = LIBSPECTRUM_CLASS_CARTRIDGE_TIMEX; return 0; case LIBSPECTRUM_ID_COMPRESSED_BZ2: case LIBSPECTRUM_ID_COMPRESSED_GZ: case LIBSPECTRUM_ID_COMPRESSED_XFD: *libspectrum_class = LIBSPECTRUM_CLASS_COMPRESSED; return 0; case LIBSPECTRUM_ID_DISK_DSK: *libspectrum_class = LIBSPECTRUM_CLASS_DISK_PLUS3; return 0; case LIBSPECTRUM_ID_DISK_IMG: case LIBSPECTRUM_ID_DISK_MGT: *libspectrum_class = LIBSPECTRUM_CLASS_DISK_PLUSD; return 0; case LIBSPECTRUM_ID_DISK_SCL: case LIBSPECTRUM_ID_DISK_TRD: *libspectrum_class = LIBSPECTRUM_CLASS_DISK_TRDOS; return 0; case LIBSPECTRUM_ID_HARDDISK_HDF: *libspectrum_class = LIBSPECTRUM_CLASS_HARDDISK; return 0; case LIBSPECTRUM_ID_MICRODRIVE_MDR: *libspectrum_class = LIBSPECTRUM_CLASS_MICRODRIVE; return 0; case LIBSPECTRUM_ID_RECORDING_RZX: *libspectrum_class = LIBSPECTRUM_CLASS_RECORDING; return 0; case LIBSPECTRUM_ID_SNAPSHOT_PLUSD: case LIBSPECTRUM_ID_SNAPSHOT_SNA: case LIBSPECTRUM_ID_SNAPSHOT_SNP: case LIBSPECTRUM_ID_SNAPSHOT_SP: case LIBSPECTRUM_ID_SNAPSHOT_SZX: case LIBSPECTRUM_ID_SNAPSHOT_Z80: case LIBSPECTRUM_ID_SNAPSHOT_ZXS: *libspectrum_class = LIBSPECTRUM_CLASS_SNAPSHOT; return 0; case LIBSPECTRUM_ID_TAPE_TAP: case LIBSPECTRUM_ID_TAPE_SPC: case LIBSPECTRUM_ID_TAPE_STA: case LIBSPECTRUM_ID_TAPE_LTP: case LIBSPECTRUM_ID_TAPE_TZX: case LIBSPECTRUM_ID_TAPE_WARAJEVO: case LIBSPECTRUM_ID_TAPE_Z80EM: case LIBSPECTRUM_ID_TAPE_CSW: case LIBSPECTRUM_ID_TAPE_WAV: *libspectrum_class = LIBSPECTRUM_CLASS_TAPE; return 0; case LIBSPECTRUM_ID_CARTRIDGE_IF2: *libspectrum_class = LIBSPECTRUM_CLASS_CARTRIDGE_IF2; return 0; } libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "Unknown file type %d", type ); return LIBSPECTRUM_ERROR_UNKNOWN; } libspectrum_error libspectrum_uncompress_file( unsigned char **new_buffer, size_t *new_length, char **new_filename, libspectrum_id_t type, const unsigned char *old_buffer, size_t old_length, const char *old_filename ) { libspectrum_class_t class; libspectrum_error error; error = libspectrum_identify_class( &class, type ); if( error ) return error; if( class != LIBSPECTRUM_CLASS_COMPRESSED ) { libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "file type %d is not a compressed type", type ); return LIBSPECTRUM_ERROR_LOGIC; } if( new_filename && old_filename ) { *new_filename = strdup( old_filename ); if( !*new_filename ) { libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY, "out of memory at %s:%d", __FILE__, __LINE__ ); return LIBSPECTRUM_ERROR_MEMORY; } } /* Tells the inflation routines to allocate memory for us */ *new_length = 0; switch( type ) { case LIBSPECTRUM_ID_COMPRESSED_BZ2: #ifdef HAVE_LIBBZ2 if( new_filename && *new_filename ) { if( strlen( *new_filename ) >= 4 && !strcasecmp( &(*new_filename)[ strlen( *new_filename ) - 4 ], ".bz2" ) ) (*new_filename)[ strlen( *new_filename ) - 4 ] = '\0'; } error = libspectrum_bzip2_inflate( old_buffer, old_length, new_buffer, new_length ); if( error ) { if( new_filename ) free( *new_filename ); return error; } #else /* #ifdef HAVE_LIBBZ2 */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "libbz2 not available to decompress bzipped file" ); if( new_filename ) free( *new_filename ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_LIBBZ2 */ break; case LIBSPECTRUM_ID_COMPRESSED_GZ: #ifdef HAVE_ZLIB_H if( new_filename && *new_filename ) { if( strlen( *new_filename ) >= 3 && !strcasecmp( &(*new_filename)[ strlen( *new_filename ) - 3 ], ".gz" ) ) (*new_filename)[ strlen( *new_filename ) - 3 ] = '\0'; } error = libspectrum_gzip_inflate( old_buffer, old_length, new_buffer, new_length ); if( error ) { if( new_filename ) free( *new_filename ); return error; } #else /* #ifdef HAVE_ZLIB_H */ libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "zlib not available to decompress gzipped file" ); if( new_filename ) free( *new_filename ); return LIBSPECTRUM_ERROR_UNKNOWN; #endif /* #ifdef HAVE_ZLIB_H */ break; #if defined AMIGA || defined __MORPHOS__ case LIBSPECTRUM_ID_COMPRESSED_XFD: { #ifndef __MORPHOS__ struct ExecIFace *IExec = (struct ExecIFace *)(*(struct ExecBase **)4)->MainInterface; #endif /* #ifndef __MORPHOS__ */ struct xfdBufferInfo *xfdobj; #ifndef __MORPHOS__ if( xfdMasterBase = IExec->OpenLibrary( "xfdmaster.library", 38 ) ) { if( IxfdMaster = (struct xfdMasterIFace *)IExec->GetInterface(xfdMasterBase,"main",1,NULL)) { if( xfdobj = (struct xfdBufferInfo *)IxfdMaster->xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #else /* #ifndef __MORPHOS__ */ if( xfdMasterBase = OpenLibrary( "xfdmaster.library", 38 ) ) { if( xfdobj = (struct xfdBufferInfo *)xfdAllocObject(XFDOBJ_BUFFERINFO) ) { #endif /* #ifndef __MORPHOS__ */ xfdobj->xfdbi_SourceBufLen = old_length; xfdobj->xfdbi_SourceBuffer = old_buffer; xfdobj->xfdbi_Flags = XFDFB_RECOGEXTERN | XFDFB_RECOGTARGETLEN; xfdobj->xfdbi_PackerFlags = XFDPFB_RECOGLEN; #ifndef __MORPHOS__ if( IxfdMaster->xfdRecogBuffer( xfdobj ) ) { #else /* #ifndef __MORPHOS__ */ if( xfdRecogBuffer( xfdobj ) ) { #endif /* #ifndef __MORPHOS__ */ xfdobj->xfdbi_TargetBufMemType = MEMF_ANY; #ifndef __MORPHOS__ if( IxfdMaster->xfdDecrunchBuffer( xfdobj ) ) { #else /* #ifndef __MORPHOS__ */ if( xfdDecrunchBuffer( xfdobj ) ) { #endif /* #ifndef __MORPHOS__ */ *new_buffer = malloc( xfdobj->xfdbi_TargetBufSaveLen ); *new_length = xfdobj->xfdbi_TargetBufSaveLen; memcpy( *new_buffer, xfdobj->xfdbi_TargetBuffer, *new_length ); #ifndef __MORPHOS__ IExec->FreeMem( xfdobj->xfdbi_TargetBuffer,xfdobj->xfdbi_TargetBufLen ); #else /* #ifndef __MORPHOS__ */ FreeMem( xfdobj->xfdbi_TargetBuffer,xfdobj->xfdbi_TargetBufLen ); #endif /* #ifndef __MORPHOS__ */ } else { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "xfdmaster.library not able to decrunch %s file", xfdobj->xfdbi_PackerName ); return LIBSPECTRUM_ERROR_UNKNOWN; } } else { libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN, "xfdmaster.library does not recognise file" ); return LIBSPECTRUM_ERROR_UNKNOWN; } #ifndef __MORPHOS__ IxfdMaster->xfdFreeObject( xfdobj ); #else /* #ifndef __MORPHOS__ */ xfdFreeObject( xfdobj ); #endif /* #ifndef __MORPHOS__ */ } #ifndef __MORPHOS__ IExec->DropInterface( (struct Interface *)IxfdMaster ); } IExec->CloseLibrary( xfdMasterBase ); #else /* #ifndef __MORPHOS__ */ CloseLibrary( xfdMasterBase ); #endif /* #ifndef __MORPHOS__ */ } } break; #endif /* #ifdef AMIGA */ default: libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC, "unknown compressed type %d", type ); if( new_filename ) free( *new_filename ); return LIBSPECTRUM_ERROR_LOGIC; } return LIBSPECTRUM_ERROR_NONE; } /* Ensure there is room for `requested' characters after the current position `ptr' in `buffer'. If not, realloc() and update the pointers as necessary */ int libspectrum_make_room( libspectrum_byte **dest, size_t requested, libspectrum_byte **ptr, size_t *allocated ) { size_t current_length; current_length = *ptr - *dest; if( *allocated == 0 ) { (*allocated) = requested; *dest = (libspectrum_byte*)malloc( requested * sizeof(libspectrum_byte) ); if( *dest == NULL ) return 1; } else { /* If there's already enough room here, just return */ if( current_length + requested <= (*allocated) ) return 0; /* Make the new size the maximum of the new needed size and the old allocated size * 2 */ (*allocated) = current_length + requested > 2 * (*allocated) ? current_length + requested : 2 * (*allocated); *dest = (libspectrum_byte*) realloc( *dest, (*allocated) * sizeof( libspectrum_byte ) ); if( *dest == NULL ) return 1; } /* Update the secondary pointer to the block */ *ptr = *dest + current_length; return 0; } /* Read an LSB word from 'buffer' */ libspectrum_word libspectrum_read_word( const libspectrum_byte **buffer ) { libspectrum_word value; value = (*buffer)[0] + (*buffer)[1] * 0x100; (*buffer) += 2; return value; } /* Read an LSB dword from buffer */ libspectrum_dword libspectrum_read_dword( const libspectrum_byte **buffer ) { libspectrum_dword value; value = (*buffer)[0] + (*buffer)[1] * 0x100 + (*buffer)[2] * 0x10000 + (*buffer)[3] * 0x1000000 ; (*buffer) += 4; return value; } /* Write an (LSB) word to buffer */ int libspectrum_write_word( libspectrum_byte **buffer, libspectrum_word w ) { *(*buffer)++ = w & 0xff; *(*buffer)++ = w >> 8; return LIBSPECTRUM_ERROR_NONE; } /* Write an LSB dword to buffer */ int libspectrum_write_dword( libspectrum_byte **buffer, libspectrum_dword d ) { *(*buffer)++ = ( d & 0x000000ff ) ; *(*buffer)++ = ( d & 0x0000ff00 ) >> 8; *(*buffer)++ = ( d & 0x00ff0000 ) >> 16; *(*buffer)++ = ( d & 0xff000000 ) >> 24; return LIBSPECTRUM_ERROR_NONE; }