|
|
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.