|
|
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.6 ! root 9: const char MSA_rcsid[] = "Hatari $Id: msa.c,v 1.10 2006/02/08 22:49:27 eerot 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:
19: #include "uae-cpu/sysdeps.h"
20: #include "uae-cpu/maccess.h"
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:
94: typedef struct {
95: short int ID; /* Word ID marker, should be $0E0F */
96: short int SectorsPerTrack; /* Word Sectors per track */
97: short int Sides; /* Word Sides (0 or 1; add 1 to this to get correct number of sides) */
98: short int StartingTrack; /* Word Starting track (0-based) */
99: short int EndingTrack; /* Word Ending track (0-based) */
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 root 106: /*
1.1.1.3 root 107: Does filename end with a .MSA extension? If so, return TRUE
108: */
109: BOOL MSA_FileNameIsMSA(char *pszFileName, BOOL bAllowGZ)
110: {
111: return(File_DoesFileExtensionMatch(pszFileName,".msa")
112: || (bAllowGZ && File_DoesFileExtensionMatch(pszFileName,".msa.gz")));
113: }
114:
115:
116: /*-----------------------------------------------------------------------*/
117: /*
118: Uncompress .MSA data into a new buffer.
1.1 root 119: */
1.1.1.3 root 120: Uint8 *MSA_UnCompress(Uint8 *pMSAFile, long *pImageSize)
1.1 root 121: {
122: MSAHEADERSTRUCT *pMSAHeader;
123: unsigned char *pMSAImageBuffer,*pImageBuffer;
124: unsigned char Byte,Data;
125: int i,Track,Side,DataLength,NumBytesUnCompressed,RunLength;
1.1.1.3 root 126: Uint8 *pBuffer = NULL;
127:
128: *pImageSize = 0;
1.1 root 129:
1.1.1.2 root 130: /* Is an '.msa' file?? Check header */
1.1 root 131: pMSAHeader = (MSAHEADERSTRUCT *)pMSAFile;
1.1.1.3 root 132: if (pMSAHeader->ID == SDL_SwapBE16(0x0E0F)) {
1.1.1.2 root 133: /* First swap 'header' words around to PC format - easier later on */
1.1.1.3 root 134: pMSAHeader->SectorsPerTrack = SDL_SwapBE16(pMSAHeader->SectorsPerTrack);
135: pMSAHeader->Sides = SDL_SwapBE16(pMSAHeader->Sides);
136: pMSAHeader->StartingTrack = SDL_SwapBE16(pMSAHeader->StartingTrack);
137: pMSAHeader->EndingTrack = SDL_SwapBE16(pMSAHeader->EndingTrack);
138:
139: /* Create buffer */
140: pBuffer = malloc((pMSAHeader->EndingTrack - pMSAHeader->StartingTrack + 1) * pMSAHeader->SectorsPerTrack
141: * (pMSAHeader->Sides + 1) * NUMBYTESPERSECTOR);
142: if (!pBuffer)
143: {
144: perror("MSA_UnCompress");
145: return NULL;
146: }
1.1 root 147:
1.1.1.2 root 148: /* Set pointers */
1.1 root 149: pImageBuffer = (unsigned char *)pBuffer;
150: pMSAImageBuffer = (unsigned char *)((unsigned long)pMSAFile+sizeof(MSAHEADERSTRUCT));
151:
1.1.1.5 root 152: /* Uncompress to memory as '.ST' disk image - NOTE: assumes 512 bytes per sector (use NUMBYTESPERSECTOR define)!!! */
1.1.1.3 root 153: for(Track=pMSAHeader->StartingTrack; Track <= pMSAHeader->EndingTrack; Track++) {
1.1 root 154: for(Side=0; Side<(pMSAHeader->Sides+1); Side++) {
1.1.1.2 root 155: /* Uncompress MSA Track, first check if is not compressed */
1.1.1.4 root 156: DataLength = do_get_mem_word(pMSAImageBuffer);
1.1 root 157: pMSAImageBuffer += sizeof(short int);
158: if (DataLength==(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)) {
1.1.1.2 root 159: /* No compression on track, simply copy and continue */
1.1 root 160: memcpy(pImageBuffer,pMSAImageBuffer,NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack);
161: pImageBuffer += NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack;
162: pMSAImageBuffer += DataLength;
163: }
164: else {
1.1.1.2 root 165: /* Uncompress track */
1.1 root 166: NumBytesUnCompressed = 0;
167: while(NumBytesUnCompressed<(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)) {
168: Byte = *pMSAImageBuffer++;
1.1.1.2 root 169: if (Byte!=0xE5) { /* Compressed header?? */
170: *pImageBuffer++ = Byte; /* No, just copy byte */
1.1 root 171: NumBytesUnCompressed++;
172: }
173: else {
1.1.1.2 root 174: Data = *pMSAImageBuffer++; /* Byte to copy */
1.1.1.4 root 175: RunLength = do_get_mem_word(pMSAImageBuffer); /* For length */
1.1.1.2 root 176: /* Limit length to size of track, incorrect images may overflow */
1.1 root 177: if ( (RunLength+NumBytesUnCompressed)>(NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack) )
1.1.1.3 root 178: {
1.1.1.5 root 179: fprintf(stderr, "MSA_UnCompress: Illegal run length -> corrupted disk image?\n");
1.1 root 180: RunLength = (NUMBYTESPERSECTOR*pMSAHeader->SectorsPerTrack)-NumBytesUnCompressed;
1.1.1.3 root 181: }
1.1 root 182: pMSAImageBuffer += sizeof(short int);
183: for(i=0; i<RunLength; i++)
1.1.1.2 root 184: *pImageBuffer++ = Data; /* Copy byte */
1.1 root 185: NumBytesUnCompressed += RunLength;
186: }
187: }
188: }
189: }
190: }
191:
1.1.1.2 root 192: /* Set size of loaded image */
1.1.1.3 root 193: *pImageSize = (unsigned long)pImageBuffer-(unsigned long)pBuffer;
1.1 root 194: }
195:
1.1.1.3 root 196: /* Return pointer to buffer, NULL if failed */
197: return(pBuffer);
1.1 root 198: }
199:
1.1.1.2 root 200:
201: /*-----------------------------------------------------------------------*/
1.1 root 202: /*
1.1.1.5 root 203: Uncompress .MSA file into memory, set number bytes of the disk image and
1.1.1.3 root 204: return a pointer to the buffer.
1.1 root 205: */
1.1.1.5 root 206: Uint8 *MSA_ReadDisk(char *pszFileName, long *pImageSize)
1.1 root 207: {
1.1.1.3 root 208: Uint8 *pMsaFile;
1.1.1.5 root 209: Uint8 *pDiskBuffer = NULL;
1.1.1.3 root 210:
211: *pImageSize = 0;
1.1 root 212:
1.1.1.2 root 213: /* Read in file */
1.1.1.3 root 214: pMsaFile = File_Read(pszFileName, NULL, NULL, NULL);
215: if (pMsaFile)
216: {
1.1.1.5 root 217: /* Uncompress into disk buffer */
218: pDiskBuffer = MSA_UnCompress(pMsaFile, pImageSize);
1.1 root 219:
1.1.1.2 root 220: /* Free MSA file we loaded */
1.1.1.3 root 221: free(pMsaFile);
1.1 root 222: }
223:
1.1.1.3 root 224: /* Return pointer to buffer, NULL if failed */
1.1.1.5 root 225: return pDiskBuffer;
1.1 root 226: }
227:
1.1.1.2 root 228:
229: /*-----------------------------------------------------------------------*/
1.1 root 230: /*
231: Return number of bytes of the same byte in the passed buffer
1.1.1.3 root 232: If we return '0' this means no run (or end of buffer)
1.1 root 233: */
1.1.1.3 root 234: static int MSA_FindRunOfBytes(unsigned char *pBuffer, int nBytesInBuffer)
1.1 root 235: {
236: unsigned char ScannedByte;
237: int nTotalRun;
1.1.1.3 root 238: BOOL bMarker;
1.1 root 239: int i;
240:
1.1.1.3 root 241: /* Is this the marker? If so, this is at least a run of one. */
242: bMarker = (*pBuffer == 0xE5);
243:
1.1.1.2 root 244: /* Do we enough for a run? */
1.1 root 245: if (nBytesInBuffer<2)
1.1.1.3 root 246: {
247: if (nBytesInBuffer==1 && bMarker)
248: return 1;
249: else
250: return 0;
251: }
1.1 root 252:
1.1.1.2 root 253: /* OK, scan for run */
1.1 root 254: nTotalRun = 1;
255: ScannedByte = *pBuffer++;
1.1.1.3 root 256:
257: for(i=1; i<nBytesInBuffer; i++)
258: {
259: if (*pBuffer++==ScannedByte)
260: nTotalRun++;
261: else
262: break;
1.1 root 263: }
264:
1.1.1.3 root 265: /* Was this enough of a run to make a difference? */
266: if (nTotalRun<4 && !bMarker) nTotalRun = 0; /* Just store uncompressed */
267:
1.1 root 268: return(nTotalRun);
269: }
270:
1.1.1.2 root 271:
272: /*-----------------------------------------------------------------------*/
1.1 root 273: /*
274: Save compressed .MSA file from memory buffer. Returns TRUE is all OK
275: */
1.1.1.5 root 276: BOOL MSA_WriteDisk(char *pszFileName, Uint8 *pBuffer, int ImageSize)
1.1 root 277: {
278: #ifdef SAVE_TO_MSA_IMAGES
279:
280: MSAHEADERSTRUCT *pMSAHeader;
281: unsigned short int *pMSADataLength;
282: unsigned char *pMSAImageBuffer, *pMSABuffer, *pImageBuffer;
283: unsigned short int nSectorsPerTrack, nSides, nCompressedBytes, nBytesPerTrack;
284: BOOL nRet;
285: int nTracks,nBytesToGo,nBytesRun;
286: int Track,Side;
287:
1.1.1.2 root 288: /* Allocate workspace for compressed image */
1.1.1.4 root 289: pMSAImageBuffer = (unsigned char *)malloc(MSA_WORKSPACE_SIZE);
290: if (!pMSAImageBuffer)
291: {
1.1.1.5 root 292: perror("MSA_WriteDisk");
1.1.1.4 root 293: return FALSE;
294: }
1.1 root 295:
1.1.1.2 root 296: /* Store header */
1.1 root 297: pMSAHeader = (MSAHEADERSTRUCT *)pMSAImageBuffer;
1.1.1.3 root 298: pMSAHeader->ID = SDL_SwapBE16(0x0E0F);
1.1.1.5 root 299: Floppy_FindDiskDetails(pBuffer,ImageSize,&nSectorsPerTrack,&nSides);
1.1.1.3 root 300: pMSAHeader->SectorsPerTrack = SDL_SwapBE16(nSectorsPerTrack);
301: pMSAHeader->Sides = SDL_SwapBE16(nSides-1);
302: pMSAHeader->StartingTrack = SDL_SwapBE16(0);
1.1 root 303: nTracks = ((ImageSize / NUMBYTESPERSECTOR) / nSectorsPerTrack) / nSides;
1.1.1.3 root 304: pMSAHeader->EndingTrack = SDL_SwapBE16(nTracks-1);
1.1 root 305:
1.1.1.2 root 306: /* Compress image */
1.1 root 307: pMSABuffer = pMSAImageBuffer + sizeof(MSAHEADERSTRUCT);
308: for(Track=0; Track<nTracks; Track++) {
309: for(Side=0; Side<nSides; Side++) {
1.1.1.2 root 310: /* Get track data pointer */
1.1 root 311: nBytesPerTrack = NUMBYTESPERSECTOR*nSectorsPerTrack;
312: pImageBuffer = pBuffer + (nBytesPerTrack*Side) + ((nBytesPerTrack*nSides)*Track);
313:
1.1.1.2 root 314: /* Skip data length (fill in later) */
1.1 root 315: pMSADataLength = (unsigned short int *)pMSABuffer;
316: pMSABuffer += sizeof(short int);
317:
1.1.1.2 root 318: /* Compress track */
1.1 root 319: nBytesToGo = NUMBYTESPERSECTOR * nSectorsPerTrack;
320: nCompressedBytes = 0;
321: while(nBytesToGo>0) {
322: nBytesRun = MSA_FindRunOfBytes(pImageBuffer,nBytesToGo);
323: if (nBytesRun==0) {
1.1.1.2 root 324: /* Just copy byte */
1.1 root 325: *pMSABuffer++ = *pImageBuffer++;
326: nCompressedBytes++;
327: nBytesRun = 1;
328: }
329: else {
1.1.1.2 root 330: /* Store run! */
331: *pMSABuffer++ = 0xE5; /* Marker */
332: *pMSABuffer++ = *pImageBuffer; /* Byte, and follow with 16-bit length */
1.1.1.4 root 333: do_put_mem_word(pMSABuffer, nBytesRun);
1.1 root 334: pMSABuffer += sizeof(short int);
335: pImageBuffer += nBytesRun;
336: nCompressedBytes += 4;
337: }
338: nBytesToGo -= nBytesRun;
339: }
340:
1.1.1.2 root 341: /* Is compressed track smaller than the original? */
1.1 root 342: if (nCompressedBytes<(NUMBYTESPERSECTOR*nSectorsPerTrack)) {
1.1.1.2 root 343: /* Yes, store size */
1.1.1.3 root 344: do_put_mem_word(pMSADataLength, nCompressedBytes);
1.1 root 345: }
346: else {
1.1.1.2 root 347: /* No, just store uncompressed track */
1.1.1.3 root 348: do_put_mem_word(pMSADataLength, NUMBYTESPERSECTOR*nSectorsPerTrack);
349: pMSABuffer = ((unsigned char *)pMSADataLength) + 2;
1.1 root 350: pImageBuffer = pBuffer + (nBytesPerTrack*Side) + ((nBytesPerTrack*nSides)*Track);
351: memcpy(pMSABuffer,pImageBuffer,(NUMBYTESPERSECTOR*nSectorsPerTrack));
352: pMSABuffer += (NUMBYTESPERSECTOR*nSectorsPerTrack);
353: }
354: }
355: }
356:
1.1.1.2 root 357: /* And save to file! */
358: nRet = File_Save(pszFileName,pMSAImageBuffer,pMSABuffer-pMSAImageBuffer,FALSE);
1.1 root 359:
1.1.1.2 root 360: /* Free workspace */
1.1.1.4 root 361: free(pMSAImageBuffer);
1.1 root 362:
363: return(nRet);
364:
1.1.1.2 root 365: #else /*SAVE_TO_MSA_IMAGES*/
1.1 root 366:
1.1.1.2 root 367: /* Oops, cannot save */
1.1 root 368: return(FALSE);
369:
1.1.1.2 root 370: #endif /*SAVE_TO_MSA_IMAGES*/
1.1 root 371: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.