3234541224e9f1aa3611df910e330a32040c8c94
[pcsx_rearmed.git] / libpcsxcore / sio.c
1 /***************************************************************************
2  *   Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team              *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
18  ***************************************************************************/
19
20 /*
21 * SIO functions.
22 */
23
24 #include <stdio.h>
25 #include "misc.h"
26 #include "psxcounters.h"
27 #include "psxevents.h"
28 #include "sio.h"
29 #include <sys/stat.h>
30
31 #ifdef USE_LIBRETRO_VFS
32 #include <streams/file_stream_transforms.h>
33 #endif
34
35 // Status Flags
36 #define TX_RDY          0x0001
37 #define RX_RDY          0x0002
38 #define TX_EMPTY        0x0004
39 #define PARITY_ERR      0x0008
40 #define RX_OVERRUN      0x0010
41 #define FRAMING_ERR     0x0020
42 #define SYNC_DETECT     0x0040
43 #define DSR                     0x0080
44 #define CTS                     0x0100
45 #define IRQ                     0x0200
46
47 // Control Flags
48 #define TX_PERM         0x0001
49 #define DTR                     0x0002
50 #define RX_PERM         0x0004
51 #define BREAK           0x0008
52 #define RESET_ERR       0x0010
53 #define RTS                     0x0020
54 #define SIO_RESET       0x0040
55
56 // *** FOR WORKS ON PADS AND MEMORY CARDS *****
57
58 static unsigned char buf[256];
59 static unsigned char cardh1[4] = { 0xff, 0x08, 0x5a, 0x5d };
60 static unsigned char cardh2[4] = { 0xff, 0x08, 0x5a, 0x5d };
61
62 // Transfer Ready and the Buffer is Empty
63 // static unsigned short StatReg = 0x002b;
64 static unsigned short StatReg = TX_RDY | TX_EMPTY;
65 static unsigned short ModeReg;
66 static unsigned short CtrlReg;
67 static unsigned short BaudReg;
68
69 static unsigned int bufcount;
70 static unsigned int parp;
71 static unsigned int mcdst, rdwr;
72 static unsigned char adrH, adrL;
73 static unsigned int padst;
74
75 char Mcd1Data[MCD_SIZE], Mcd2Data[MCD_SIZE];
76 char McdDisable[2];
77
78 // clk cycle byte
79 // 4us * 8bits = (PSXCLK / 1000000) * 32; (linuzappz)
80 // TODO: add SioModePrescaler and BaudReg
81 #define SIO_CYCLES              535
82
83 void sioWrite8(unsigned char value) {
84         int more_data = 0;
85 #if 0
86         s32 framec = psxRegs.cycle - rcnts[3].cycleStart;
87         printf("%d:%03d sio write8 %04x %02x\n", frame_counter,
88                 (s32)(framec / (PSXCLK / 60 / 263.0f)), CtrlReg, value);
89 #endif
90         switch (padst) {
91                 case 1:
92                         if ((value & 0x40) == 0x40) {
93                                 padst = 2; parp = 1;
94                                 switch (CtrlReg & 0x2002) {
95                                         case 0x0002:
96                                                 buf[parp] = PAD1_poll(value, &more_data);
97                                                 break;
98                                         case 0x2002:
99                                                 buf[parp] = PAD2_poll(value, &more_data);
100                                                 break;
101                                 }
102
103                                 if (more_data) {
104                                         bufcount = parp + 1;
105                                         set_event(PSXINT_SIO, SIO_CYCLES);
106                                 }
107                         }
108                         else padst = 0;
109                         return;
110                 case 2:
111                         parp++;
112                         switch (CtrlReg & 0x2002) {
113                                 case 0x0002: buf[parp] = PAD1_poll(value, &more_data); break;
114                                 case 0x2002: buf[parp] = PAD2_poll(value, &more_data); break;
115                         }
116
117                         if (more_data) {
118                                 bufcount = parp + 1;
119                                 set_event(PSXINT_SIO, SIO_CYCLES);
120                         }
121                         return;
122         }
123
124         switch (mcdst) {
125                 case 1:
126                         set_event(PSXINT_SIO, SIO_CYCLES);
127                         if (rdwr) { parp++; return; }
128                         parp = 1;
129                         switch (value) {
130                                 case 0x52: rdwr = 1; break;
131                                 case 0x57: rdwr = 2; break;
132                                 default: mcdst = 0;
133                         }
134                         return;
135                 case 2: // address H
136                         set_event(PSXINT_SIO, SIO_CYCLES);
137                         adrH = value;
138                         *buf = 0;
139                         parp = 0;
140                         bufcount = 1;
141                         mcdst = 3;
142                         return;
143                 case 3: // address L
144                         set_event(PSXINT_SIO, SIO_CYCLES);
145                         adrL = value;
146                         *buf = adrH;
147                         parp = 0;
148                         bufcount = 1;
149                         mcdst = 4;
150                         return;
151                 case 4:
152                         set_event(PSXINT_SIO, SIO_CYCLES);
153                         parp = 0;
154                         switch (rdwr) {
155                                 case 1: // read
156                                         buf[0] = 0x5c;
157                                         buf[1] = 0x5d;
158                                         buf[2] = adrH;
159                                         buf[3] = adrL;
160                                         switch (CtrlReg & 0x2002) {
161                                                 case 0x0002:
162                                                         memcpy(&buf[4], Mcd1Data + (adrL | (adrH << 8)) * 128, 128);
163                                                         break;
164                                                 case 0x2002:
165                                                         memcpy(&buf[4], Mcd2Data + (adrL | (adrH << 8)) * 128, 128);
166                                                         break;
167                                         }
168                                         {
169                                         char xor = 0;
170                                         int i;
171                                         for (i = 2; i < 128 + 4; i++)
172                                                 xor ^= buf[i];
173                                         buf[132] = xor;
174                                         }
175                                         buf[133] = 0x47;
176                                         bufcount = 133;
177                                         break;
178                                 case 2: // write
179                                         buf[0] = adrL;
180                                         buf[1] = value;
181                                         buf[129] = 0x5c;
182                                         buf[130] = 0x5d;
183                                         buf[131] = 0x47;
184                                         bufcount = 131;
185                                         break;
186                         }
187                         mcdst = 5;
188                         return;
189                 case 5:
190                         parp++;
191                         if ((rdwr == 1 && parp == 132) ||
192                             (rdwr == 2 && parp == 129)) {
193                                 // clear "new card" flags
194                                 if (CtrlReg & 0x2000)
195                                         cardh2[1] &= ~8;
196                                 else
197                                         cardh1[1] &= ~8;
198                         }
199                         if (rdwr == 2) {
200                                 if (parp < 128) buf[parp + 1] = value;
201                         }
202                         set_event(PSXINT_SIO, SIO_CYCLES);
203                         return;
204         }
205
206         switch (value) {
207                 case 0x01: // start pad
208                         StatReg |= RX_RDY;              // Transfer is Ready
209
210                         switch (CtrlReg & 0x2002) {
211                                 case 0x0002: buf[0] = PAD1_startPoll(1); break;
212                                 case 0x2002: buf[0] = PAD2_startPoll(2); break;
213                         }
214                         bufcount = 1;
215                         parp = 0;
216                         padst = 1;
217                         set_event(PSXINT_SIO, SIO_CYCLES);
218                         return;
219                 case 0x81: // start memcard
220                         if (CtrlReg & 0x2000)
221                         {
222                                 if (McdDisable[1])
223                                         goto no_device;
224                                 memcpy(buf, cardh2, 4);
225                         }
226                         else
227                         {
228                                 if (McdDisable[0])
229                                         goto no_device;
230                                 memcpy(buf, cardh1, 4);
231                         }
232                         StatReg |= RX_RDY;
233                         parp = 0;
234                         bufcount = 3;
235                         mcdst = 1;
236                         rdwr = 0;
237                         set_event(PSXINT_SIO, SIO_CYCLES);
238                         return;
239                 default:
240                 no_device:
241                         StatReg |= RX_RDY;
242                         buf[0] = 0xff;
243                         parp = 0;
244                         bufcount = 0;
245                         return;
246         }
247 }
248
249 void sioWriteStat16(unsigned short value) {
250 }
251
252 void sioWriteMode16(unsigned short value) {
253         ModeReg = value;
254 }
255
256 void sioWriteCtrl16(unsigned short value) {
257         CtrlReg = value & ~RESET_ERR;
258         if (value & RESET_ERR) StatReg &= ~IRQ;
259         if ((CtrlReg & SIO_RESET) || !(CtrlReg & DTR)) {
260                 padst = 0; mcdst = 0; parp = 0;
261                 StatReg = TX_RDY | TX_EMPTY;
262                 psxRegs.interrupt &= ~(1 << PSXINT_SIO);
263         }
264 }
265
266 void sioWriteBaud16(unsigned short value) {
267         BaudReg = value;
268 }
269
270 unsigned char sioRead8() {
271         unsigned char ret = 0;
272
273         if ((StatReg & RX_RDY)/* && (CtrlReg & RX_PERM)*/) {
274 //              StatReg &= ~RX_OVERRUN;
275                 ret = buf[parp];
276                 if (parp == bufcount) {
277                         StatReg &= ~RX_RDY;             // Receive is not Ready now
278                         if (mcdst == 5) {
279                                 mcdst = 0;
280                                 if (rdwr == 2) {
281                                         switch (CtrlReg & 0x2002) {
282                                                 case 0x0002:
283                                                         memcpy(Mcd1Data + (adrL | (adrH << 8)) * 128, &buf[1], 128);
284                                                         SaveMcd(Config.Mcd1, Mcd1Data, (adrL | (adrH << 8)) * 128, 128);
285                                                         break;
286                                                 case 0x2002:
287                                                         memcpy(Mcd2Data + (adrL | (adrH << 8)) * 128, &buf[1], 128);
288                                                         SaveMcd(Config.Mcd2, Mcd2Data, (adrL | (adrH << 8)) * 128, 128);
289                                                         break;
290                                         }
291                                 }
292                         }
293                         if (padst == 2) padst = 0;
294                         if (mcdst == 1) {
295                                 mcdst = 2;
296                                 StatReg|= RX_RDY;
297                         }
298                 }
299         }
300
301 #if 0
302         s32 framec = psxRegs.cycle - rcnts[3].cycleStart;
303         printf("%d:%03d sio read8  %04x %02x\n", frame_counter,
304                 (s32)((float)framec / (PSXCLK / 60 / 263.0f)), CtrlReg, ret);
305 #endif
306         return ret;
307 }
308
309 unsigned short sioReadStat16() {
310         return StatReg;
311 }
312
313 unsigned short sioReadMode16() {
314         return ModeReg;
315 }
316
317 unsigned short sioReadCtrl16() {
318         return CtrlReg;
319 }
320
321 unsigned short sioReadBaud16() {
322         return BaudReg;
323 }
324
325 void sioInterrupt() {
326 #ifdef PAD_LOG
327         PAD_LOG("Sio Interrupt (CP0.Status = %x)\n", psxRegs.CP0.n.Status);
328 #endif
329 //      SysPrintf("Sio Interrupt\n");
330         if (!(StatReg & IRQ)) {
331                 StatReg |= IRQ;
332                 psxHu32ref(0x1070) |= SWAPu32(0x80);
333         }
334 }
335
336 void LoadMcd(int mcd, char *str) {
337         FILE *f;
338         char *data = NULL;
339
340         if (mcd != 1 && mcd != 2)
341                 return;
342
343         if (mcd == 1) {
344                 data = Mcd1Data;
345                 cardh1[1] |= 8; // mark as new
346         }
347         if (mcd == 2) {
348                 data = Mcd2Data;
349                 cardh2[1] |= 8;
350         }
351
352         McdDisable[mcd - 1] = 0;
353 #ifdef HAVE_LIBRETRO
354         // memcard1 is handled by libretro
355         if (mcd == 1)
356                 return;
357 #endif
358
359         if (str == NULL || strcmp(str, "none") == 0) {
360                 McdDisable[mcd - 1] = 1;
361                 return;
362         }
363         if (*str == 0)
364                 return;
365
366         f = fopen(str, "rb");
367         if (f == NULL) {
368                 SysPrintf(_("The memory card %s doesn't exist - creating it\n"), str);
369                 CreateMcd(str);
370                 f = fopen(str, "rb");
371                 if (f != NULL) {
372                         struct stat buf;
373
374                         if (stat(str, &buf) != -1) {
375                                 if (buf.st_size == MCD_SIZE + 64)
376                                         fseek(f, 64, SEEK_SET);
377                                 else if(buf.st_size == MCD_SIZE + 3904)
378                                         fseek(f, 3904, SEEK_SET);
379                         }
380                         if (fread(data, 1, MCD_SIZE, f) != MCD_SIZE) {
381 #ifndef NDEBUG
382                                 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
383 #endif
384                                 memset(data, 0x00, MCD_SIZE);
385                         }
386                         fclose(f);
387                 }
388                 else
389                         SysMessage(_("Memory card %s failed to load!\n"), str);
390         }
391         else {
392                 struct stat buf;
393                 SysPrintf(_("Loading memory card %s\n"), str);
394                 if (stat(str, &buf) != -1) {
395                         if (buf.st_size == MCD_SIZE + 64)
396                                 fseek(f, 64, SEEK_SET);
397                         else if(buf.st_size == MCD_SIZE + 3904)
398                                 fseek(f, 3904, SEEK_SET);
399                 }
400                 if (fread(data, 1, MCD_SIZE, f) != MCD_SIZE) {
401 #ifndef NDEBUG
402                         SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
403 #endif
404                         memset(data, 0x00, MCD_SIZE);
405                 }
406                 fclose(f);
407         }
408 }
409
410 void LoadMcds(char *mcd1, char *mcd2) {
411         LoadMcd(1, mcd1);
412         LoadMcd(2, mcd2);
413 }
414
415 void SaveMcd(char *mcd, char *data, uint32_t adr, int size) {
416         FILE *f;
417
418         if (mcd == NULL || *mcd == 0 || strcmp(mcd, "none") == 0)
419                 return;
420
421         f = fopen(mcd, "r+b");
422         if (f != NULL) {
423                 struct stat buf;
424
425                 if (stat(mcd, &buf) != -1) {
426                         if (buf.st_size == MCD_SIZE + 64)
427                                 fseek(f, adr + 64, SEEK_SET);
428                         else if (buf.st_size == MCD_SIZE + 3904)
429                                 fseek(f, adr + 3904, SEEK_SET);
430                         else
431                                 fseek(f, adr, SEEK_SET);
432                 } else
433                         fseek(f, adr, SEEK_SET);
434
435                 fwrite(data + adr, 1, size, f);
436                 fclose(f);
437                 return;
438         }
439
440 #if 0
441         // try to create it again if we can't open it
442         f = fopen(mcd, "wb");
443         if (f != NULL) {
444                 fwrite(data, 1, MCD_SIZE, f);
445                 fclose(f);
446         }
447 #endif
448
449         ConvertMcd(mcd, data);
450 }
451
452 void CreateMcd(char *mcd) {
453         FILE *f;
454         struct stat buf;
455         int s = MCD_SIZE;
456         int i = 0, j;
457
458         f = fopen(mcd, "wb");
459         if (f == NULL) {
460                 SysPrintf("CreateMcd: couldn't open %s\n", mcd);
461                 return;
462         }
463
464         if (stat(mcd, &buf) != -1) {
465                 if ((buf.st_size == MCD_SIZE + 3904) || strstr(mcd, ".gme")) {
466                         s = s + 3904;
467                         fputc('1', f);
468                         s--;
469                         fputc('2', f);
470                         s--;
471                         fputc('3', f);
472                         s--;
473                         fputc('-', f);
474                         s--;
475                         fputc('4', f);
476                         s--;
477                         fputc('5', f);
478                         s--;
479                         fputc('6', f);
480                         s--;
481                         fputc('-', f);
482                         s--;
483                         fputc('S', f);
484                         s--;
485                         fputc('T', f);
486                         s--;
487                         fputc('D', f);
488                         s--;
489                         for (i = 0; i < 7; i++) {
490                                 fputc(0, f);
491                                 s--;
492                         }
493                         fputc(1, f);
494                         s--;
495                         fputc(0, f);
496                         s--;
497                         fputc(1, f);
498                         s--;
499                         fputc('M', f);
500                         s--;
501                         fputc('Q', f);
502                         s--;
503                         for (i = 0; i < 14; i++) {
504                                 fputc(0xa0, f);
505                                 s--;
506                         }
507                         fputc(0, f);
508                         s--;
509                         fputc(0xff, f);
510                         while (s-- > (MCD_SIZE + 1))
511                                 fputc(0, f);
512                 } else if ((buf.st_size == MCD_SIZE + 64) || strstr(mcd, ".mem") || strstr(mcd, ".vgs")) {
513                         s = s + 64;
514                         fputc('V', f);
515                         s--;
516                         fputc('g', f);
517                         s--;
518                         fputc('s', f);
519                         s--;
520                         fputc('M', f);
521                         s--;
522                         for (i = 0; i < 3; i++) {
523                                 fputc(1, f);
524                                 s--;
525                                 fputc(0, f);
526                                 s--;
527                                 fputc(0, f);
528                                 s--;
529                                 fputc(0, f);
530                                 s--;
531                         }
532                         fputc(0, f);
533                         s--;
534                         fputc(2, f);
535                         while (s-- > (MCD_SIZE + 1))
536                                 fputc(0, f);
537                 }
538         }
539         fputc('M', f);
540         s--;
541         fputc('C', f);
542         s--;
543         while (s-- > (MCD_SIZE - 127))
544                 fputc(0, f);
545         fputc(0xe, f);
546         s--;
547
548         for (i = 0; i < 15; i++) { // 15 blocks
549                 fputc(0xa0, f);
550                 s--;
551                 fputc(0x00, f);
552                 s--;
553                 fputc(0x00, f);
554                 s--;
555                 fputc(0x00, f);
556                 s--;
557                 fputc(0x00, f);
558                 s--;
559                 fputc(0x00, f);
560                 s--;
561                 fputc(0x00, f);
562                 s--;
563                 fputc(0x00, f);
564                 s--;
565                 fputc(0xff, f);
566                 s--;
567                 fputc(0xff, f);
568                 s--;
569                 for (j = 0; j < 117; j++) {
570                         fputc(0x00, f);
571                         s--;
572                 }
573                 fputc(0xa0, f);
574                 s--;
575         }
576
577         for (i = 0; i < 20; i++) {
578                 fputc(0xff, f);
579                 s--;
580                 fputc(0xff, f);
581                 s--;
582                 fputc(0xff, f);
583                 s--;
584                 fputc(0xff, f);
585                 s--;
586                 fputc(0x00, f);
587                 s--;
588                 fputc(0x00, f);
589                 s--;
590                 fputc(0x00, f);
591                 s--;
592                 fputc(0x00, f);
593                 s--;
594                 fputc(0xff, f);
595                 s--;
596                 fputc(0xff, f);
597                 s--;
598                 for (j = 0; j < 118; j++) {
599                         fputc(0x00, f);
600                         s--;
601                 }
602         }
603
604         while ((s--) >= 0)
605                 fputc(0, f);
606
607         fclose(f);
608 }
609
610 void ConvertMcd(char *mcd, char *data) {
611         FILE *f;
612         int i = 0;
613         int s = MCD_SIZE;
614
615         if (strstr(mcd, ".gme")) {
616                 f = fopen(mcd, "wb");
617                 if (f != NULL) {
618                         fwrite(data - 3904, 1, MCD_SIZE + 3904, f);
619                         fclose(f);
620                 }
621                 f = fopen(mcd, "r+");
622                 if (f == NULL) return;
623                 s = s + 3904;
624                 fputc('1', f); s--;
625                 fputc('2', f); s--;
626                 fputc('3', f); s--;
627                 fputc('-', f); s--;
628                 fputc('4', f); s--;
629                 fputc('5', f); s--;
630                 fputc('6', f); s--;
631                 fputc('-', f); s--;
632                 fputc('S', f); s--;
633                 fputc('T', f); s--;
634                 fputc('D', f); s--;
635                 for (i = 0; i < 7; i++) {
636                         fputc(0, f); s--;
637                 }
638                 fputc(1, f); s--;
639                 fputc(0, f); s--;
640                 fputc(1, f); s--;
641                 fputc('M', f); s--;
642                 fputc('Q', f); s--;
643                 for(i=0;i<14;i++) {
644                         fputc(0xa0, f); s--;
645                 }
646                 fputc(0, f); s--;
647                 fputc(0xff, f);
648                 while (s-- > (MCD_SIZE+1)) fputc(0, f);
649                 fclose(f);
650         } else if(strstr(mcd, ".mem") || strstr(mcd,".vgs")) {
651                 f = fopen(mcd, "wb");
652                 if (f != NULL) {
653                         fwrite(data-64, 1, MCD_SIZE+64, f);
654                         fclose(f);
655                 }
656                 f = fopen(mcd, "r+");
657                 if (f == NULL) return;
658                 s = s + 64;
659                 fputc('V', f); s--;
660                 fputc('g', f); s--;
661                 fputc('s', f); s--;
662                 fputc('M', f); s--;
663                 for(i=0;i<3;i++) {
664                         fputc(1, f); s--;
665                         fputc(0, f); s--;
666                         fputc(0, f); s--;
667                         fputc(0, f); s--;
668                 }
669                 fputc(0, f); s--;
670                 fputc(2, f);
671                 while (s-- > (MCD_SIZE+1)) fputc(0, f);
672                 fclose(f);
673         } else {
674                 f = fopen(mcd, "wb");
675                 if (f != NULL) {
676                         fwrite(data, 1, MCD_SIZE, f);
677                         fclose(f);
678                 }
679         }
680 }
681
682 void GetMcdBlockInfo(int mcd, int block, McdBlock *Info) {
683         unsigned char *data = NULL, *ptr;
684         char *str, *sstr;
685         unsigned short clut[16];
686         unsigned short c;
687         int i, s, x, skip;
688
689         memset(Info, 0, sizeof(McdBlock));
690
691         if (mcd != 1 && mcd != 2)
692                 return;
693
694         if (McdDisable[mcd - 1])
695                 return;
696
697         if (mcd == 1) data = (unsigned char *)Mcd1Data;
698         if (mcd == 2) data = (unsigned char *)Mcd2Data;
699
700         ptr = data + block * 8192 + 2;
701
702         Info->IconCount = *ptr & 0x3;
703
704         ptr += 2;
705
706         x = 0;
707
708         str = Info->Title;
709         sstr = Info->sTitle;
710
711         s = skip = 0;
712         for (i = 0; i < 48; i++) {
713                 c = *(ptr) << 8;
714                 c |= *(ptr + 1);
715                 if (!c) break;
716
717                 // Convert ASCII characters to half-width
718                 if (c >= 0x8281 && c <= 0x829A)
719                         c = (c - 0x8281) + 'a';
720                 else if (c >= 0x824F && c <= 0x827A)
721                         c = (c - 0x824F) + '0';
722                 else if (c == 0x8140) c = ' ';
723                 else if (c == 0x8143) c = ',';
724                 else if (c == 0x8144) c = '.';
725                 else if (c == 0x8146) c = ':';
726                 else if (c == 0x8147) c = ';';
727                 else if (c == 0x8148) c = '?';
728                 else if (c == 0x8149) c = '!';
729                 else if (c == 0x815E) c = '/';
730                 else if (c == 0x8168) c = '"';
731                 else if (c == 0x8169) c = '(';
732                 else if (c == 0x816A) c = ')';
733                 else if (c == 0x816D) c = '[';
734                 else if (c == 0x816E) c = ']';
735                 else if (c == 0x817C) c = '-';
736                 else {
737                         if (!skip++)
738                                 str[s++] = ' ';
739                         sstr[x++] = *ptr++; sstr[x++] = *ptr++;
740                         continue;
741                 }
742
743                 skip = 0;
744                 str[s++] = sstr[x++] = c;
745                 ptr += 2;
746         }
747
748         trim(str);
749         trim(sstr);
750
751         ptr = data + block * 8192 + 0x60; // icon palette data
752         memcpy(clut, ptr, 16*2);
753
754         for (i = 0; i < Info->IconCount; i++) {
755                 short *icon = &Info->Icon[i * 16 * 16];
756
757                 ptr = data + block * 8192 + 128 + 128 * i; // icon data
758
759                 for (x = 0; x < 16 * 16; ) {
760                         icon[x++] = clut[*ptr & 0xf];
761                         icon[x++] = clut[*ptr >> 4];
762                         ptr++;
763                 }
764         }
765
766         ptr = data + block * 128;
767
768         Info->Flags = *ptr;
769
770         ptr += 0xa;
771         strncpy(Info->ID, (char *)ptr, 12);
772         ptr += 12;
773         strncpy(Info->Name, (char *)ptr, 16);
774 }
775
776 int sioFreeze(void *f, int Mode) {
777         gzfreeze(buf, sizeof(buf));
778         gzfreeze(&StatReg, sizeof(StatReg));
779         gzfreeze(&ModeReg, sizeof(ModeReg));
780         gzfreeze(&CtrlReg, sizeof(CtrlReg));
781         gzfreeze(&BaudReg, sizeof(BaudReg));
782         gzfreeze(&bufcount, sizeof(bufcount));
783         gzfreeze(&parp, sizeof(parp));
784         gzfreeze(&mcdst, sizeof(mcdst));
785         gzfreeze(&rdwr, sizeof(rdwr));
786         gzfreeze(&adrH, sizeof(adrH));
787         gzfreeze(&adrL, sizeof(adrL));
788         gzfreeze(&padst, sizeof(padst));
789
790         return 0;
791 }