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

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

unix.superglobalmegacorp.com

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