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