add OMAP layer, also preliminary menu, hud and input support
[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)
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
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.
461static const u32 SaveVersion = 0x8b410004;
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
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
519int 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
575int 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
598int 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
612int 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
646void 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
667static 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
699u16 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}