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