drc: don't pass r3 to memhandlers
[pcsx_rearmed.git] / libpcsxcore / misc.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 * Miscellaneous functions, including savestates and CD-ROM loading.
22 */
23
24 #include "misc.h"
25 #include "cdrom.h"
26 #include "mdec.h"
27 #include "ppf.h"
28
29 char CdromId[10] = "";
30 char CdromLabel[33] = "";
31
32 // PSX Executable types
33 #define PSX_EXE     1
34 #define CPE_EXE     2
35 #define COFF_EXE    3
36 #define INVALID_EXE 4
37
38 #define ISODCL(from, to) (to - from + 1)
39
40 struct iso_directory_record {
41         char length                     [ISODCL (1, 1)]; /* 711 */
42         char ext_attr_length            [ISODCL (2, 2)]; /* 711 */
43         char extent                     [ISODCL (3, 10)]; /* 733 */
44         char size                       [ISODCL (11, 18)]; /* 733 */
45         char date                       [ISODCL (19, 25)]; /* 7 by 711 */
46         char flags                      [ISODCL (26, 26)];
47         char file_unit_size             [ISODCL (27, 27)]; /* 711 */
48         char interleave                 [ISODCL (28, 28)]; /* 711 */
49         char volume_sequence_number     [ISODCL (29, 32)]; /* 723 */
50         unsigned char name_len          [ISODCL (33, 33)]; /* 711 */
51         char name                       [1];
52 };
53
54 void mmssdd( char *b, char *p )
55 {
56         int m, s, d;
57 #if defined(__arm__)
58         int block = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
59 #elif defined(__BIGENDIAN__)
60         int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
61 #else
62         int block = *((int*)b);
63 #endif
64
65         block += 150;
66         m = block / 4500;                       // minutes
67         block = block - m * 4500;       // minutes rest
68         s = block / 75;                         // seconds
69         d = block - s * 75;                     // seconds rest
70
71         m = ((m / 10) << 4) | m % 10;
72         s = ((s / 10) << 4) | s % 10;
73         d = ((d / 10) << 4) | d % 10;   
74
75         p[0] = m;
76         p[1] = s;
77         p[2] = d;
78 }
79
80 #define incTime() \
81         time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
82         time[2]++; \
83         if(time[2] == 75) { \
84                 time[2] = 0; \
85                 time[1]++; \
86                 if (time[1] == 60) { \
87                         time[1] = 0; \
88                         time[0]++; \
89                 } \
90         } \
91         time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
92
93 #define READTRACK() \
94         if (CDR_readTrack(time) == -1) return -1; \
95         buf = CDR_getBuffer(); \
96         if (buf == NULL) return -1; else CheckPPFCache(buf, time[0], time[1], time[2]);
97
98 #define READDIR(_dir) \
99         READTRACK(); \
100         memcpy(_dir, buf + 12, 2048); \
101  \
102         incTime(); \
103         READTRACK(); \
104         memcpy(_dir + 2048, buf + 12, 2048);
105
106 int GetCdromFile(u8 *mdir, u8 *time, s8 *filename) {
107         struct iso_directory_record *dir;
108         char ddir[4096];
109         u8 *buf;
110         int i;
111
112         // only try to scan if a filename is given
113         if (!strlen(filename)) return -1;
114
115         i = 0;
116         while (i < 4096) {
117                 dir = (struct iso_directory_record*) &mdir[i];
118                 if (dir->length[0] == 0) {
119                         return -1;
120                 }
121                 i += dir->length[0];
122
123                 if (dir->flags[0] & 0x2) { // it's a dir
124                         if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
125                                 if (filename[dir->name_len[0]] != '\\') continue;
126
127                                 filename += dir->name_len[0] + 1;
128
129                                 mmssdd(dir->extent, (char *)time);
130                                 READDIR(ddir);
131                                 i = 0;
132                                 mdir = ddir;
133                         }
134                 } else {
135                         if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
136                                 mmssdd(dir->extent, (char *)time);
137                                 break;
138                         }
139                 }
140         }
141         return 0;
142 }
143
144 int LoadCdrom() {
145         EXE_HEADER tmpHead;
146         struct iso_directory_record *dir;
147         u8 time[4], *buf;
148         u8 mdir[4096];
149         s8 exename[256];
150
151         if (!Config.HLE) {
152                 psxRegs.pc = psxRegs.GPR.n.ra;
153                 return 0;
154         }
155
156         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
157
158         READTRACK();
159
160         // skip head and sub, and go to the root directory record
161         dir = (struct iso_directory_record*) &buf[12+156]; 
162
163         mmssdd(dir->extent, (char*)time);
164
165         READDIR(mdir);
166
167         // Load SYSTEM.CNF and scan for the main executable
168         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
169                 // if SYSTEM.CNF is missing, start an existing PSX.EXE
170                 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
171
172                 READTRACK();
173         }
174         else {
175                 // read the SYSTEM.CNF
176                 READTRACK();
177
178                 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
179                 if (GetCdromFile(mdir, time, exename) == -1) {
180                         sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
181                         if (GetCdromFile(mdir, time, exename) == -1) {
182                                 char *ptr = strstr(buf + 12, "cdrom:");
183                                 if (ptr != NULL) {
184                                         ptr += 6;
185                                         while (*ptr == '\\' || *ptr == '/') ptr++;
186                                         strncpy(exename, ptr, 255);
187                                         exename[255] = '\0';
188                                         ptr = exename;
189                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
190                                         *ptr = '\0';
191                                         if (GetCdromFile(mdir, time, exename) == -1)
192                                                 return -1;
193                                 } else
194                                         return -1;
195                         }
196                 }
197
198                 // Read the EXE-Header
199                 READTRACK();
200         }
201
202         memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
203
204         psxRegs.pc = SWAP32(tmpHead.pc0);
205         psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
206         psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr); 
207         if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
208
209         tmpHead.t_size = SWAP32(tmpHead.t_size);
210         tmpHead.t_addr = SWAP32(tmpHead.t_addr);
211
212         // Read the rest of the main executable
213         while (tmpHead.t_size) {
214                 void *ptr = (void *)PSXM(tmpHead.t_addr);
215
216                 incTime();
217                 READTRACK();
218
219                 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
220
221                 tmpHead.t_size -= 2048;
222                 tmpHead.t_addr += 2048;
223         }
224
225         return 0;
226 }
227
228 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
229         struct iso_directory_record *dir;
230         u8 time[4],*buf;
231         u8 mdir[4096], exename[256];
232         u32 size, addr;
233
234         sscanf(filename, "cdrom:\\%256s", exename);
235
236         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
237
238         READTRACK();
239
240         // skip head and sub, and go to the root directory record
241         dir = (struct iso_directory_record *)&buf[12 + 156]; 
242
243         mmssdd(dir->extent, (char*)time);
244
245         READDIR(mdir);
246
247         if (GetCdromFile(mdir, time, exename) == -1) return -1;
248
249         READTRACK();
250
251         memcpy(head, buf + 12, sizeof(EXE_HEADER));
252         size = head->t_size;
253         addr = head->t_addr;
254
255         while (size) {
256                 incTime();
257                 READTRACK();
258
259                 memcpy((void *)PSXM(addr), buf + 12, 2048);
260
261                 size -= 2048;
262                 addr += 2048;
263         }
264
265         return 0;
266 }
267
268 int CheckCdrom() {
269         struct iso_directory_record *dir;
270         unsigned char time[4], *buf;
271         unsigned char mdir[4096];
272         char exename[256];
273         int i, c;
274
275         FreePPFCache();
276
277         time[0] = itob(0);
278         time[1] = itob(2);
279         time[2] = itob(0x10);
280
281         READTRACK();
282
283         CdromLabel[0] = '\0';
284         CdromId[0] = '\0';
285
286         strncpy(CdromLabel, buf + 52, 32);
287
288         // skip head and sub, and go to the root directory record
289         dir = (struct iso_directory_record *)&buf[12 + 156]; 
290
291         mmssdd(dir->extent, (char *)time);
292
293         READDIR(mdir);
294
295         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
296                 READTRACK();
297
298                 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
299                 if (GetCdromFile(mdir, time, exename) == -1) {
300                         sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
301                         if (GetCdromFile(mdir, time, exename) == -1) {
302                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
303                                 if (ptr != NULL) {
304                                         ptr += 6;
305                                         while (*ptr == '\\' || *ptr == '/') ptr++;
306                                         strncpy(exename, ptr, 255);
307                                         exename[255] = '\0';
308                                         ptr = exename;
309                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
310                                         *ptr = '\0';
311                                         if (GetCdromFile(mdir, time, exename) == -1)
312                                                 return -1;              // main executable not found
313                                 } else
314                                         return -1;
315                         }
316                 }
317         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
318                 strcpy(exename, "PSX.EXE;1");
319                 strcpy(CdromId, "SLUS99999");
320         } else
321                 return -1;              // SYSTEM.CNF and PSX.EXE not found
322
323         if (CdromId[0] == '\0') {
324                 i = strlen(exename);
325                 if (i >= 2) {
326                         if (exename[i - 2] == ';') i-= 2;
327                         c = 8; i--;
328                         while (i >= 0 && c >= 0) {
329                                 if (isalnum(exename[i])) CdromId[c--] = exename[i];
330                                 i--;
331                         }
332                 }
333         }
334
335         if (Config.PsxAuto) { // autodetect system (pal or ntsc)
336                 if (CdromId[2] == 'e' || CdromId[2] == 'E')
337                         Config.PsxType = PSX_TYPE_PAL; // pal
338                 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
339         }
340
341         if (CdromLabel[0] == ' ') {
342                 strncpy(CdromLabel, CdromId, 9);
343         }
344         SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
345         SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
346
347         BuildPPFCache();
348
349         return 0;
350 }
351
352 static int PSXGetFileType(FILE *f) {
353         unsigned long current;
354         u8 mybuf[2048];
355         EXE_HEADER *exe_hdr;
356         FILHDR *coff_hdr;
357
358         current = ftell(f);
359         fseek(f, 0L, SEEK_SET);
360         fread(mybuf, 2048, 1, f);
361         fseek(f, current, SEEK_SET);
362
363         exe_hdr = (EXE_HEADER *)mybuf;
364         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
365                 return PSX_EXE;
366
367         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
368                 return CPE_EXE;
369
370         coff_hdr = (FILHDR *)mybuf;
371         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
372                 return COFF_EXE;
373
374         return INVALID_EXE;
375 }
376
377 int Load(const char *ExePath) {
378         FILE *tmpFile;
379         EXE_HEADER tmpHead;
380         int type;
381         int retval = 0;
382         u8 opcode;
383         u32 section_address, section_size;
384
385         strncpy(CdromId, "SLUS99999", 9);
386         strncpy(CdromLabel, "SLUS_999.99", 11);
387
388         tmpFile = fopen(ExePath, "rb");
389         if (tmpFile == NULL) {
390                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
391                 retval = -1;
392         } else {
393                 type = PSXGetFileType(tmpFile);
394                 switch (type) {
395                         case PSX_EXE:
396                                 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
397                                 fseek(tmpFile, 0x800, SEEK_SET);                
398                                 fread((void *)PSXM(SWAP32(tmpHead.t_addr)), SWAP32(tmpHead.t_size),1,tmpFile);
399                                 fclose(tmpFile);
400                                 psxRegs.pc = SWAP32(tmpHead.pc0);
401                                 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
402                                 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr); 
403                                 if (psxRegs.GPR.n.sp == 0)
404                                         psxRegs.GPR.n.sp = 0x801fff00;
405                                 retval = 0;
406                                 break;
407                         case CPE_EXE:
408                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
409                                 do {
410                                         fread(&opcode, 1, 1, tmpFile);
411                                         switch (opcode) {
412                                                 case 1: /* Section loading */
413                                                         fread(&section_address, 4, 1, tmpFile);
414                                                         fread(&section_size, 4, 1, tmpFile);
415                                                         section_address = SWAPu32(section_address);
416                                                         section_size = SWAPu32(section_size);
417 #ifdef EMU_LOG
418                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
419 #endif
420                                                         fread(PSXM(section_address), section_size, 1, tmpFile);
421                                                         break;
422                                                 case 3: /* register loading (PC only?) */
423                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
424                                                         fread(&psxRegs.pc, 4, 1, tmpFile);
425                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
426                                                         break;
427                                                 case 0: /* End of file */
428                                                         break;
429                                                 default:
430                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
431                                                         retval = -1;
432                                                         break;
433                                         }
434                                 } while (opcode != 0 && retval == 0);
435                                 break;
436                         case COFF_EXE:
437                                 SysPrintf(_("COFF files not supported.\n"));
438                                 retval = -1;
439                                 break;
440                         case INVALID_EXE:
441                                 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
442                                 retval = -1;
443                                 break;
444                 }
445         }
446
447         if (retval != 0) {
448                 CdromId[0] = '\0';
449                 CdromLabel[0] = '\0';
450         }
451
452         return retval;
453 }
454
455 // STATES
456
457 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
458
459 // Savestate Versioning!
460 // If you make changes to the savestate version, please increment the value below.
461 static const u32 SaveVersion = 0x8b410006;
462
463 int SaveState(const char *file) {
464         gzFile f;
465         GPUFreeze_t *gpufP;
466         SPUFreeze_t *spufP;
467         int Size;
468         unsigned char *pMem;
469
470         f = gzopen(file, "wb");
471         if (f == NULL) return -1;
472
473         new_dyna_save();
474
475         gzwrite(f, (void *)PcsxHeader, 32);
476         gzwrite(f, (void *)&SaveVersion, sizeof(u32));
477         gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
478
479         pMem = (unsigned char *)malloc(128 * 96 * 3);
480         if (pMem == NULL) return -1;
481         GPU_getScreenPic(pMem);
482         gzwrite(f, pMem, 128 * 96 * 3);
483         free(pMem);
484
485         if (Config.HLE)
486                 psxBiosFreeze(1);
487
488         gzwrite(f, psxM, 0x00200000);
489         gzwrite(f, psxR, 0x00080000);
490         gzwrite(f, psxH, 0x00010000);
491         gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
492
493         // gpu
494         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
495         gpufP->ulFreezeVersion = 1;
496         GPU_freeze(1, gpufP);
497         gzwrite(f, gpufP, sizeof(GPUFreeze_t));
498         free(gpufP);
499
500         // spu
501         spufP = (SPUFreeze_t *) malloc(16);
502         SPU_freeze(2, spufP);
503         Size = spufP->Size; gzwrite(f, &Size, 4);
504         free(spufP);
505         spufP = (SPUFreeze_t *) malloc(Size);
506         SPU_freeze(1, spufP);
507         gzwrite(f, spufP, Size);
508         free(spufP);
509
510         sioFreeze(f, 1);
511         cdrFreeze(f, 1);
512         psxHwFreeze(f, 1);
513         psxRcntFreeze(f, 1);
514         mdecFreeze(f, 1);
515
516         gzclose(f);
517
518         return 0;
519 }
520
521 int LoadState(const char *file) {
522         gzFile f;
523         GPUFreeze_t *gpufP;
524         SPUFreeze_t *spufP;
525         int Size;
526         char header[32];
527         u32 version;
528         boolean hle;
529
530         f = gzopen(file, "rb");
531         if (f == NULL) return -1;
532
533         gzread(f, header, sizeof(header));
534         gzread(f, &version, sizeof(u32));
535         gzread(f, &hle, sizeof(boolean));
536
537         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
538                 gzclose(f);
539                 return -1;
540         }
541         Config.HLE = hle;
542
543         if (Config.HLE)
544                 psxBiosInit();
545
546         psxCpu->Reset();
547         gzseek(f, 128 * 96 * 3, SEEK_CUR);
548
549         gzread(f, psxM, 0x00200000);
550         gzread(f, psxR, 0x00080000);
551         gzread(f, psxH, 0x00010000);
552         gzread(f, (void *)&psxRegs, sizeof(psxRegs));
553
554         if (Config.HLE)
555                 psxBiosFreeze(0);
556
557         // gpu
558         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
559         gzread(f, gpufP, sizeof(GPUFreeze_t));
560         GPU_freeze(0, gpufP);
561         free(gpufP);
562
563         // spu
564         gzread(f, &Size, 4);
565         spufP = (SPUFreeze_t *)malloc(Size);
566         gzread(f, spufP, Size);
567         SPU_freeze(0, spufP);
568         free(spufP);
569
570         sioFreeze(f, 0);
571         cdrFreeze(f, 0);
572         psxHwFreeze(f, 0);
573         psxRcntFreeze(f, 0);
574         mdecFreeze(f, 0);
575
576         gzclose(f);
577         new_dyna_restore();
578
579         return 0;
580 }
581
582 int CheckState(const char *file) {
583         gzFile f;
584         char header[32];
585         u32 version;
586         boolean hle;
587
588         f = gzopen(file, "rb");
589         if (f == NULL) return -1;
590
591         gzread(f, header, sizeof(header));
592         gzread(f, &version, sizeof(u32));
593         gzread(f, &hle, sizeof(boolean));
594
595         gzclose(f);
596
597         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
598                 return -1;
599
600         return 0;
601 }
602
603 // NET Function Helpers
604
605 int SendPcsxInfo() {
606         if (NET_recvData == NULL || NET_sendData == NULL)
607                 return 0;
608
609         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
610         NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
611         NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
612         NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
613         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
614         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
615
616         return 0;
617 }
618
619 int RecvPcsxInfo() {
620         int tmp;
621
622         if (NET_recvData == NULL || NET_sendData == NULL)
623                 return 0;
624
625         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
626         NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
627         NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
628         NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
629         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
630
631         SysUpdate();
632
633         tmp = Config.Cpu;
634         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
635         if (tmp != Config.Cpu) {
636                 psxCpu->Shutdown();
637 #ifdef PSXREC
638                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
639                 else psxCpu = &psxRec;
640 #else
641                 psxCpu = &psxInt;
642 #endif
643                 if (psxCpu->Init() == -1) {
644                         SysClose(); return -1;
645                 }
646                 psxCpu->Reset();
647         }
648
649         return 0;
650 }
651
652 // remove the leading and trailing spaces in a string
653 void trim(char *str) {
654         int pos = 0;
655         char *dest = str;
656
657         // skip leading blanks
658         while (str[pos] <= ' ' && str[pos] > 0)
659                 pos++;
660
661         while (str[pos]) {
662                 *(dest++) = str[pos];
663                 pos++;
664         }
665
666         *(dest--) = '\0'; // store the null
667
668         // remove trailing blanks
669         while (dest >= str && *dest <= ' ' && *dest > 0)
670                 *(dest--) = '\0';
671 }
672
673 // lookup table for crc calculation
674 static unsigned short crctab[256] = {
675         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
676         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
677         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
678         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
679         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
680         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
681         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
682         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
683         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
684         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
685         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
686         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
687         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
688         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
689         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
690         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
691         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
692         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
693         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
694         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
695         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
696         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
697         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
698         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
699         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
700         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
701         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
702         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
703         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
704 };
705
706 u16 calcCrc(u8 *d, int len) {
707         u16 crc = 0;
708         int i;
709
710         for (i = 0; i < len; i++) {
711                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
712         }
713
714         return ~crc;
715 }