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