add default skin
[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 (strstr(exename, "ES") != NULL)
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 = 0x8b410005;
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 || hle != Config.HLE) {
538                 gzclose(f);
539                 return -1;
540         }
541
542         psxCpu->Reset();
543         gzseek(f, 128 * 96 * 3, SEEK_CUR);
544
545         gzread(f, psxM, 0x00200000);
546         gzread(f, psxR, 0x00080000);
547         gzread(f, psxH, 0x00010000);
548         gzread(f, (void *)&psxRegs, sizeof(psxRegs));
549
550         if (Config.HLE)
551                 psxBiosFreeze(0);
552
553         // gpu
554         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
555         gzread(f, gpufP, sizeof(GPUFreeze_t));
556         GPU_freeze(0, gpufP);
557         free(gpufP);
558
559         // spu
560         gzread(f, &Size, 4);
561         spufP = (SPUFreeze_t *)malloc(Size);
562         gzread(f, spufP, Size);
563         SPU_freeze(0, spufP);
564         free(spufP);
565
566         sioFreeze(f, 0);
567         cdrFreeze(f, 0);
568         psxHwFreeze(f, 0);
569         psxRcntFreeze(f, 0);
570         mdecFreeze(f, 0);
571
572         gzclose(f);
573         new_dyna_restore();
574
575         return 0;
576 }
577
578 int CheckState(const char *file) {
579         gzFile f;
580         char header[32];
581         u32 version;
582         boolean hle;
583
584         f = gzopen(file, "rb");
585         if (f == NULL) return -1;
586
587         gzread(f, header, sizeof(header));
588         gzread(f, &version, sizeof(u32));
589         gzread(f, &hle, sizeof(boolean));
590
591         gzclose(f);
592
593         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion || hle != Config.HLE)
594                 return -1;
595
596         return 0;
597 }
598
599 // NET Function Helpers
600
601 int SendPcsxInfo() {
602         if (NET_recvData == NULL || NET_sendData == NULL)
603                 return 0;
604
605         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
606         NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
607         NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
608         NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
609         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
610         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
611
612         return 0;
613 }
614
615 int RecvPcsxInfo() {
616         int tmp;
617
618         if (NET_recvData == NULL || NET_sendData == NULL)
619                 return 0;
620
621         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
622         NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
623         NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
624         NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
625         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
626
627         SysUpdate();
628
629         tmp = Config.Cpu;
630         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
631         if (tmp != Config.Cpu) {
632                 psxCpu->Shutdown();
633 #ifdef PSXREC
634                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
635                 else psxCpu = &psxRec;
636 #else
637                 psxCpu = &psxInt;
638 #endif
639                 if (psxCpu->Init() == -1) {
640                         SysClose(); return -1;
641                 }
642                 psxCpu->Reset();
643         }
644
645         return 0;
646 }
647
648 // remove the leading and trailing spaces in a string
649 void trim(char *str) {
650         int pos = 0;
651         char *dest = str;
652
653         // skip leading blanks
654         while (str[pos] <= ' ' && str[pos] > 0)
655                 pos++;
656
657         while (str[pos]) {
658                 *(dest++) = str[pos];
659                 pos++;
660         }
661
662         *(dest--) = '\0'; // store the null
663
664         // remove trailing blanks
665         while (dest >= str && *dest <= ' ' && *dest > 0)
666                 *(dest--) = '\0';
667 }
668
669 // lookup table for crc calculation
670 static unsigned short crctab[256] = {
671         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
672         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
673         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
674         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
675         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
676         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
677         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
678         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
679         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
680         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
681         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
682         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
683         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
684         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
685         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
686         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
687         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
688         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
689         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
690         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
691         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
692         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
693         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
694         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
695         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
696         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
697         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
698         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
699         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
700 };
701
702 u16 calcCrc(u8 *d, int len) {
703         u16 crc = 0;
704         int i;
705
706         for (i = 0; i < len; i++) {
707                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
708         }
709
710         return ~crc;
711 }