drc: initial cop2/gte implementation (works, mostly)
[pcsx_rearmed.git] / libpcsxcore / misc.c
CommitLineData
ef79bbde
P
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
29char CdromId[10] = "";
30char 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
40struct 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
54void 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
104int 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
142int 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
226int 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
266int 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
350static 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
375int 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
455static 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.
459static const u32 SaveVersion = 0x8b410004;
460
461int 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
517int 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
573int 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
596int 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
610int 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
644void 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
665static 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
697u16 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}