|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.