Annotation of hatari/src/msa.c, revision 1.1.1.2

1.1       root        1: /*
                      2:   Hatari
                      3: 
                      4:   MSA Disc support
                      5: */
                      6: 
                      7: #include "main.h"
                      8: #include "file.h"
                      9: #include "floppy.h"
                     10: #include "memAlloc.h"
                     11: #include "misc.h"
                     12: #include "stMemory.h"
                     13: 
                     14: #define SAVE_TO_MSA_IMAGES
                     15: 
                     16: /*
                     17:     .MSA FILE FORMAT
                     18:   --================------------------------------------------------------------
                     19: 
                     20:   For those interested, an MSA file is made up as follows:
                     21: 
                     22:   Header:
                     23: 
                     24:   Word  ID marker, should be $0E0F
                     25:   Word  Sectors per track
                     26:   Word  Sides (0 or 1; add 1 to this to get correct number of sides)
                     27:   Word  Starting track (0-based)
                     28:   Word  Ending track (0-based)
                     29: 
                     30:   Individual tracks follow the header in alternating side order, e.g. a double
                     31:   sided disk is stored as:
                     32: 
                     33:   TRACK 0, SIDE 0
                     34:   TRACK 0, SIDE 1
                     35:   TRACK 1, SIDE 0
                     36:   TRACK 1, SIDE 1
                     37:   TRACK 2, SIDE 0
                     38:   TRACK 2, SIDE 1
                     39: 
                     40:   ...and so on. Track blocks are made up as follows:
                     41: 
                     42:   Word  Data length
                     43:   Bytes  Data
                     44: 
                     45:   If the data length is equal to 512 x the sectors per track value, it is an
                     46:   uncompressed track and you can merely copy the data to the appropriate track
                     47:   of the disk. However, if the data length value is less than 512 x the sectors
                     48:   per track value it is a compressed track.
                     49: 
                     50:   Compressed tracks use simple a Run Length Encoding (RLE) compression method.
                     51:   You can directly copy any data bytes until you find an $E5 byte. This signals
                     52:   a compressed run, and is made up as follows:
                     53: 
                     54:   Byte  Marker - $E5
                     55:   Byte  Data byte
                     56:   Word  Run length
                     57: 
                     58:   So, if MSA found six $AA bytes in a row it would encode it as:
                     59: 
                     60:   $E5AA0006
                     61: 
                     62:   What happens if there's an actual $E5 byte on the disk? Well, logically
                     63:   enough, it is encoded as:
                     64: 
                     65:   $E5E50001
                     66: 
                     67:   This is obviously bad news if a disk consists of lots of data like
                     68:   $E500E500E500E500... but if MSA makes a track bigger when attempting to
                     69:   compress it, it just stores the uncompressed version instead.
                     70: 
                     71:   MSA only compresses runs of at least 4 identical bytes (after all, it would be
                     72:   wasteful to store 4 bytes for a run of only 3 identical bytes!). There is one
                     73:   exception to this rule: if a run of 2 or 3 $E5 bytes is found, that is stored
                     74:   appropriately enough as a run. Again, it would be wasteful to store 4 bytes
                     75:   for every single $E5 byte.
                     76: 
                     77:   The hacked release of MSA that enables the user to turn off compression
                     78:   completely simply stops MSA from trying this compression and produces MSA
                     79:   images that are completely uncompressed. This is okay because it is possible
                     80:   for MSA to produce such an image anyway, and such images are therefore 100%
                     81:   compatible with normal MSA versions (and MSA-to-ST of course).
                     82: */
                     83: 
                     84: typedef struct {
                     85:   short int ID;                 /* Word  ID marker, should be $0E0F */
                     86:   short int SectorsPerTrack;    /* Word  Sectors per track */
                     87:   short int Sides;              /* Word  Sides (0 or 1; add 1 to this to get correct number of sides) */
                     88:   short int StartingTrack;      /* Word  Starting track (0-based) */
                     89:   short int EndingTrack;        /* Word  Ending track (0-based) */
                     90: } MSAHEADERSTRUCT;
                     91: 
                     92: #define MSA_WORKSPACE_SIZE  (1024*1024)  /* Size of workspace to use when saving MSA files */
                     93: 
                     94: 
