/* vMSX tap format Virtual MSX 1.x (being replaced by the MSX driver in mess) had it's on format for cassette recordings. The format is similar to .cas files; however several recordings could be stored in one file. Virtual MSX was never really popular; I haven't seen any of these files on the internet. However for completeness sake I've added this converter. It's read only; the MSX driver cannot read these files, but can read the .cas files (like most other MSX emulators). Further use and specially distribution of the .tap files is discouraged as Virtual MSX is obsolete (along with its private file formats). These files used the M$ mmio functions (similar to .aiff files). There is an INFO block which lists all files in the archive. The TAPE block contains the actual data. Original code in Virtual MSX: ftp://ftp.komkon.org/pub/EMUL8/MSX/Emulators/VMSX1SRC.zip It's in the file TAPE.C. RIFF format specs: http://www.saettler.com/RIFFMCI/riffmci.html This module converts the files to .cas files. Implemented are: dir - list all the files in the archive as "%title%-%type%.cas" getall - writes all the files to disk get - gets one file. If "getall" fails, due to characters in the filename your FS/OS doesn't support, use: imgtool get vmsx_tap monmsx.tap monmsx-binary.cas monmsx.cas for example. Sean Young */ #include "osdepend.h" #include "imgtoolx.h" #include "utils.h" typedef struct { char title[32]; char type[10]; char chunk[4]; } TAP_ENTRY; typedef struct { imgtool_image base; imgtool_stream *file_handle; int size; UINT8 *data; int count; TAP_ENTRY *entries; } TAP_IMAGE; typedef struct { imgtool_directory base; TAP_IMAGE *image; int index; } TAP_ITERATOR; static int vmsx_tap_image_init(const imgtool_module *mod, imgtool_stream *f, imgtool_image **outimg); static void vmsx_tap_image_exit(imgtool_image *img); static int vmsx_tap_image_beginenum(imgtool_image *img, imgtool_directory **outenum); static int vmsx_tap_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent); static void vmsx_tap_image_closeenum(imgtool_directory *enumeration); static int vmsx_tap_image_readfile(imgtool_image *img, const char *fname, imgtool_stream *destf); IMAGEMODULE( vmsx_tap, "Virtual MSX Tape archive", /* human readable name */ "tap", /* file extension */ NULL, /* crcfile */ NULL, /* crc system name */ NULL, /* eoln */ 0, /* flags */ vmsx_tap_image_init, /* init function */ vmsx_tap_image_exit, /* exit function */ NULL, /* info function */ vmsx_tap_image_beginenum, /* begin enumeration */ vmsx_tap_image_nextenum, /* enumerate next */ vmsx_tap_image_closeenum, /* close enumeration */ NULL, /* free space on image */ vmsx_tap_image_readfile, /* read file */ NULL,/* write file */ NULL,/* delete file */ NULL,/* create image */ NULL, NULL, NULL, NULL ) static const UINT8 CasHeader[8] = { 0x1F,0xA6,0xDE,0xBA,0xCC,0x13,0x7D,0x74 }; static int vmsx_tap_read_image (TAP_IMAGE *image) { unsigned char *p, *pmem; unsigned int pos, i, size; UINT32 offset; pmem = image->data; size = image->size; pos = 0; while ( (pos + 8) < size) { offset = *((UINT32*)(pmem + pos + 4)); offset = LITTLE_ENDIANIZE_INT32 (offset); if (strncmp ((char*)pmem + pos, "INFO", 4) ) { /* not this chunk, skip */ if (offset & 1) offset++; /* pad byte */ pos += offset + 8; continue; } /* found it */ pos += 8; p = pmem + pos; /* intregity check */ if ( (offset + pos) > size) return IMGTOOLERR_CORRUPTIMAGE; if (p[0] != 0 || p[1] != 1) return IMGTOOLERR_CORRUPTIMAGE; image->count = p[2] + p[3] * 256; p += 4; if (offset < (image->count * (32 + 10 + 4) + 4) ) return IMGTOOLERR_CORRUPTIMAGE; image->entries = (TAP_ENTRY*)malloc ( sizeof (TAP_ENTRY) * image->count); if (!image->entries) return IMGTOOLERR_OUTOFMEMORY; for (i=0;icount;i++) { /* this should really check the data */ strncpy (image->entries[i].title, (char*)p, 32); p += 32; strncpy (image->entries[i].type, (char*)p, 10); p += 10; memcpy (image->entries[i].chunk, (char*)p, 4); p += 4; } return 0; } return IMGTOOLERR_MODULENOTFOUND; } static int vmsx_tap_image_read_data (TAP_IMAGE *image, char *chunk, unsigned char **pcas, int *psize) { unsigned int caspos, pos = 0, found = 0, offset, size, tappos; UINT32 tapblock; unsigned char *p, *pmem; size = image->size; pmem = image->data; while ( (pos + 8) < size) { offset = *((UINT32*)(pmem + pos + 4)); offset = LITTLE_ENDIANIZE_INT32 (offset); if (memcmp (pmem + pos, "LIST", 4) ) { if (offset & 1) offset++; pos += offset + 8; continue; } if (memcmp (pmem + pos + 8, "TAPE", 4) ) return IMGTOOLERR_CORRUPTIMAGE; pos += 12; offset -= 8; if ( (pos + offset) > size) return IMGTOOLERR_CORRUPTIMAGE; pmem += pos; size = offset; found = 1; break; } if (!found) return IMGTOOLERR_CORRUPTIMAGE; /* OK we've got the right data chunk. Now we can start looking for the blocks */ pos = 0; while ( (pos + 8) < size) { offset = *((UINT32*)(pmem + pos + 4)); offset = LITTLE_ENDIANIZE_INT32 (offset); if (memcmp (pmem + pos, chunk, 4) ) { if (offset & 1) offset++; pos += offset + 8; continue; } pos += 8; caspos = tappos = 0; p = malloc (offset * 2); if (!p) return IMGTOOLERR_OUTOFMEMORY; while ( (tappos + 4) <= offset) { tapblock = *((UINT32*)(pmem + pos + tappos)); tappos += 4; tapblock = LITTLE_ENDIANIZE_INT32 (tapblock); if (tapblock == 0xffffffff) break; memcpy (p + caspos, CasHeader, 8); caspos += 8; memcpy (p + caspos, pmem + pos + tappos, tapblock); caspos += tapblock; tappos += tapblock; } *psize = caspos; *pcas = p; return 0; } return IMGTOOLERR_CORRUPTIMAGE; } static int vmsx_tap_image_init(const imgtool_module *mod, imgtool_stream *f, imgtool_image **outimg) { TAP_IMAGE *image; int rc; image = (TAP_IMAGE*)malloc (sizeof (TAP_IMAGE) ); if (!image) return IMGTOOLERR_OUTOFMEMORY; *outimg = (imgtool_image*)image; memset(image, 0, sizeof(TAP_IMAGE)); image->base.module = mod; image->size=stream_size(f); image->file_handle=f; image->data = (UINT8*) malloc(image->size); if (!image->data) { free(image); *outimg=NULL; return IMGTOOLERR_OUTOFMEMORY; } if (stream_read(f, image->data, image->size)!=image->size) { free(image); *outimg=NULL; return IMGTOOLERR_READERROR; } if ( (rc=vmsx_tap_read_image(image)) ) { if (image->entries) free(image->entries); free(image); *outimg=NULL; return rc; } return 0; } static void vmsx_tap_image_exit(imgtool_image *img) { TAP_IMAGE *image=(TAP_IMAGE*)img; stream_close(image->file_handle); free(image->entries); free(image->data); free(image); } static int vmsx_tap_image_beginenum(imgtool_image *img, imgtool_directory **outenum) { TAP_IMAGE *image=(TAP_IMAGE*)img; TAP_ITERATOR *iter; iter=*(TAP_ITERATOR**)outenum = (TAP_ITERATOR*) malloc(sizeof(TAP_ITERATOR)); if (!iter) return IMGTOOLERR_OUTOFMEMORY; iter->base.module = img->module; iter->image=image; iter->index = 0; return 0; } static int vmsx_tap_image_nextenum(imgtool_directory *enumeration, imgtool_dirent *ent) { TAP_ITERATOR *iter=(TAP_ITERATOR*)enumeration; unsigned char *p; int size, rc; ent->eof=iter->index>=iter->image->count; if (!ent->eof) { sprintf (ent->fname, "%s-%s.cas", iter->image->entries[iter->index].title, iter->image->entries[iter->index].type); size = 0; p = NULL; rc = vmsx_tap_image_read_data (iter->image, iter->image->entries[iter->index].chunk, &p, &size); ent->corrupt=rc; if (!rc) ent->filesize = size; iter->index++; if (p) free (p); } return 0; } static void vmsx_tap_image_closeenum(imgtool_directory *enumeration) { free (enumeration); } static TAP_ENTRY* vmsx_tap_image_findfile(TAP_IMAGE *image, const char *fname) { int i; char filename[64]; /* that's always enough! */ for (i=0; icount; i++) { sprintf (filename, "%s-%s.cas", image->entries[i].title, image->entries[i].type); if (!strcmp(fname, filename) ) return image->entries+i; } return NULL; } static int vmsx_tap_image_readfile(imgtool_image *img, const char *fname, imgtool_stream *destf) { TAP_IMAGE *image=(TAP_IMAGE*)img; TAP_ENTRY *entry; unsigned char* p; int size, rc; if ((entry=vmsx_tap_image_findfile(image, fname))==NULL ) return IMGTOOLERR_MODULENOTFOUND; rc = vmsx_tap_image_read_data (image, entry->chunk, &p, &size); if (!rc) { if (stream_write(destf, p, size)!=size) { free (p); return IMGTOOLERR_WRITEERROR; } } free (p); return rc; }