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