/***************************************************************************** * * * CQM UDOS file extraction for P8000 floppies * * Displays UDOS file directory and creates subdirectory for files * * Copyright (C) 2008: Matt Knoth, * * * * This program has been derived from LIBDSK 1.2.1 * * LIBDSK: General floppy and diskimage access library * * Copyright (C) 2001-2,2005 John Elliott * * Credits also to Per Ola Ingvarsson and Roger Plant * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; if not, write to the Free * * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * * MA 02111-1307, USA * * * *****************************************************************************/ #include #include #include #include #include #pragma warning(disable:4996) /* depricated stdlib calls: old fashioned guys at work ;-) */ #define DRV_QM_DEBUG 1 #define QM_HEADER_SIZE 133 #define DSK_ERR_OK (0) /* No error */ #define DSK_ERR_BADPTR (-1) /* Bad pointer */ #define DSK_ERR_NOTME (-5) /* Driver rejects disc */ #define DSK_ERR_NOMEM (-7) /* Null return from malloc */ /* Automatically generated CRC table */ /* polynomial: 0x104C11DB7, bit reverse algorithm */ const unsigned long crc32r_table[256] = { 0x00000000UL,0x77073096UL,0xEE0E612CUL,0x990951BAUL, 0x076DC419UL,0x706AF48FUL,0xE963A535UL,0x9E6495A3UL, 0x0EDB8832UL,0x79DCB8A4UL,0xE0D5E91EUL,0x97D2D988UL, 0x09B64C2BUL,0x7EB17CBDUL,0xE7B82D07UL,0x90BF1D91UL, 0x1DB71064UL,0x6AB020F2UL,0xF3B97148UL,0x84BE41DEUL, 0x1ADAD47DUL,0x6DDDE4EBUL,0xF4D4B551UL,0x83D385C7UL, 0x136C9856UL,0x646BA8C0UL,0xFD62F97AUL,0x8A65C9ECUL, 0x14015C4FUL,0x63066CD9UL,0xFA0F3D63UL,0x8D080DF5UL, 0x3B6E20C8UL,0x4C69105EUL,0xD56041E4UL,0xA2677172UL, 0x3C03E4D1UL,0x4B04D447UL,0xD20D85FDUL,0xA50AB56BUL, 0x35B5A8FAUL,0x42B2986CUL,0xDBBBC9D6UL,0xACBCF940UL, 0x32D86CE3UL,0x45DF5C75UL,0xDCD60DCFUL,0xABD13D59UL, 0x26D930ACUL,0x51DE003AUL,0xC8D75180UL,0xBFD06116UL, 0x21B4F4B5UL,0x56B3C423UL,0xCFBA9599UL,0xB8BDA50FUL, 0x2802B89EUL,0x5F058808UL,0xC60CD9B2UL,0xB10BE924UL, 0x2F6F7C87UL,0x58684C11UL,0xC1611DABUL,0xB6662D3DUL, 0x76DC4190UL,0x01DB7106UL,0x98D220BCUL,0xEFD5102AUL, 0x71B18589UL,0x06B6B51FUL,0x9FBFE4A5UL,0xE8B8D433UL, 0x7807C9A2UL,0x0F00F934UL,0x9609A88EUL,0xE10E9818UL, 0x7F6A0DBBUL,0x086D3D2DUL,0x91646C97UL,0xE6635C01UL, 0x6B6B51F4UL,0x1C6C6162UL,0x856530D8UL,0xF262004EUL, 0x6C0695EDUL,0x1B01A57BUL,0x8208F4C1UL,0xF50FC457UL, 0x65B0D9C6UL,0x12B7E950UL,0x8BBEB8EAUL,0xFCB9887CUL, 0x62DD1DDFUL,0x15DA2D49UL,0x8CD37CF3UL,0xFBD44C65UL, 0x4DB26158UL,0x3AB551CEUL,0xA3BC0074UL,0xD4BB30E2UL, 0x4ADFA541UL,0x3DD895D7UL,0xA4D1C46DUL,0xD3D6F4FBUL, 0x4369E96AUL,0x346ED9FCUL,0xAD678846UL,0xDA60B8D0UL, 0x44042D73UL,0x33031DE5UL,0xAA0A4C5FUL,0xDD0D7CC9UL, 0x5005713CUL,0x270241AAUL,0xBE0B1010UL,0xC90C2086UL, 0x5768B525UL,0x206F85B3UL,0xB966D409UL,0xCE61E49FUL, 0x5EDEF90EUL,0x29D9C998UL,0xB0D09822UL,0xC7D7A8B4UL, 0x59B33D17UL,0x2EB40D81UL,0xB7BD5C3BUL,0xC0BA6CADUL, 0xEDB88320UL,0x9ABFB3B6UL,0x03B6E20CUL,0x74B1D29AUL, 0xEAD54739UL,0x9DD277AFUL,0x04DB2615UL,0x73DC1683UL, 0xE3630B12UL,0x94643B84UL,0x0D6D6A3EUL,0x7A6A5AA8UL, 0xE40ECF0BUL,0x9309FF9DUL,0x0A00AE27UL,0x7D079EB1UL, 0xF00F9344UL,0x8708A3D2UL,0x1E01F268UL,0x6906C2FEUL, 0xF762575DUL,0x806567CBUL,0x196C3671UL,0x6E6B06E7UL, 0xFED41B76UL,0x89D32BE0UL,0x10DA7A5AUL,0x67DD4ACCUL, 0xF9B9DF6FUL,0x8EBEEFF9UL,0x17B7BE43UL,0x60B08ED5UL, 0xD6D6A3E8UL,0xA1D1937EUL,0x38D8C2C4UL,0x4FDFF252UL, 0xD1BB67F1UL,0xA6BC5767UL,0x3FB506DDUL,0x48B2364BUL, 0xD80D2BDAUL,0xAF0A1B4CUL,0x36034AF6UL,0x41047A60UL, 0xDF60EFC3UL,0xA867DF55UL,0x316E8EEFUL,0x4669BE79UL, 0xCB61B38CUL,0xBC66831AUL,0x256FD2A0UL,0x5268E236UL, 0xCC0C7795UL,0xBB0B4703UL,0x220216B9UL,0x5505262FUL, 0xC5BA3BBEUL,0xB2BD0B28UL,0x2BB45A92UL,0x5CB36A04UL, 0xC2D7FFA7UL,0xB5D0CF31UL,0x2CD99E8BUL,0x5BDEAE1DUL, 0x9B64C2B0UL,0xEC63F226UL,0x756AA39CUL,0x026D930AUL, 0x9C0906A9UL,0xEB0E363FUL,0x72076785UL,0x05005713UL, 0x95BF4A82UL,0xE2B87A14UL,0x7BB12BAEUL,0x0CB61B38UL, 0x92D28E9BUL,0xE5D5BE0DUL,0x7CDCEFB7UL,0x0BDBDF21UL, 0x86D3D2D4UL,0xF1D4E242UL,0x68DDB3F8UL,0x1FDA836EUL, 0x81BE16CDUL,0xF6B9265BUL,0x6FB077E1UL,0x18B74777UL, 0x88085AE6UL,0xFF0F6A70UL,0x66063BCAUL,0x11010B5CUL, 0x8F659EFFUL,0xF862AE69UL,0x616BFFD3UL,0x166CCF45UL, 0xA00AE278UL,0xD70DD2EEUL,0x4E048354UL,0x3903B3C2UL, 0xA7672661UL,0xD06016F7UL,0x4969474DUL,0x3E6E77DBUL, 0xAED16A4AUL,0xD9D65ADCUL,0x40DF0B66UL,0x37D83BF0UL, 0xA9BCAE53UL,0xDEBB9EC5UL,0x47B2CF7FUL,0x30B5FFE9UL, 0xBDBDF21CUL,0xCABAC28AUL,0x53B39330UL,0x24B4A3A6UL, 0xBAD03605UL,0xCDD70693UL,0x54DE5729UL,0x23D967BFUL, 0xB3667A2EUL,0xC4614AB8UL,0x5D681B02UL,0x2A6F2B94UL, 0xB40BBE37UL,0xC30C8EA1UL,0x5A05DF1BUL,0x2D02EF8DUL, }; typedef struct { void* qm_super; char* qm_filename; int qm_h_sector_size; /* Number of total sectors. Not valid if blind */ int qm_h_nbr_sectors; int qm_h_nbr_sec_per_track; int qm_h_nbr_heads; int qm_h_comment_len; /* Density - 1 means HD, 2 means QD */ int qm_h_density; /* Blind transfer or not. */ int qm_h_blind; int qm_h_used_tracks; int qm_h_total_tracks; /* Interleave */ int qm_h_interleave; /* Skew. Negative number for skew between sides */ int qm_h_skew; /* Sector number base. */ signed char qm_h_secbase; /* The crc read from the header */ unsigned long qm_h_crc; /* The crc calculated while the image is read */ unsigned long qm_calc_crc; unsigned int qm_image_offset; unsigned char* qm_image; /* Fake sector for READ ID command */ unsigned int qm_sector; } QM_DSK_DRIVER; static QM_DSK_DRIVER qm; static QM_DSK_DRIVER* qm_self = &qm; static int get_i16( char* buf, int pos ) { unsigned char low_byte; unsigned char high_byte; unsigned int outInt; low_byte = buf[pos++]; high_byte = buf[pos]; /* Signed, eh. Lets see. */ outInt = 0; if ( (signed char)high_byte < 0 ) { /* Set all to ones except for the lower 16 */ /* Should work if sizeof(int) >= 16 */ outInt = (-1) << 16; } outInt |= (high_byte << 8 ) | low_byte; return outInt; } static unsigned int get_u16( char* buf, int pos ) { return ((unsigned int)get_i16( buf, pos )) & 0xffff; } static unsigned long get_u32( char* buf, int pos ) { int i; unsigned long ret_val = 0; for ( i = 3; i >= 0; --i ) { ret_val <<= 8; ret_val |= ( (unsigned long)buf[pos+i] & 0xff ); } return ret_val; } static void drv_qm_update_crc( unsigned long* crc, unsigned char byte ) { /* Note that there is a bug in the CopyQM CRC calculation */ /* When indexing in this table, they shift the crc ^ data */ /* 2 bits up to address longwords, but they do that in an */ /* eight bit register, so that the top 2 bits are lost, */ /* thus the anding with 0x3f */ *crc = crc32r_table[(byte ^ (unsigned char)*crc) & 0x3f ] ^ (*crc >> 8); } int drv_qm_load_image( QM_DSK_DRIVER* qm_self, FILE* fp ) { unsigned char* image = 0; int errcond = DSK_ERR_OK; /* Write position in the memory image */ size_t curwritepos = 0; /* FIXME: Use the used tracks instead of the total tracks to detect */ /* that there is the correct amount of data in the image */ size_t image_size = (size_t)qm_self->qm_h_nbr_sec_per_track * (size_t)qm_self->qm_h_nbr_heads * (size_t)qm_self->qm_h_total_tracks * (size_t)qm_self->qm_h_sector_size; /* Set the position after the header and comment */ size_t res = fseek( fp, QM_HEADER_SIZE + qm_self->qm_h_comment_len, SEEK_SET ); if ( res < 0 ) return DSK_ERR_NOTME; /* Alloc memory for the image */ qm_self->qm_image = malloc( image_size ); if ( ! qm_self->qm_image ) return DSK_ERR_NOMEM; image = qm_self->qm_image; /* Start reading */ /* Note that it seems like each track starts a new block */ while ( curwritepos < image_size ) { /* Read the length */ char lengthBuf[2]; res = fread( lengthBuf, 2, 1, fp ); if ( res != 1 ) { if ( feof( fp ) ) { /* End of file - fill with f6 - do not update CRC for these */ memset( image + curwritepos, 0xf6, image_size - curwritepos ); curwritepos += image_size - curwritepos; } else { return DSK_ERR_NOTME; } } else { int length = get_i16( lengthBuf, 0 ); if ( length < 0 ) { /* Negative number - next byte is repeated (-length) times */ int c = fgetc( fp ); if ( c == EOF ) return DSK_ERR_NOTME; /* Copy the byte into memory and update the offset */ memset( image + curwritepos, c, -length ); curwritepos -= length; /* Update CRC */ for( ; length != 0; ++length ) { drv_qm_update_crc( &qm_self->qm_calc_crc, (unsigned char)c ); } } else { if ( length != 0 ) { /* Positive number - length different characters */ res = fread( image + curwritepos, length, 1, fp ); /* Update CRC (and write pos) */ while ( length-- ) { drv_qm_update_crc( &qm_self->qm_calc_crc, image[curwritepos++] ); } if ( res != 1 ) return DSK_ERR_NOTME; } } } } #ifdef DRV_QM_DEBUG fprintf( stderr, "drv_qm_load_image - crc from header = 0x%08lx, " "calc = 0x%08lx\n", qm_self->qm_h_crc, qm_self->qm_calc_crc ); #endif /* Compare the CRCs */ /* The CRC is zero on old images so it cannot be checked then */ if ( qm_self->qm_h_crc ) { if ( qm_self->qm_h_crc != qm_self->qm_calc_crc ) { return DSK_ERR_NOTME; } } //#ifdef DRV_QM_DEBUG // /* Write image to file for testing */ // if(1) { // FILE* tmpFile = fopen("image.dd", "wb" ); // fwrite( image, image_size, 1, tmpFile ); // fclose( tmpFile ); // fprintf(stderr,"INFO: wrote image file image.dd - size %d\n",image_size); // } //#endif return errcond; } int drv_qm_load_header( QM_DSK_DRIVER* qm_self, char* header ) { int i; /* Check the header checksum */ unsigned char chksum = 0; for ( i = 0; i < QM_HEADER_SIZE; ++i ) { chksum += (unsigned char)(header[i]); } if ( chksum != 0 ) { #ifdef DRV_QM_DEBUG fprintf( stderr, "qm: header checksum error\n"); #endif return DSK_ERR_NOTME; } else { #ifdef DRV_QM_DEBUG fprintf( stderr, "qm: header checksum is ok!\n"); #endif } if ( header[0] != 'C' || header[1] != 'Q' ) { #ifdef DRV_QM_DEBUG fprintf( stderr, "qm: First two chars are %c%c\n", header[0], header[1] ); #endif return DSK_ERR_NOTME; } /* I'm guessing sector size is at 3. Expandqm thinks 7 */ qm_self->qm_h_sector_size = get_u16( header, 0x03 ); /* Number of sectors 0x0B-0x0C, strange number for non-blind, often 116 */ qm_self->qm_h_nbr_sectors = get_u16( header, 0x0b ); /* Number of sectors per track */ qm_self->qm_h_nbr_sec_per_track = get_u16( header, 0x10 ); /* Number of heads */ qm_self->qm_h_nbr_heads = get_u16( header, 0x12 ); /* Blind or not */ qm_self->qm_h_blind = header[0x58]; /* Density - 0 is DD, 1 means HD */ qm_self->qm_h_density = header[0x59]; /* Number of used tracks */ qm_self->qm_h_used_tracks = header[0x5a]; /* Number of total tracks */ qm_self->qm_h_total_tracks = header[0x5b]; /* CRC 0x5c - 0x5f */ qm_self->qm_h_crc = get_u32( header, 0x5c ); /* Length of comment */ qm_self->qm_h_comment_len = get_u16( header, 0x6f ); /* 0x71 is first sector number - 1 */ qm_self->qm_h_secbase = (signed char)(header[0x71]); /* 0x74 is interleave, I think. Normally 1, but 0 for old copyqm */ qm_self->qm_h_interleave = header[0x74]; /* 0x75 is skew. Normally 0. Negative number for alternating sides */ qm_self->qm_h_skew = header[0x75]; #ifdef DRV_QM_DEBUG fprintf( stderr, "qm: sector size is %d\n", qm_self->qm_h_sector_size); fprintf( stderr, "qm: nbr sectors %d\n", qm_self->qm_h_nbr_sectors ); fprintf( stderr, "qm: nbr sectors/track %d\n", qm_self->qm_h_nbr_sec_per_track); fprintf( stderr, "qm: nbr heads %d\n", qm_self->qm_h_nbr_heads ); fprintf( stderr, "qm: secbase %d\n", qm_self->qm_h_secbase ); fprintf( stderr, "qm: density %d\n", qm_self->qm_h_density ); fprintf( stderr, "qm: used tracks %d\n", qm_self->qm_h_used_tracks ); fprintf( stderr, "qm: CRC 0x%08lx\n", qm_self->qm_h_crc ); fprintf( stderr, "qm: interleave %d\n", qm_self->qm_h_interleave ); fprintf( stderr, "qm: skew %d\n", qm_self->qm_h_skew ); fprintf( stderr, "qm: description \"%s\"\n" , header + 0x1c ); #endif /* Fix the interleave value for old versions of CopyQM */ if ( qm_self->qm_h_interleave == 0 ) { qm_self->qm_h_interleave = 1; } return DSK_ERR_OK; } int drv_qm_get_read_ptr(QM_DSK_DRIVER *qm_self, unsigned char **buf_ptr, int cylinder, int head, int sector) { long offset; if (!buf_ptr || !qm_self) return DSK_ERR_BADPTR; offset = (cylinder * qm_self->qm_h_nbr_heads) + head; /* Drive track */ offset *= qm_self->qm_h_nbr_sec_per_track; offset += (sector - ((qm_self->qm_h_secbase + 1) & 0xFF)); offset *= qm_self->qm_h_sector_size; *buf_ptr = qm_self->qm_image + offset; return DSK_ERR_OK; } void drv_qm_dump_sector_hex(unsigned char* buf,int sz) { int m,n; for (n = 0; n < sz; n = ((n / 16)+1)*16) { int base = (n/16)*16; int c = 0; printf("%04x: ", n); for (m = 0; m < (n%16); m++) { putchar(' '); putchar(' '); if ((c & 3) == 3) putchar(' '); c++; } for (m = (n % 16); m < 16; m++) { printf("%02x", buf[base+m]); if ((c & 3) == 3) putchar(' '); c++; if ((n + m + 1) >= sz) break; } for (; c < 16; c++) { putchar(' '); putchar(' '); if ((c & 3) == 3) putchar(' '); } printf(" *"); for (m = 0; m < (n%16); m++) putchar(' '); for (m = (n % 16); m < 16; m++) { if (isprint(buf[base+m])) putchar(buf[base+m]); else putchar('.'); if ((n + m + 1) >= sz) break; } putchar('*'); putchar('\n'); } } void dump_udos_file(unsigned char* descriptor,unsigned char* file_name) { unsigned char* dataptr; unsigned char* ptrs; unsigned char buf[48]; FILE* ofp; int i,len,lastlen,ren; strncpy(buf,file_name+1, 0x1f & *file_name); buf[0x1f & *file_name]=0; ren=0; while((ofp=fopen(buf,"rb"))!=NULL) { // Problem: UDOS has case sensitive names. DOS doesn't. Thanks Bill! fclose(ofp); sprintf(&buf[0x1f & *file_name],"_renamed%d",ren); ren++; } if((ofp=fopen(buf,"wb"))==NULL) { fprintf(stderr,"ERROR: unable to open file %s\n", buf); return; } len=(descriptor[0x10]<<8)|descriptor[0x0f]; lastlen=((descriptor[0x17]<<8)|descriptor[0x16]); drv_qm_get_read_ptr( qm_self, &ptrs, descriptor[0x81], 0, 1+ (descriptor[0x80] & 0x1f)); // checkout POINTERS for(i=2; !((i>ptrs[0xfa]) && (ptrs[0xfe]==0xff) && (ptrs[0xff]==0xff)); i+=2) { if((i>=0xf9) && (ptrs[0xfe]!=0xff) && (ptrs[0xff]!=0xff)) { drv_qm_get_read_ptr( qm_self, &ptrs, ptrs[0xff], 0, 1+ (ptrs[0xfe] & 0x1f)); // checkout next POINTERS block i=0; } drv_qm_get_read_ptr( qm_self, &dataptr, ptrs[i+1], 0, 1+ (ptrs[i] & 0x1f)); // checkout this data block, order is swapped in P8000! if((i==ptrs[0xfa]) && (ptrs[0xfe]==0xff) && (ptrs[0xff]==0xff)) fwrite(dataptr, 1, lastlen, ofp); else fwrite(dataptr, 1, len, ofp); } fclose(ofp); } void extract_udos_directory_block(FILE* fp, unsigned char* rd_buf, int extract, int blk) { int i, j, n, d; unsigned char* descriptor; unsigned char buf[32]; for(i=0; (i<512) && (rd_buf[i]!=0xff); i+=n+3) { n=rd_buf[i] & 0x7f; // get name length d=rd_buf[i+n+1] | (rd_buf[i+n+2]<<8); // get descriptor block for(j=1; j<=n; j++) // print name fputc(rd_buf[i+j],fp); for(; j<35; j++) // fill rest fputc(' ',fp); drv_qm_get_read_ptr( qm_self, &descriptor, d>>8, 0, 1+ (d & 0x1f)); // checkout DESCRIPTOR fprintf(fp,"%04xh ",d); // descriptor address fprintf(fp,"%04xh ",(descriptor[0x81]<<8)|descriptor[0x80]); fprintf(fp,"%6d ",(((descriptor[0x10]<<8)|descriptor[0x0f]) * // blocklen ((descriptor[0x0e]<<8)|descriptor[0x0d])) - // number of blocks (((descriptor[0x10]<<8)|descriptor[0x0f]) - // blocklen ((descriptor[0x17]<<8)|descriptor[0x16]))); // bytes in last block fprintf(fp,"%c ",(rd_buf[i] & 0x80)? 'S':'.'); // secret attribute if(descriptor[0x0c] & 0x10) fprintf(fp,"B "); // file type if(descriptor[0x0c] & 0x20) fprintf(fp,"A "); if(descriptor[0x0c] & 0x40) fprintf(fp,"D "); if(descriptor[0x0c] & 0x80) fprintf(fp,"P "); fprintf(fp,"%3d %4d %4d ",(descriptor[0xe]<<8)|descriptor[0xd], (descriptor[0x10]<<8)|descriptor[0x0f],(descriptor[0x17]<<8)|descriptor[0x16]); fprintf(fp,"%c%c%c%c%c%c %c%c%c%c%c%c ",descriptor[0x18],descriptor[0x19],descriptor[0x1a],descriptor[0x1b],descriptor[0x1c],descriptor[0x1d], descriptor[0x20],descriptor[0x21],descriptor[0x22],descriptor[0x23],descriptor[0x24],descriptor[0x25]); strcpy(buf,"......"); if(descriptor[0x13] & 0x04) buf[ 5]='W'; if(descriptor[0x13] & 0x08) buf[ 4]='E'; if(descriptor[0x13] & 0x10) buf[ 3]='L'; if(descriptor[0x13] & 0x20) buf[ 2]='S'; if(descriptor[0x13] & 0x40) buf[ 1]='R'; if(descriptor[0x13] & 0x80) buf[ 0]='F'; fprintf(fp,"%s ",buf); fprintf(fp,"%04x %04x %04x " ,(descriptor[0x29]<<8)|descriptor[0x28], (descriptor[0x7b]<<8)|descriptor[0x7a],(descriptor[0x7d]<<8)|descriptor[0x7c]); fprintf(fp,"%5d %4d" ,(descriptor[0x2b]<<8)|descriptor[0x2a], (descriptor[0x7f]<<8)|descriptor[0x7e]); fprintf(fp,"\n"); if((i||blk) && extract) dump_udos_file(descriptor,&rd_buf[i]); // dump out this file - except DIRECTORY } } void extract_udos_directory(FILE* fp, unsigned char* rd_buf, int extract) { int i,n; unsigned char* descriptor, *ptrs, *dptr; fprintf(fp,"FILENAME %30s%7s%9s%5s%5s%7s%8s%7s%7s%8s","DESCR","PNTRS","SIZE","ATTR","TYP","RECS","BLKLEN","LASTR","CREAT","MODFD"); fprintf(fp,"%8s%9s%7s%8s%7s%8s\n","PROP","STARTAD","LOWAD","HIGHAD","LN1SEG","STKSZ"); drv_qm_get_read_ptr( qm_self, &descriptor, rd_buf[11], 0, 1+ (rd_buf[10] & 0x1f)); // checkout DESCRIPTOR of DIRECTORY drv_qm_get_read_ptr( qm_self, &ptrs, descriptor[0x81], 0, 1+ (descriptor[0x80] & 0x1f)); // checkout pointers of DIRECTORY n=0; // block counter - prevents binary dump of file DIRECTORY for(i=2; !((i>=ptrs[0xfa]) && (ptrs[0xfe]==0xff) && (ptrs[0xff]==0xff)); i+=2) { // step through all ptr blocks if((i>=ptrs[0xfa]) && (ptrs[0xfe]!=0xff) && (ptrs[0xff]!=0xff)) { // go to next ptr block drv_qm_get_read_ptr( qm_self, &ptrs, ptrs[0xff], 0, 1+ (ptrs[0xfe] & 0x1f)); // checkout next ptrs block i=0; n++; } drv_qm_get_read_ptr( qm_self, &dptr, ptrs[i+1], 0, 1+ (ptrs[i] & 0x1f)); // checkout next DIRECTORY data block extract_udos_directory_block(fp, dptr, extract, n); // extract this DIRECTORY block } } int main(int argc, char** argv) { FILE* fp; char header[QM_HEADER_SIZE]; size_t res; int errcond = 0; unsigned char* rd_buf; unsigned char* udos_directory; char extract_dir[256]; if(argc<2) { fprintf(stderr,"ERROR: missing file argument - abort\n"); fprintf(stderr,"USAGE: %s \n",argv[0]); fprintf(stderr,"INFO: %s - derived from LibDsk-1.2.1 - GNU Library General Public License applies\n",argv[0]); fprintf(stderr,"INFO: %s - read CopyQM image, reports drive statistics and extracts UDOS files \n\n",argv[0]); return 0; } /* Zero some stuff */ qm_self->qm_image = 0; qm_self->qm_filename = 0; /* Open file. Read only for now */ fp = fopen(argv[1], "rb"); if ( ! fp ) { fprintf(stderr,"ERROR: cannot open file %s - abort\n",argv[1]); return DSK_ERR_BADPTR; } /* Load the header */ res = fread( header, QM_HEADER_SIZE, 1, fp ); if ( res != 1 ) { fprintf(stderr,"ERROR while reading header - abort\n"); return DSK_ERR_NOTME; } /* Load the header */ errcond = drv_qm_load_header( qm_self, header ); /* If there's a comment, allocate a temporary buffer for it and load it. */ if (errcond == DSK_ERR_OK && qm_self->qm_h_comment_len) { char *comment_buf = malloc(1 + qm_self->qm_h_comment_len); /* If malloc fails, ignore it - comments aren't essential */ if (comment_buf) { int res = fseek( fp, QM_HEADER_SIZE, SEEK_SET ); if ( res < 0 ) errcond = DSK_ERR_NOTME; else { res = fread(comment_buf, 1, qm_self->qm_h_comment_len, fp); if ( res < qm_self->qm_h_comment_len) { fprintf(stderr,"ERROR: comment length exceeded image - abort\n"); return DSK_ERR_NOTME; } else { comment_buf[qm_self->qm_h_comment_len] = 0; /*dsk_set_comment(&qm_self->qm_super, comment_buf); In case we want to store the comment */ fprintf(stderr,"qm: Disk Comment: %s\n",comment_buf); free(comment_buf); } } } } if ( errcond != DSK_ERR_OK ) { fprintf( stderr, "drv_qm_load_header returned %d\n", (int)errcond ); return(errcond); } errcond = drv_qm_load_image( qm_self, fp ); if ( errcond != DSK_ERR_OK ) { fprintf( stderr, "drv_qm_load_image returned %d\n", (int)errcond ); return(errcond); } // check if P8000 UDOS floppy at all drv_qm_get_read_ptr( qm_self, &rd_buf, 22, 0, 1 ); // checkout DIRECTORY DESCRIPTOR if(!((rd_buf[0x0c] & 0x40) && (rd_buf[0x0f]==0x00) && (rd_buf[0x10]==0x01) && // type D, record length 256 (rd_buf[0x06]==rd_buf[0x08]) && (rd_buf[0x07]==rd_buf[0x09]))) { // Dptr == first data record fprintf(stderr,"ERROR: not a UDOS disk! - no directory found\n"); return(DSK_ERR_NOTME); } sprintf(extract_dir,"FLOPPY_crc%08lx",qm_self->qm_calc_crc); // set extract directory fprintf(stderr,"assigning extract directory %s\n",extract_dir); mkdir(extract_dir,0777); chdir(extract_dir); drv_qm_get_read_ptr( qm_self, &rd_buf, rd_buf[0x07], 0, 1+ (rd_buf[0x06] & 0x1f)); // checkout DIRECTORY if((rd_buf[0]==0x89) && ~strncmp(&rd_buf[1],"DIRECTORY",9)) { FILE* fop; if((fop=fopen("DIRECTORY","wb"))==NULL) { fprintf(stderr,"ERROR: unable to write DIRECTORY file - abort\n"); return(DSK_ERR_BADPTR); } udos_directory = rd_buf; extract_udos_directory(stderr,rd_buf,1); extract_udos_directory(fop ,rd_buf,0); fclose(fop); } else { udos_directory=NULL; fprintf(stderr,"ERROR: not a UDOS disk! - no directory found\n"); return(DSK_ERR_NOTME); } drv_qm_get_read_ptr( qm_self, &rd_buf, 0, 0, 1 ); // checkout MBR if(!strncmp(&rd_buf[0x80],"P8000SYS",8)) { FILE* fop; fprintf(stderr,"P8000SYS disk found - extract MasterBootRecord\n"); if((fop=fopen("MBR.dat","wb"))==NULL) { fprintf(stderr,"ERROR: unable to write MBR.dat - abort\n"); return(DSK_ERR_BADPTR); } fwrite(rd_buf,1,256,fop); fclose(fop); } /* Close the file */ if ( fp ) { fclose( fp ); } return DSK_ERR_OK; }