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