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