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);
249 // Read the rest of the main executable
250 while (tmpHead.t_size & ~2047) {
251 void *ptr = (void *)PSXM(tmpHead.t_addr);
256 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
258 tmpHead.t_size -= 2048;
259 tmpHead.t_addr += 2048;
265 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
266 struct iso_directory_record *dir;
273 sscanf(filename, "cdrom:\\%255s", exename);
275 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
279 // skip head and sub, and go to the root directory record
280 dir = (struct iso_directory_record *)&buf[12 + 156];
282 mmssdd(dir->extent, (char*)time);
286 if (GetCdromFile(mdir, time, exename) == -1) return -1;
290 memcpy(head, buf + 12, sizeof(EXE_HEADER));
294 psxCpu->Clear(addr, size / 4);
297 while (size & ~2047) {
303 memcpy(mem, buf + 12, 2048);
313 struct iso_directory_record *dir;
314 unsigned char time[4];
316 unsigned char mdir[4096];
324 time[2] = itob(0x10);
328 memset(CdromLabel, 0, sizeof(CdromLabel));
329 memset(CdromId, 0, sizeof(CdromId));
330 memset(exename, 0, sizeof(exename));
332 strncpy(CdromLabel, buf + 52, 32);
334 // skip head and sub, and go to the root directory record
335 dir = (struct iso_directory_record *)&buf[12 + 156];
337 mmssdd(dir->extent, (char *)time);
341 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
344 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
345 if (GetCdromFile(mdir, time, exename) == -1) {
346 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
347 if (GetCdromFile(mdir, time, exename) == -1) {
348 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
351 while (*ptr == '\\' || *ptr == '/') ptr++;
352 strncpy(exename, ptr, 255);
355 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
357 if (GetCdromFile(mdir, time, exename) == -1)
358 return -1; // main executable not found
363 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
364 strcpy(exename, "PSX.EXE;1");
365 strcpy(CdromId, "SLUS99999");
367 return -1; // SYSTEM.CNF and PSX.EXE not found
369 if (CdromId[0] == '\0') {
370 len = strlen(exename);
372 for (i = 0; i < len; ++i) {
373 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
375 if (isalnum(exename[i]))
376 CdromId[c++] = exename[i];
380 if (CdromId[0] == '\0')
381 strcpy(CdromId, "SLUS99999");
383 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
385 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
386 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
387 !strncmp(CdromId, "DTLS3035", 8) ||
388 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
389 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
390 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
391 Config.PsxType = PSX_TYPE_PAL; // pal
392 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
395 if (CdromLabel[0] == ' ') {
396 strncpy(CdromLabel, CdromId, 9);
398 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
399 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
400 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
409 static int PSXGetFileType(FILE *f) {
410 unsigned long current;
416 fseek(f, 0L, SEEK_SET);
417 fread(mybuf, 2048, 1, f);
418 fseek(f, current, SEEK_SET);
420 exe_hdr = (EXE_HEADER *)mybuf;
421 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
424 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
427 coff_hdr = (FILHDR *)mybuf;
428 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
434 // temporary pandora workaround..
436 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
441 tmp = malloc(size * nmemb);
443 ret = fread(tmp, size, nmemb, stream);
444 memcpy(ptr, tmp, size * nmemb);
450 int Load(const char *ExePath) {
456 u32 section_address, section_size;
459 strncpy(CdromId, "SLUS99999", 9);
460 strncpy(CdromLabel, "SLUS_999.99", 11);
462 tmpFile = fopen(ExePath, "rb");
463 if (tmpFile == NULL) {
464 SysPrintf(_("Error opening file: %s.\n"), ExePath);
467 type = PSXGetFileType(tmpFile);
470 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
471 section_address = SWAP32(tmpHead.t_addr);
472 section_size = SWAP32(tmpHead.t_size);
473 mem = PSXM(section_address);
475 fseek(tmpFile, 0x800, SEEK_SET);
476 fread_to_ram(mem, section_size, 1, tmpFile);
477 psxCpu->Clear(section_address, section_size / 4);
480 psxRegs.pc = SWAP32(tmpHead.pc0);
481 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
482 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
483 if (psxRegs.GPR.n.sp == 0)
484 psxRegs.GPR.n.sp = 0x801fff00;
488 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
490 fread(&opcode, 1, 1, tmpFile);
492 case 1: /* Section loading */
493 fread(§ion_address, 4, 1, tmpFile);
494 fread(§ion_size, 4, 1, tmpFile);
495 section_address = SWAPu32(section_address);
496 section_size = SWAPu32(section_size);
498 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
500 mem = PSXM(section_address);
502 fread_to_ram(mem, section_size, 1, tmpFile);
503 psxCpu->Clear(section_address, section_size / 4);
506 case 3: /* register loading (PC only?) */
507 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
508 fread(&psxRegs.pc, 4, 1, tmpFile);
509 psxRegs.pc = SWAPu32(psxRegs.pc);
511 case 0: /* End of file */
514 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
518 } while (opcode != 0 && retval == 0);
521 SysPrintf(_("COFF files not supported.\n"));
525 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
526 SysPrintf(_("(did you forget -cdfile ?)\n"));
534 CdromLabel[0] = '\0';
542 static void *zlib_open(const char *name, const char *mode)
544 return gzopen(name, mode);
547 static int zlib_read(void *file, void *buf, u32 len)
549 return gzread(file, buf, len);
552 static int zlib_write(void *file, const void *buf, u32 len)
554 return gzwrite(file, buf, len);
557 static long zlib_seek(void *file, long offs, int whence)
559 return gzseek(file, offs, whence);
562 static void zlib_close(void *file)
567 struct PcsxSaveFuncs SaveFuncs = {
568 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
571 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
573 // Savestate Versioning!
574 // If you make changes to the savestate version, please increment the value below.
575 static const u32 SaveVersion = 0x8b410006;
577 int SaveState(const char *file) {
584 f = SaveFuncs.open(file, "wb");
585 if (f == NULL) return -1;
587 new_dyna_before_save();
589 SaveFuncs.write(f, (void *)PcsxHeader, 32);
590 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
591 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
593 pMem = (unsigned char *)malloc(128 * 96 * 3);
594 if (pMem == NULL) return -1;
595 GPU_getScreenPic(pMem);
596 SaveFuncs.write(f, pMem, 128 * 96 * 3);
602 SaveFuncs.write(f, psxM, 0x00200000);
603 SaveFuncs.write(f, psxR, 0x00080000);
604 SaveFuncs.write(f, psxH, 0x00010000);
605 SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
608 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
609 gpufP->ulFreezeVersion = 1;
610 GPU_freeze(1, gpufP);
611 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
615 spufP = (SPUFreeze_t *) malloc(16);
616 SPU_freeze(2, spufP, psxRegs.cycle);
617 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
619 spufP = (SPUFreeze_t *) malloc(Size);
620 SPU_freeze(1, spufP, psxRegs.cycle);
621 SaveFuncs.write(f, spufP, Size);
629 new_dyna_freeze(f, 1);
633 new_dyna_after_save();
638 int LoadState(const char *file) {
647 f = SaveFuncs.open(file, "rb");
648 if (f == NULL) return -1;
650 SaveFuncs.read(f, header, sizeof(header));
651 SaveFuncs.read(f, &version, sizeof(u32));
652 SaveFuncs.read(f, &hle, sizeof(boolean));
654 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
664 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
666 SaveFuncs.read(f, psxM, 0x00200000);
667 SaveFuncs.read(f, psxR, 0x00080000);
668 SaveFuncs.read(f, psxH, 0x00010000);
669 SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
675 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
676 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
677 GPU_freeze(0, gpufP);
679 if (HW_GPU_STATUS == 0)
680 HW_GPU_STATUS = GPU_readStatus();
683 SaveFuncs.read(f, &Size, 4);
684 spufP = (SPUFreeze_t *)malloc(Size);
685 SaveFuncs.read(f, spufP, Size);
686 SPU_freeze(0, spufP, psxRegs.cycle);
694 new_dyna_freeze(f, 0);
701 int CheckState(const char *file) {
707 f = SaveFuncs.open(file, "rb");
708 if (f == NULL) return -1;
710 SaveFuncs.read(f, header, sizeof(header));
711 SaveFuncs.read(f, &version, sizeof(u32));
712 SaveFuncs.read(f, &hle, sizeof(boolean));
716 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
722 // NET Function Helpers
725 if (NET_recvData == NULL || NET_sendData == NULL)
728 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
729 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
730 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
731 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
732 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
733 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
741 if (NET_recvData == NULL || NET_sendData == NULL)
744 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
745 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
746 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
747 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
748 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
753 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
754 if (tmp != Config.Cpu) {
757 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
758 else psxCpu = &psxRec;
762 if (psxCpu->Init() == -1) {
763 SysClose(); return -1;
771 // remove the leading and trailing spaces in a string
772 void trim(char *str) {
776 // skip leading blanks
777 while (str[pos] <= ' ' && str[pos] > 0)
781 *(dest++) = str[pos];
785 *(dest--) = '\0'; // store the null
787 // remove trailing blanks
788 while (dest >= str && *dest <= ' ' && *dest > 0)
792 // lookup table for crc calculation
793 static unsigned short crctab[256] = {
794 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
795 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
796 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
797 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
798 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
799 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
800 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
801 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
802 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
803 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
804 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
805 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
806 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
807 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
808 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
809 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
810 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
811 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
812 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
813 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
814 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
815 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
816 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
817 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
818 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
819 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
820 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
821 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
822 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
825 u16 calcCrc(u8 *d, int len) {
829 for (i = 0; i < len; i++) {
830 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);