1.1.1.2 ! root       95: /*-----------------------------------------------------------------------*/
1.1       root       96: /*
                     97:   Uncompress .MSA data into buffer
                     98: */
                     99: int MSA_UnCompress(unsigned char *pMSAFile,unsigned char *pBuffer)
                    100: {
                    101:   MSAHEADERSTRUCT *pMSAHeader;
                    102:   unsigned char *pMSAImageBuffer,*pImageBuffer;
                    103:   unsigned char Byte,Data;
                    104:   int ImageSize = 0;
                    105:   int i,Track,Side,DataLength,NumBytesUnCompressed,RunLength;
                    106: 
1.1.1.2 ! root      107:   /* Is an '.msa' file?? Check header */
1.1       root      108:   pMSAHeader = (MSAHEADERSTRUCT *)pMSAFile;
                    109:   if (pMSAHeader->ID==STMemory_Swap68000Int(0x0E0F)) {
1.1.1.2 ! root      110:     /* First swap 'header' words around to PC format - easier later on */
1.1       root      111:     pMSAHeader->SectorsPerTrack = STMemory_Swap68000Int(pMSAHeader->SectorsPerTrack);
                    112:     pMSAHeader->Sides = STMemory_Swap68000Int(pMSAHeader->Sides);
                    113:     pMSAHeader->StartingTrack = STMemory_Swap68000Int(pMSAHeader->StartingTrack);
                    114:     pMSAHeader->EndingTrack = STMemory_Swap68000Int(pMSAHeader->EndingTrack);
                    115: 
1.1.1.2 ! root      116:     /* Set pointers */
1.1       root      117:     pImageBuffer = (unsigned char *)pBuffer;
                    118:     pMSAImageBuffer = (unsigned char *)((unsigned long)pMSAFile+sizeof(MSAHEADERSTRUCT));
                    119: 
1.1.1.2 ! root      120:     /* Uncompress to memory as '.ST' disc image - NOTE: assumes 512 bytes per sector (use NUMBYTESPERSECTOR define)!!! */
1.1       root      121:     for(Track=pMSAHeader->StartingTrack; Track<(pMSAHeader->EndingTrack+1); Track++) {
                    122:       for(Side=0; Side<(pMSAHeader->Sides+1); Side++) {
1.1.1.2 ! root      123:         /* Uncompress MSA Track, first check if is not compressed */
1.1       root      124:         DataLength = STMemory_Swap68000Int(*(short int *)pMSAImageBuffer);
                    125:         pMSAImageBuffer += sizeof(short int);
                    126:         if (DataLength==(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)) {
1.1.1.2 ! root      127:           /* No compression on track, simply copy and continue */
1.1       root      128:           memcpy(pImageBuffer,pMSAImageBuffer,NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack);
                    129:           pImageBuffer += NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack;
                    130:           pMSAImageBuffer += DataLength;
                    131:         }
                    132:         else {
1.1.1.2 ! root      133:           /* Uncompress track */
1.1       root      134:           NumBytesUnCompressed = 0;
                    135:           while(NumBytesUnCompressed<(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)) {
                    136:             Byte = *pMSAImageBuffer++;
1.1.1.2 ! root      137:             if (Byte!=0xE5) {                           /* Compressed header?? */
        !           138:               *pImageBuffer++ = Byte;                   /* No, just copy byte */
1.1       root      139:               NumBytesUnCompressed++;
                    140:             }
                    141:             else {
1.1.1.2 ! root      142:               Data = *pMSAImageBuffer++;                /* Byte to copy */
        !           143:               RunLength = STMemory_Swap68000Int(*(short int *)pMSAImageBuffer);  /* For length */
        !           144:               /* Limit length to size of track, incorrect images may overflow */
1.1       root      145:               if ( (RunLength+NumBytesUnCompressed)>(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack) )
                    146:                 RunLength = (NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)-NumBytesUnCompressed;
                    147:               pMSAImageBuffer += sizeof(short int);
                    148:               for(i=0; i<RunLength; i++)
1.1.1.2 ! root      149:                 *pImageBuffer++ = Data;                 /* Copy byte */
1.1       root      150:               NumBytesUnCompressed += RunLength;
                    151:             }
                    152:           }
                    153:         }          
                    154:       }
                    155:     }
                    156: 
1.1.1.2 ! root      157:     /* Set size of loaded image */
1.1       root      158:     ImageSize = (unsigned long)pImageBuffer-(unsigned long)pBuffer;
                    159:   }
                    160: 
1.1.1.2 ! root      161:   /* Return number of bytes loaded, '0' if failed */
1.1       root      162:   return(ImageSize);
                    163: }
                    164: 
