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

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

unix.superglobalmegacorp.com

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