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

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

unix.superglobalmegacorp.com

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