1.1.1.2 ! root      165: 
        !           166: /*-----------------------------------------------------------------------*/
1.1       root      167: /*
                    168:   Uncompress .MSA file into memory, return number of bytes loaded
                    169: */
                    170: int MSA_ReadDisc(char *pszFileName,unsigned char *pBuffer)
                    171: {
                    172:   unsigned char *pMSAFile;
                    173:   int ImageSize = 0;
                    174: 
1.1.1.2 ! root      175:   /* Read in file */
1.1       root      176:   pMSAFile = (unsigned char *)File_Read(pszFileName,NULL,NULL,NULL);
                    177:   if (pMSAFile) {
1.1.1.2 ! root      178:     /* Uncompress into disc buffer */
1.1       root      179:     ImageSize = MSA_UnCompress(pMSAFile,pBuffer);
                    180: 
1.1.1.2 ! root      181:     /* Free MSA file we loaded */
1.1       root      182:     Memory_Free(pMSAFile);
                    183:   }
                    184: 
1.1.1.2 ! root      185:   /* Return number of bytes loaded, '0' if failed */
1.1       root      186:   return(ImageSize);
                    187: }
                    188: 
1.1.1.2 ! root      189: 
        !           190: /*-----------------------------------------------------------------------*/
1.1       root      191: /*
                    192:   Return number of bytes of the same byte in the passed buffer
                    193:   If we return '0' this means no run(or end of buffer)
                    194: */
                    195: int MSA_FindRunOfBytes(unsigned char *pBuffer,int nBytesInBuffer)
                    196: {
                    197:   unsigned char ScannedByte;
                    198:   int nTotalRun;
                    199:   int i;
                    200: 
1.1.1.2 ! root      201:   /* Do we enough for a run? */
1.1       root      202:   if (nBytesInBuffer<2)
                    203:     return(0);
                    204: 
1.1.1.2 ! root      205:   /* OK, scan for run */
1.1       root      206:   nTotalRun = 1;
                    207:   ScannedByte = *pBuffer++;
1.1.1.2 ! root      208:   /* Is this the marker? If so, this is a run of one */
1.1       root      209:   if (ScannedByte!=0xE5) {
                    210:     for(i=1; i<nBytesInBuffer; i++) {
                    211:       if (*pBuffer++==ScannedByte)
                    212:         nTotalRun++;
                    213:       else
                    214:         break;
                    215:     }
1.1.1.2 ! root      216:     /* Was this enough of a run to make a difference? */
1.1       root      217:     if (nTotalRun<4)
1.1.1.2 ! root      218:       nTotalRun = 0;    /* Just store a bytes */
1.1       root      219:   }
                    220: 
                    221:   return(nTotalRun);
                    222: }
                    223: 
1.1.1.2 ! root      224: 
        !           225: /*-----------------------------------------------------------------------*/
