|
|
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: ! 95: //----------------------------------------------------------------------- ! 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: ! 107: // Is an '.msa' file?? Check header ! 108: pMSAHeader = (MSAHEADERSTRUCT *)pMSAFile; ! 109: if (pMSAHeader->ID==STMemory_Swap68000Int(0x0E0F)) { ! 110: // First swap 'header' words around to PC format - easier later on ! 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: ! 116: // Set pointers ! 117: pImageBuffer = (unsigned char *)pBuffer; ! 118: pMSAImageBuffer = (unsigned char *)((unsigned long)pMSAFile+sizeof(MSAHEADERSTRUCT)); ! 119: ! 120: // Uncompress to memory as '.ST' disc image - NOTE assumes 512 bytes per sector(use NUMBYTESPERSECTOR define)!!! ! 121: for(Track=pMSAHeader->StartingTrack; Track<(pMSAHeader->EndingTrack+1); Track++) { ! 122: for(Side=0; Side<(pMSAHeader->Sides+1); Side++) { ! 123: // Uncompress MSA Track, first check if is not compressed ! 124: DataLength = STMemory_Swap68000Int(*(short int *)pMSAImageBuffer); ! 125: pMSAImageBuffer += sizeof(short int); ! 126: if (DataLength==(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)) { ! 127: // No compression on track, simply copy and continue ! 128: memcpy(pImageBuffer,pMSAImageBuffer,NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack); ! 129: pImageBuffer += NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack; ! 130: pMSAImageBuffer += DataLength; ! 131: } ! 132: else { ! 133: // Uncompress track ! 134: NumBytesUnCompressed = 0; ! 135: while(NumBytesUnCompressed<(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)) { ! 136: Byte = *pMSAImageBuffer++; ! 137: if (Byte!=0xE5) { // Compressed header?? ! 138: *pImageBuffer++ = Byte; // No, just copy byte ! 139: NumBytesUnCompressed++; ! 140: } ! 141: else { ! 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 ! 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++) ! 149: *pImageBuffer++ = Data; // Copy byte ! 150: NumBytesUnCompressed += RunLength; ! 151: } ! 152: } ! 153: } ! 154: } ! 155: } ! 156: ! 157: // Set size of loaded image ! 158: ImageSize = (unsigned long)pImageBuffer-(unsigned long)pBuffer; ! 159: } ! 160: ! 161: // Return number of bytes loaded, '0' if failed ! 162: return(ImageSize); ! 163: } ! 164: ! 165: //----------------------------------------------------------------------- ! 166: /* ! 167: Uncompress .MSA file into memory, return number of bytes loaded ! 168: */ ! 169: int MSA_ReadDisc(char *pszFileName,unsigned char *pBuffer) ! 170: { ! 171: unsigned char *pMSAFile; ! 172: int ImageSize = 0; ! 173: ! 174: // Read in file ! 175: pMSAFile = (unsigned char *)File_Read(pszFileName,NULL,NULL,NULL); ! 176: if (pMSAFile) { ! 177: // Uncompress into disc buffer ! 178: ImageSize = MSA_UnCompress(pMSAFile,pBuffer); ! 179: ! 180: // Free MSA file we loaded ! 181: Memory_Free(pMSAFile); ! 182: } ! 183: ! 184: // Return number of bytes loaded, '0' if failed ! 185: return(ImageSize); ! 186: } ! 187: ! 188: //----------------------------------------------------------------------- ! 189: /* ! 190: Return number of bytes of the same byte in the passed buffer ! 191: If we return '0' this means no run(or end of buffer) ! 192: */ ! 193: int MSA_FindRunOfBytes(unsigned char *pBuffer,int nBytesInBuffer) ! 194: { ! 195: unsigned char ScannedByte; ! 196: int nTotalRun; ! 197: int i; ! 198: ! 199: // Do we enough for a run? ! 200: if (nBytesInBuffer<2) ! 201: return(0); ! 202: ! 203: // OK, scan for run ! 204: nTotalRun = 1; ! 205: ScannedByte = *pBuffer++; ! 206: // Is this the marker? If so, this is a run of one ! 207: if (ScannedByte!=0xE5) { ! 208: for(i=1; i<nBytesInBuffer; i++) { ! 209: if (*pBuffer++==ScannedByte) ! 210: nTotalRun++; ! 211: else ! 212: break; ! 213: } ! 214: // Was this enough of a run to make a difference? ! 215: if (nTotalRun<4) ! 216: nTotalRun = 0; // Just store a bytes ! 217: } ! 218: ! 219: return(nTotalRun); ! 220: } ! 221: ! 222: //----------------------------------------------------------------------- ! 223: /* ! 224: Save compressed .MSA file from memory buffer. Returns TRUE is all OK ! 225: */ ! 226: BOOL MSA_WriteDisc(char *pszFileName,unsigned char *pBuffer,int ImageSize) ! 227: { ! 228: #ifdef SAVE_TO_MSA_IMAGES ! 229: ! 230: MSAHEADERSTRUCT *pMSAHeader; ! 231: unsigned short int *pMSADataLength; ! 232: unsigned char *pMSAImageBuffer, *pMSABuffer, *pImageBuffer; ! 233: unsigned short int nSectorsPerTrack, nSides, nCompressedBytes, nBytesPerTrack; ! 234: BOOL nRet; ! 235: int nTracks,nBytesToGo,nBytesRun; ! 236: int Track,Side; ! 237: ! 238: // Allocate workspace for compressed image ! 239: pMSAImageBuffer = (unsigned char *)Memory_Alloc(MSA_WORKSPACE_SIZE); ! 240: ! 241: // Store header ! 242: pMSAHeader = (MSAHEADERSTRUCT *)pMSAImageBuffer; ! 243: pMSAHeader->ID = STMemory_Swap68000Int(0x0E0F); ! 244: Floppy_FindDiscDetails(pBuffer,ImageSize,&nSectorsPerTrack,&nSides); ! 245: pMSAHeader->SectorsPerTrack = STMemory_Swap68000Int(nSectorsPerTrack); ! 246: pMSAHeader->Sides = STMemory_Swap68000Int(nSides-1); ! 247: pMSAHeader->StartingTrack = STMemory_Swap68000Int(0); ! 248: nTracks = ((ImageSize / NUMBYTESPERSECTOR) / nSectorsPerTrack) / nSides; ! 249: pMSAHeader->EndingTrack = STMemory_Swap68000Int(nTracks-1); ! 250: ! 251: // Compress image ! 252: pMSABuffer = pMSAImageBuffer + sizeof(MSAHEADERSTRUCT); ! 253: for(Track=0; Track<nTracks; Track++) { ! 254: for(Side=0; Side<nSides; Side++) { ! 255: // Get track data pointer ! 256: nBytesPerTrack = NUMBYTESPERSECTOR*nSectorsPerTrack; ! 257: pImageBuffer = pBuffer + (nBytesPerTrack*Side) + ((nBytesPerTrack*nSides)*Track); ! 258: ! 259: // Skip data length(fill in later) ! 260: pMSADataLength = (unsigned short int *)pMSABuffer; ! 261: pMSABuffer += sizeof(short int); ! 262: ! 263: // Compress track ! 264: nBytesToGo = NUMBYTESPERSECTOR * nSectorsPerTrack; ! 265: nCompressedBytes = 0; ! 266: while(nBytesToGo>0) { ! 267: nBytesRun = MSA_FindRunOfBytes(pImageBuffer,nBytesToGo); ! 268: if (nBytesRun==0) { ! 269: // Just copy byte ! 270: *pMSABuffer++ = *pImageBuffer++; ! 271: nCompressedBytes++; ! 272: nBytesRun = 1; ! 273: } ! 274: else { ! 275: // Store run! ! 276: *pMSABuffer++ = 0xE5; // Marker ! 277: *pMSABuffer++ = *pImageBuffer; // Byte, and follow with 16-bit length ! 278: *(short int *)pMSABuffer = STMemory_Swap68000Int(nBytesRun); ! 279: pMSABuffer += sizeof(short int); ! 280: pImageBuffer += nBytesRun; ! 281: nCompressedBytes += 4; ! 282: } ! 283: nBytesToGo -= nBytesRun; ! 284: } ! 285: ! 286: // Is compressed track smaller than the original? ! 287: if (nCompressedBytes<(NUMBYTESPERSECTOR*nSectorsPerTrack)) { ! 288: // Yes, store size ! 289: *pMSADataLength = STMemory_Swap68000Int(nCompressedBytes); ! 290: } ! 291: else { ! 292: // No, just store uncompressed track ! 293: *pMSADataLength++ = STMemory_Swap68000Int(NUMBYTESPERSECTOR*nSectorsPerTrack); ! 294: pMSABuffer = (unsigned char *)pMSADataLength; ! 295: pImageBuffer = pBuffer + (nBytesPerTrack*Side) + ((nBytesPerTrack*nSides)*Track); ! 296: memcpy(pMSABuffer,pImageBuffer,(NUMBYTESPERSECTOR*nSectorsPerTrack)); ! 297: pMSABuffer += (NUMBYTESPERSECTOR*nSectorsPerTrack); ! 298: } ! 299: } ! 300: } ! 301: ! 302: // And save to file! ! 303: nRet = File_Save(/*hWnd,*/pszFileName,pMSAImageBuffer,pMSABuffer-pMSAImageBuffer,FALSE); ! 304: ! 305: // Free workspace ! 306: Memory_Free(pMSAImageBuffer); ! 307: ! 308: return(nRet); ! 309: ! 310: #else //SAVE_TO_MSA_IMAGES ! 311: ! 312: // Oops, cannot save ! 313: return(FALSE); ! 314: ! 315: #endif //SAVE_TO_MSA_IMAGES ! 316: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.