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.
33 char CdromId[10] = "";
34 char CdromLabel[33] = "";
36 // PSX Executable types
42 #define ISODCL(from, to) (to - from + 1)
44 struct iso_directory_record {
45 char length [ISODCL (1, 1)]; /* 711 */
46 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
47 char extent [ISODCL (3, 10)]; /* 733 */
48 char size [ISODCL (11, 18)]; /* 733 */
49 char date [ISODCL (19, 25)]; /* 7 by 711 */
50 char flags [ISODCL (26, 26)];
51 char file_unit_size [ISODCL (27, 27)]; /* 711 */
52 char interleave [ISODCL (28, 28)]; /* 711 */
53 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
54 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
58 static void mmssdd( char *b, char *p )
61 unsigned char *ub = (void *)b;
62 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
65 m = block / 4500; // minutes
66 block = block - m * 4500; // minutes rest
67 s = block / 75; // seconds
68 d = block - s * 75; // seconds rest
70 m = ((m / 10) << 4) | m % 10;
71 s = ((s / 10) << 4) | s % 10;
72 d = ((d / 10) << 4) | d % 10;
80 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
85 if (time[1] == 60) { \
90 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
93 if (CDR_readTrack(time) == -1) return -1; \
94 buf = (void *)CDR_getBuffer(); \
95 if (buf == NULL) return -1; \
96 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
98 #define READDIR(_dir) \
100 memcpy(_dir, buf + 12, 2048); \
104 memcpy(_dir + 2048, buf + 12, 2048);
106 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
107 struct iso_directory_record *dir;
113 // only try to scan if a filename is given
114 if (!strlen(filename)) return -1;
118 dir = (struct iso_directory_record*) &mdir[i];
119 if (dir->length[0] == 0) {
122 i += (u8)dir->length[0];
124 if (dir->flags[0] & 0x2) { // it's a dir
125 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
126 if (filename[dir->name_len[0]] != '\\') continue;
128 filename += dir->name_len[0] + 1;
130 mmssdd(dir->extent, (char *)time);
136 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
137 mmssdd(dir->extent, (char *)time);
146 static const unsigned int gpu_ctl_def[] = {
147 0x00000000, 0x01000000, 0x03000000, 0x04000000,
148 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
151 static const unsigned int gpu_data_def[] = {
152 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
153 0xe5001000, 0xe6000000,
154 0x02000000, 0x00000000, 0x01ff03ff,
157 static void fake_bios_gpu_setup(void)
161 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
162 GPU_writeStatus(gpu_ctl_def[i]);
164 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
165 GPU_writeData(gpu_data_def[i]);
170 struct iso_directory_record *dir;
175 // not the best place to do it, but since BIOS boot logo killer
176 // is just below, do it here
177 fake_bios_gpu_setup();
181 psxRegs.pc = psxRegs.GPR.n.ra;
185 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
189 // skip head and sub, and go to the root directory record
190 dir = (struct iso_directory_record*) &buf[12+156];
192 mmssdd(dir->extent, (char*)time);
196 // Load SYSTEM.CNF and scan for the main executable
197 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
198 // if SYSTEM.CNF is missing, start an existing PSX.EXE
199 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
204 // read the SYSTEM.CNF
207 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
208 if (GetCdromFile(mdir, time, exename) == -1) {
209 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
210 if (GetCdromFile(mdir, time, exename) == -1) {
211 char *ptr = strstr((char *)buf + 12, "cdrom:");
214 while (*ptr == '\\' || *ptr == '/') ptr++;
215 strncpy(exename, ptr, 255);
218 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
220 if (GetCdromFile(mdir, time, exename) == -1)
227 // Read the EXE-Header
231 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
233 psxRegs.pc = SWAP32(tmpHead.pc0);
234 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
235 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
236 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
238 tmpHead.t_size = SWAP32(tmpHead.t_size);
239 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
241 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
244 // Read the rest of the main executable
245 while (tmpHead.t_size & ~2047) {
246 void *ptr = (void *)PSXM(tmpHead.t_addr);
251 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
253 tmpHead.t_size -= 2048;
254 tmpHead.t_addr += 2048;
260 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
261 struct iso_directory_record *dir;
268 sscanf(filename, "cdrom:\\%255s", exename);
270 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
274 // skip head and sub, and go to the root directory record
275 dir = (struct iso_directory_record *)&buf[12 + 156];
277 mmssdd(dir->extent, (char*)time);
281 if (GetCdromFile(mdir, time, exename) == -1) return -1;
285 memcpy(head, buf + 12, sizeof(EXE_HEADER));
289 psxCpu->Clear(addr, size / 4);
292 while (size & ~2047) {
298 memcpy(mem, buf + 12, 2048);
308 struct iso_directory_record *dir;
309 unsigned char time[4];
311 unsigned char mdir[4096];
319 time[2] = itob(0x10);
323 memset(CdromLabel, 0, sizeof(CdromLabel));
324 memset(CdromId, 0, sizeof(CdromId));
325 memset(exename, 0, sizeof(exename));
327 strncpy(CdromLabel, buf + 52, 32);
329 // skip head and sub, and go to the root directory record
330 dir = (struct iso_directory_record *)&buf[12 + 156];
332 mmssdd(dir->extent, (char *)time);
336 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
339 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
340 if (GetCdromFile(mdir, time, exename) == -1) {
341 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
342 if (GetCdromFile(mdir, time, exename) == -1) {
343 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
346 while (*ptr == '\\' || *ptr == '/') ptr++;
347 strncpy(exename, ptr, 255);
350 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
352 if (GetCdromFile(mdir, time, exename) == -1)
353 return -1; // main executable not found
358 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
359 strcpy(exename, "PSX.EXE;1");
360 strcpy(CdromId, "SLUS99999");
362 return -1; // SYSTEM.CNF and PSX.EXE not found
364 if (CdromId[0] == '\0') {
365 len = strlen(exename);
367 for (i = 0; i < len; ++i) {
368 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
370 if (isalnum(exename[i]))
371 CdromId[c++] = exename[i];
375 if (CdromId[0] == '\0')
376 strcpy(CdromId, "SLUS99999");
378 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
380 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
381 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
382 !strncmp(CdromId, "DTLS3035", 8) ||
383 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
384 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
385 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
386 Config.PsxType = PSX_TYPE_PAL; // pal
387 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
390 if (CdromLabel[0] == ' ') {
391 strncpy(CdromLabel, CdromId, 9);
393 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
394 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
395 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
404 static int PSXGetFileType(FILE *f) {
405 unsigned long current;
411 fseek(f, 0L, SEEK_SET);
412 fread(mybuf, 2048, 1, f);
413 fseek(f, current, SEEK_SET);
415 exe_hdr = (EXE_HEADER *)mybuf;
416 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
419 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
422 coff_hdr = (FILHDR *)mybuf;
423 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
429 // temporary pandora workaround..
431 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
436 tmp = malloc(size * nmemb);
438 ret = fread(tmp, size, nmemb, stream);
439 memcpy(ptr, tmp, size * nmemb);
445 int Load(const char *ExePath) {
451 u32 section_address, section_size;
454 strncpy(CdromId, "SLUS99999", 9);
455 strncpy(CdromLabel, "SLUS_999.99", 11);
457 tmpFile = fopen(ExePath, "rb");
458 if (tmpFile == NULL) {
459 SysPrintf(_("Error opening file: %s.\n"), ExePath);
462 type = PSXGetFileType(tmpFile);
465 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
466 section_address = SWAP32(tmpHead.t_addr);
467 section_size = SWAP32(tmpHead.t_size);
468 mem = PSXM(section_address);
470 fseek(tmpFile, 0x800, SEEK_SET);
471 fread_to_ram(mem, section_size, 1, tmpFile);
472 psxCpu->Clear(section_address, section_size / 4);
475 psxRegs.pc = SWAP32(tmpHead.pc0);
476 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
477 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
478 if (psxRegs.GPR.n.sp == 0)
479 psxRegs.GPR.n.sp = 0x801fff00;
483 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
485 fread(&opcode, 1, 1, tmpFile);
487 case 1: /* Section loading */
488 fread(§ion_address, 4, 1, tmpFile);
489 fread(§ion_size, 4, 1, tmpFile);
490 section_address = SWAPu32(section_address);
491 section_size = SWAPu32(section_size);
493 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
495 mem = PSXM(section_address);
497 fread_to_ram(mem, section_size, 1, tmpFile);
498 psxCpu->Clear(section_address, section_size / 4);
501 case 3: /* register loading (PC only?) */
502 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
503 fread(&psxRegs.pc, 4, 1, tmpFile);
504 psxRegs.pc = SWAPu32(psxRegs.pc);
506 case 0: /* End of file */
509 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
513 } while (opcode != 0 && retval == 0);
516 SysPrintf(_("COFF files not supported.\n"));
520 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
521 SysPrintf(_("(did you forget -cdfile ?)\n"));
529 CdromLabel[0] = '\0';
537 static void *zlib_open(const char *name, const char *mode)
539 return gzopen(name, mode);
542 static int zlib_read(void *file, void *buf, u32 len)
544 return gzread(file, buf, len);
547 static int zlib_write(void *file, const void *buf, u32 len)
549 return gzwrite(file, buf, len);
552 static long zlib_seek(void *file, long offs, int whence)
554 return gzseek(file, offs, whence);
557 static void zlib_close(void *file)
562 struct PcsxSaveFuncs SaveFuncs = {
563 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
566 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
568 // Savestate Versioning!
569 // If you make changes to the savestate version, please increment the value below.
570 static const u32 SaveVersion = 0x8b410006;
572 int SaveState(const char *file) {
579 f = SaveFuncs.open(file, "wb");
580 if (f == NULL) return -1;
582 new_dyna_before_save();
584 SaveFuncs.write(f, (void *)PcsxHeader, 32);
585 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
586 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
588 pMem = (unsigned char *)malloc(128 * 96 * 3);
589 if (pMem == NULL) return -1;
590 GPU_getScreenPic(pMem);
591 SaveFuncs.write(f, pMem, 128 * 96 * 3);
597 SaveFuncs.write(f, psxM, 0x00200000);
598 SaveFuncs.write(f, psxR, 0x00080000);
599 SaveFuncs.write(f, psxH, 0x00010000);
600 // only partial save of psxRegisters to maintain savestate compat
601 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
604 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
605 gpufP->ulFreezeVersion = 1;
606 GPU_freeze(1, gpufP);
607 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
611 spufP = (SPUFreeze_t *) malloc(16);
612 SPU_freeze(2, spufP, psxRegs.cycle);
613 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
615 spufP = (SPUFreeze_t *) malloc(Size);
616 SPU_freeze(1, spufP, psxRegs.cycle);
617 SaveFuncs.write(f, spufP, Size);
625 new_dyna_freeze(f, 1);
629 new_dyna_after_save();
634 int LoadState(const char *file) {
643 f = SaveFuncs.open(file, "rb");
644 if (f == NULL) return -1;
646 SaveFuncs.read(f, header, sizeof(header));
647 SaveFuncs.read(f, &version, sizeof(u32));
648 SaveFuncs.read(f, &hle, sizeof(boolean));
650 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
660 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
662 SaveFuncs.read(f, psxM, 0x00200000);
663 SaveFuncs.read(f, psxR, 0x00080000);
664 SaveFuncs.read(f, psxH, 0x00010000);
665 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
666 psxRegs.gteBusyCycle = psxRegs.cycle;
672 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
673 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
674 GPU_freeze(0, gpufP);
676 if (HW_GPU_STATUS == 0)
677 HW_GPU_STATUS = SWAP32(GPU_readStatus());
680 SaveFuncs.read(f, &Size, 4);
681 spufP = (SPUFreeze_t *)malloc(Size);
682 SaveFuncs.read(f, spufP, Size);
683 SPU_freeze(0, spufP, psxRegs.cycle);
691 new_dyna_freeze(f, 0);
698 int CheckState(const char *file) {
704 f = SaveFuncs.open(file, "rb");
705 if (f == NULL) return -1;
707 SaveFuncs.read(f, header, sizeof(header));
708 SaveFuncs.read(f, &version, sizeof(u32));
709 SaveFuncs.read(f, &hle, sizeof(boolean));
713 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
719 // NET Function Helpers
722 if (NET_recvData == NULL || NET_sendData == NULL)
725 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
726 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
727 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
728 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
729 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
730 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
738 if (NET_recvData == NULL || NET_sendData == NULL)
741 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
742 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
743 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
744 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
745 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
750 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
751 if (tmp != Config.Cpu) {
754 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
755 else psxCpu = &psxRec;
759 if (psxCpu->Init() == -1) {
760 SysClose(); return -1;
768 // remove the leading and trailing spaces in a string
769 void trim(char *str) {
773 // skip leading blanks
774 while (str[pos] <= ' ' && str[pos] > 0)
778 *(dest++) = str[pos];
782 *(dest--) = '\0'; // store the null
784 // remove trailing blanks
785 while (dest >= str && *dest <= ' ' && *dest > 0)
789 // lookup table for crc calculation
790 static unsigned short crctab[256] = {
791 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
792 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
793 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
794 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
795 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
796 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
797 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
798 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
799 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
800 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
801 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
802 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
803 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
804 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
805 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
806 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
807 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
808 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
809 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
810 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
811 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
812 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
813 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
814 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
815 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
816 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
817 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
818 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
819 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
822 u16 calcCrc(u8 *d, int len) {
826 for (i = 0; i < len; i++) {
827 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);