1.1       root      226: /*
                    227:   Save compressed .MSA file from memory buffer. Returns TRUE is all OK
                    228: */
                    229: BOOL MSA_WriteDisc(char *pszFileName,unsigned char *pBuffer,int ImageSize)
                    230: {
                    231: #ifdef SAVE_TO_MSA_IMAGES
                    232: 
                    233:   MSAHEADERSTRUCT *pMSAHeader;
                    234:   unsigned short int *pMSADataLength;
                    235:   unsigned char *pMSAImageBuffer, *pMSABuffer, *pImageBuffer;
                    236:   unsigned short int nSectorsPerTrack, nSides, nCompressedBytes, nBytesPerTrack;
                    237:   BOOL nRet;
                    238:   int nTracks,nBytesToGo,nBytesRun;
                    239:   int Track,Side;
                    240: 
1.1.1.2 ! root      241:   /* Allocate workspace for compressed image */
1.1       root      242:   pMSAImageBuffer = (unsigned char *)Memory_Alloc(MSA_WORKSPACE_SIZE);
                    243: 
1.1.1.2 ! root      244:   /* Store header */
1.1       root      245:   pMSAHeader = (MSAHEADERSTRUCT *)pMSAImageBuffer;
                    246:   pMSAHeader->ID = STMemory_Swap68000Int(0x0E0F);
                    247:   Floppy_FindDiscDetails(pBuffer,ImageSize,&nSectorsPerTrack,&nSides);
                    248:   pMSAHeader->SectorsPerTrack = STMemory_Swap68000Int(nSectorsPerTrack);
                    249:   pMSAHeader->Sides = STMemory_Swap68000Int(nSides-1);
                    250:   pMSAHeader->StartingTrack = STMemory_Swap68000Int(0);
                    251:   nTracks = ((ImageSize / NUMBYTESPERSECTOR) / nSectorsPerTrack) / nSides;
                    252:   pMSAHeader->EndingTrack = STMemory_Swap68000Int(nTracks-1);
                    253: 
1.1.1.2 ! root      254:   /* Compress image */
1.1       root      255:   pMSABuffer = pMSAImageBuffer + sizeof(MSAHEADERSTRUCT);
                    256:   for(Track=0; Track<nTracks; Track++) {
                    257:     for(Side=0; Side<nSides; Side++) {
1.1.1.2 ! root      258:       /* Get track data pointer */
1.1       root      259:       nBytesPerTrack = NUMBYTESPERSECTOR*nSectorsPerTrack;
                    260:       pImageBuffer = pBuffer + (nBytesPerTrack*Side) + ((nBytesPerTrack*nSides)*Track);
                    261: 
1.1.1.2 ! root      262:       /* Skip data length (fill in later) */
1.1       root      263:       pMSADataLength = (unsigned short int *)pMSABuffer;
                    264:       pMSABuffer += sizeof(short int);
                    265:       
1.1.1.2 ! root      266:       /* Compress track */
1.1       root      267:       nBytesToGo = NUMBYTESPERSECTOR * nSectorsPerTrack;
                    268:       nCompressedBytes = 0;
                    269:       while(nBytesToGo>0) {
                    270:         nBytesRun = MSA_FindRunOfBytes(pImageBuffer,nBytesToGo);
                    271:         if (nBytesRun==0) {
1.1.1.2 ! root      272:           /* Just copy byte */
1.1       root      273:           *pMSABuffer++ = *pImageBuffer++;
                    274:           nCompressedBytes++;
                    275:           nBytesRun = 1;
                    276:         }
                    277:         else {
1.1.1.2 ! root      278:           /* Store run! */
        !           279:           *pMSABuffer++ = 0xE5;               /* Marker */
        !           280:           *pMSABuffer++ = *pImageBuffer;      /* Byte, and follow with 16-bit length */
1.1       root      281:           *(short int *)pMSABuffer = STMemory_Swap68000Int(nBytesRun);
                    282:           pMSABuffer += sizeof(short int);
                    283:           pImageBuffer += nBytesRun;
                    284:           nCompressedBytes += 4;
                    285:         }
                    286:         nBytesToGo -= nBytesRun;
                    287:       }
                    288: 
1.1.1.2 ! root      289:       /* Is compressed track smaller than the original? */
1.1       root      290:       if (nCompressedBytes<(NUMBYTESPERSECTOR*nSectorsPerTrack)) {
1.1.1.2 ! root      291:         /* Yes, store size */
1.1       root      292:         *pMSADataLength = STMemory_Swap68000Int(nCompressedBytes);
                    293:       }
                    294:       else {
1.1.1.2 ! root      295:         /* No, just store uncompressed track */
1.1       root      296:         *pMSADataLength++ = STMemory_Swap68000Int(NUMBYTESPERSECTOR*nSectorsPerTrack);
                    297:         pMSABuffer = (unsigned char *)pMSADataLength;
                    298:         pImageBuffer = pBuffer + (nBytesPerTrack*Side) + ((nBytesPerTrack*nSides)*Track);
                    299:         memcpy(pMSABuffer,pImageBuffer,(NUMBYTESPERSECTOR*nSectorsPerTrack));
                    300:         pMSABuffer += (NUMBYTESPERSECTOR*nSectorsPerTrack);
                    301:       }
                    302:     }
                    303:   }
                    304: 
1.1.1.2 ! root      305:   /* And save to file! */
        !           306:   nRet = File_Save(pszFileName,pMSAImageBuffer,pMSABuffer-pMSAImageBuffer,FALSE);
1.1       root      307: 
1.1.1.2 ! root      308:   /* Free workspace */
1.1       root      309:   Memory_Free(pMSAImageBuffer);
                    310: 
                    311:   return(nRet);
                    312: 
1.1.1.2 ! root      313: #else   /*SAVE_TO_MSA_IMAGES*/
1.1       root      314: 
1.1.1.2 ! root      315:   /* Oops, cannot save */
1.1       root      316:   return(FALSE);
                    317: 
1.1.1.2 ! root      318: #endif  /*SAVE_TO_MSA_IMAGES*/
1.1       root      319: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.