1 /***************************************************************************
2 * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team *
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. *
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. *
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 ***************************************************************************/
21 * Miscellaneous functions, including savestates and CD-ROM loading.
32 char CdromId[10] = "";
33 char CdromLabel[33] = "";
35 // PSX Executable types
41 #define ISODCL(from, to) (to - from + 1)
43 struct iso_directory_record {
44 char length [ISODCL (1, 1)]; /* 711 */
45 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
46 char extent [ISODCL (3, 10)]; /* 733 */
47 char size [ISODCL (11, 18)]; /* 733 */
48 char date [ISODCL (19, 25)]; /* 7 by 711 */
49 char flags [ISODCL (26, 26)];
50 char file_unit_size [ISODCL (27, 27)]; /* 711 */
51 char interleave [ISODCL (28, 28)]; /* 711 */
52 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
53 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
57 void mmssdd( char *b, char *p )
61 unsigned char *u = (void *)b;
62 int block = (u[3] << 24) | (u[2] << 16) | (u[1] << 8) | u[0];
63 #elif defined(__BIGENDIAN__)
64 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
66 int block = *((int*)b);
70 m = block / 4500; // minutes
71 block = block - m * 4500; // minutes rest
72 s = block / 75; // seconds
73 d = block - s * 75; // seconds rest
75 m = ((m / 10) << 4) | m % 10;
76 s = ((s / 10) << 4) | s % 10;
77 d = ((d / 10) << 4) | d % 10;
85 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
90 if (time[1] == 60) { \
95 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
98 if (CDR_readTrack(time) == -1) return -1; \
99 buf = (void *)CDR_getBuffer(); \
100 if (buf == NULL) return -1; \
101 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
103 #define READDIR(_dir) \
105 memcpy(_dir, buf + 12, 2048); \
109 memcpy(_dir + 2048, buf + 12, 2048);
111 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
112 struct iso_directory_record *dir;
118 // only try to scan if a filename is given
119 if (!strlen(filename)) return -1;
123 dir = (struct iso_directory_record*) &mdir[i];
124 if (dir->length[0] == 0) {
127 i += (u8)dir->length[0];
129 if (dir->flags[0] & 0x2) { // it's a dir
130 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
131 if (filename[dir->name_len[0]] != '\\') continue;
133 filename += dir->name_len[0] + 1;
135 mmssdd(dir->extent, (char *)time);
141 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
142 mmssdd(dir->extent, (char *)time);
151 static const unsigned int gpu_ctl_def[] = {
152 0x00000000, 0x01000000, 0x03000000, 0x04000000,
153 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
156 static const unsigned int gpu_data_def[] = {
157 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
158 0xe5001000, 0xe6000000,
159 0x02000000, 0x00000000, 0x01ff03ff,
162 static void fake_bios_gpu_setup(void)
166 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
167 GPU_writeStatus(gpu_ctl_def[i]);
169 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
170 GPU_writeData(gpu_data_def[i]);
175 struct iso_directory_record *dir;
180 // not the best place to do it, but since BIOS boot logo killer
181 // is just below, do it here
182 fake_bios_gpu_setup();
186 psxRegs.pc = psxRegs.GPR.n.ra;
190 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
194 // skip head and sub, and go to the root directory record
195 dir = (struct iso_directory_record*) &buf[12+156];
197 mmssdd(dir->extent, (char*)time);
201 // Load SYSTEM.CNF and scan for the main executable
202 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
203 // if SYSTEM.CNF is missing, start an existing PSX.EXE
204 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
209 // read the SYSTEM.CNF
212 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
213 if (GetCdromFile(mdir, time, exename) == -1) {
214 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
215 if (GetCdromFile(mdir, time, exename) == -1) {
216 char *ptr = strstr((char *)buf + 12, "cdrom:");
219 while (*ptr == '\\' || *ptr == '/') ptr++;
220 strncpy(exename, ptr, 255);
223 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
225 if (GetCdromFile(mdir, time, exename) == -1)
232 // Read the EXE-Header
236 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
238 psxRegs.pc = SWAP32(tmpHead.pc0);
239 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
240 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
241 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
243 tmpHead.t_size = SWAP32(tmpHead.t_size);
244 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
246 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
248 // Read the rest of the main executable
249 while (tmpHead.t_size & ~2047) {
250 void *ptr = (void *)PSXM(tmpHead.t_addr);
255 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
257 tmpHead.t_size -= 2048;
258 tmpHead.t_addr += 2048;
264 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
265 struct iso_directory_record *dir;
272 sscanf(filename, "cdrom:\\%255s", exename);
274 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
278 // skip head and sub, and go to the root directory record
279 dir = (struct iso_directory_record *)&buf[12 + 156];
281 mmssdd(dir->extent, (char*)time);
285 if (GetCdromFile(mdir, time, exename) == -1) return -1;
289 memcpy(head, buf + 12, sizeof(EXE_HEADER));
293 psxCpu->Clear(addr, size / 4);
295 while (size & ~2047) {
301 memcpy(mem, buf + 12, 2048);
311 struct iso_directory_record *dir;
312 unsigned char time[4];
314 unsigned char mdir[4096];
322 time[2] = itob(0x10);
326 memset(CdromLabel, 0, sizeof(CdromLabel));
327 memset(CdromId, 0, sizeof(CdromId));
328 memset(exename, 0, sizeof(exename));
330 strncpy(CdromLabel, buf + 52, 32);
332 // skip head and sub, and go to the root directory record
333 dir = (struct iso_directory_record *)&buf[12 + 156];
335 mmssdd(dir->extent, (char *)time);
339 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
342 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
343 if (GetCdromFile(mdir, time, exename) == -1) {
344 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
345 if (GetCdromFile(mdir, time, exename) == -1) {
346 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
349 while (*ptr == '\\' || *ptr == '/') ptr++;
350 strncpy(exename, ptr, 255);
353 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
355 if (GetCdromFile(mdir, time, exename) == -1)
356 return -1; // main executable not found
361 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
362 strcpy(exename, "PSX.EXE;1");
363 strcpy(CdromId, "SLUS99999");
365 return -1; // SYSTEM.CNF and PSX.EXE not found
367 if (CdromId[0] == '\0') {
368 len = strlen(exename);
370 for (i = 0; i < len; ++i) {
371 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
373 if (isalnum(exename[i]))
374 CdromId[c++] = exename[i];
378 if (CdromId[0] == '\0')
379 strcpy(CdromId, "SLUS99999");
381 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
383 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
384 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
385 !strncmp(CdromId, "DTLS3035", 8) ||
386 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
387 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
388 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
389 Config.PsxType = PSX_TYPE_PAL; // pal
390 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
393 if (CdromLabel[0] == ' ') {
394 strncpy(CdromLabel, CdromId, 9);
396 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
397 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
398 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
407 static int PSXGetFileType(FILE *f) {
408 unsigned long current;
414 fseek(f, 0L, SEEK_SET);
415 fread(mybuf, 2048, 1, f);
416 fseek(f, current, SEEK_SET);
418 exe_hdr = (EXE_HEADER *)mybuf;
419 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
422 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
425 coff_hdr = (FILHDR *)mybuf;
426 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
432 // temporary pandora workaround..
434 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
439 tmp = malloc(size * nmemb);
441 ret = fread(tmp, size, nmemb, stream);
442 memcpy(ptr, tmp, size * nmemb);
448 int Load(const char *ExePath) {
454 u32 section_address, section_size;
457 strncpy(CdromId, "SLUS99999", 9);
458 strncpy(CdromLabel, "SLUS_999.99", 11);
460 tmpFile = fopen(ExePath, "rb");
461 if (tmpFile == NULL) {
462 SysPrintf(_("Error opening file: %s.\n"), ExePath);
465 type = PSXGetFileType(tmpFile);
468 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
469 section_address = SWAP32(tmpHead.t_addr);
470 section_size = SWAP32(tmpHead.t_size);
471 mem = PSXM(section_address);
473 fseek(tmpFile, 0x800, SEEK_SET);
474 fread_to_ram(mem, section_size, 1, tmpFile);
475 psxCpu->Clear(section_address, section_size / 4);
478 psxRegs.pc = SWAP32(tmpHead.pc0);
479 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
480 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
481 if (psxRegs.GPR.n.sp == 0)
482 psxRegs.GPR.n.sp = 0x801fff00;
486 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
488 fread(&opcode, 1, 1, tmpFile);
490 case 1: /* Section loading */
491 fread(§ion_address, 4, 1, tmpFile);
492 fread(§ion_size, 4, 1, tmpFile);
493 section_address = SWAPu32(section_address);
494 section_size = SWAPu32(section_size);
496 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
498 mem = PSXM(section_address);
500 fread_to_ram(mem, section_size, 1, tmpFile);
501 psxCpu->Clear(section_address, section_size / 4);
504 case 3: /* register loading (PC only?) */
505 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
506 fread(&psxRegs.pc, 4, 1, tmpFile);
507 psxRegs.pc = SWAPu32(psxRegs.pc);
509 case 0: /* End of file */
512 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
516 } while (opcode != 0 && retval == 0);
519 SysPrintf(_("COFF files not supported.\n"));
523 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
524 SysPrintf(_("(did you forget -cdfile ?)\n"));
532 CdromLabel[0] = '\0';
540 static void *zlib_open(const char *name, const char *mode)
542 return gzopen(name, mode);
545 static int zlib_read(void *file, void *buf, u32 len)
547 return gzread(file, buf, len);
550 static int zlib_write(void *file, const void *buf, u32 len)
552 return gzwrite(file, buf, len);
555 static long zlib_seek(void *file, long offs, int whence)
557 return gzseek(file, offs, whence);
560 static void zlib_close(void *file)
565 struct PcsxSaveFuncs SaveFuncs = {
566 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
569 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
571 // Savestate Versioning!
572 // If you make changes to the savestate version, please increment the value below.
573 static const u32 SaveVersion = 0x8b410006;
575 int SaveState(const char *file) {
582 f = SaveFuncs.open(file, "wb");
583 if (f == NULL) return -1;
585 new_dyna_before_save();
587 SaveFuncs.write(f, (void *)PcsxHeader, 32);
588 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
589 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
591 pMem = (unsigned char *)malloc(128 * 96 * 3);
592 if (pMem == NULL) return -1;
593 GPU_getScreenPic(pMem);
594 SaveFuncs.write(f, pMem, 128 * 96 * 3);
600 SaveFuncs.write(f, psxM, 0x00200000);
601 SaveFuncs.write(f, psxR, 0x00080000);
602 SaveFuncs.write(f, psxH, 0x00010000);
603 SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
606 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
607 gpufP->ulFreezeVersion = 1;
608 GPU_freeze(1, gpufP);
609 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
613 spufP = (SPUFreeze_t *) malloc(16);
614 SPU_freeze(2, spufP, psxRegs.cycle);
615 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
617 spufP = (SPUFreeze_t *) malloc(Size);
618 SPU_freeze(1, spufP, psxRegs.cycle);
619 SaveFuncs.write(f, spufP, Size);
627 new_dyna_freeze(f, 1);
631 new_dyna_after_save();
636 int LoadState(const char *file) {
645 f = SaveFuncs.open(file, "rb");
646 if (f == NULL) return -1;
648 SaveFuncs.read(f, header, sizeof(header));
649 SaveFuncs.read(f, &version, sizeof(u32));
650 SaveFuncs.read(f, &hle, sizeof(boolean));
652 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
662 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
664 SaveFuncs.read(f, psxM, 0x00200000);
665 SaveFuncs.read(f, psxR, 0x00080000);
666 SaveFuncs.read(f, psxH, 0x00010000);
667 SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
673 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
674 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
675 GPU_freeze(0, gpufP);
677 if (HW_GPU_STATUS == 0)
678 HW_GPU_STATUS = GPU_readStatus();
681 SaveFuncs.read(f, &Size, 4);
682 spufP = (SPUFreeze_t *)malloc(Size);
683 SaveFuncs.read(f, spufP, Size);
684 SPU_freeze(0, spufP, psxRegs.cycle);
692 new_dyna_freeze(f, 0);
699 int CheckState(const char *file) {
705 f = SaveFuncs.open(file, "rb");
706 if (f == NULL) return -1;
708 SaveFuncs.read(f, header, sizeof(header));
709 SaveFuncs.read(f, &version, sizeof(u32));
710 SaveFuncs.read(f, &hle, sizeof(boolean));
714 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
720 // NET Function Helpers
723 if (NET_recvData == NULL || NET_sendData == NULL)
726 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
727 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
728 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
729 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
730 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
731 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
739 if (NET_recvData == NULL || NET_sendData == NULL)
742 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
743 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
744 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
745 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
746 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
751 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
752 if (tmp != Config.Cpu) {
755 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
756 else psxCpu = &psxRec;
760 if (psxCpu->Init() == -1) {
761 SysClose(); return -1;
769 // remove the leading and trailing spaces in a string
770 void trim(char *str) {
774 // skip leading blanks
775 while (str[pos] <= ' ' && str[pos] > 0)
779 *(dest++) = str[pos];
783 *(dest--) = '\0'; // store the null
785 // remove trailing blanks
786 while (dest >= str && *dest <= ' ' && *dest > 0)
790 // lookup table for crc calculation
791 static unsigned short crctab[256] = {
792 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
793 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
794 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
795 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
796 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
797 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
798 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
799 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
800 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
801 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
802 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
803 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
804 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
805 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
806 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
807 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
808 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
809 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
810 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
811 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
812 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
813 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
814 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
815 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
816 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
817 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
818 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
819 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
820 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
823 u16 calcCrc(u8 *d, int len) {
827 for (i = 0; i < len; i++) {
828 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);