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.
31 char CdromId[10] = "";
32 char CdromLabel[33] = "";
34 // PSX Executable types
40 #define ISODCL(from, to) (to - from + 1)
42 struct iso_directory_record {
43 char length [ISODCL (1, 1)]; /* 711 */
44 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
45 char extent [ISODCL (3, 10)]; /* 733 */
46 char size [ISODCL (11, 18)]; /* 733 */
47 char date [ISODCL (19, 25)]; /* 7 by 711 */
48 char flags [ISODCL (26, 26)];
49 char file_unit_size [ISODCL (27, 27)]; /* 711 */
50 char interleave [ISODCL (28, 28)]; /* 711 */
51 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
52 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
56 void mmssdd( char *b, char *p )
60 unsigned char *u = (void *)b;
61 int block = (u[3] << 24) | (u[2] << 16) | (u[1] << 8) | u[0];
62 #elif defined(__BIGENDIAN__)
63 int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
65 int block = *((int*)b);
69 m = block / 4500; // minutes
70 block = block - m * 4500; // minutes rest
71 s = block / 75; // seconds
72 d = block - s * 75; // seconds rest
74 m = ((m / 10) << 4) | m % 10;
75 s = ((s / 10) << 4) | s % 10;
76 d = ((d / 10) << 4) | d % 10;
84 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
89 if (time[1] == 60) { \
94 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
97 if (CDR_readTrack(time) == -1) return -1; \
98 buf = (void *)CDR_getBuffer(); \
99 if (buf == NULL) return -1; \
100 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
102 #define READDIR(_dir) \
104 memcpy(_dir, buf + 12, 2048); \
108 memcpy(_dir + 2048, buf + 12, 2048);
110 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
111 struct iso_directory_record *dir;
117 // only try to scan if a filename is given
118 if (!strlen(filename)) return -1;
122 dir = (struct iso_directory_record*) &mdir[i];
123 if (dir->length[0] == 0) {
126 i += (u8)dir->length[0];
128 if (dir->flags[0] & 0x2) { // it's a dir
129 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
130 if (filename[dir->name_len[0]] != '\\') continue;
132 filename += dir->name_len[0] + 1;
134 mmssdd(dir->extent, (char *)time);
140 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
141 mmssdd(dir->extent, (char *)time);
150 static const unsigned int gpu_ctl_def[] = {
151 0x00000000, 0x01000000, 0x03000000, 0x04000000,
152 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
155 static const unsigned int gpu_data_def[] = {
156 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
157 0xe5001000, 0xe6000000,
158 0x02000000, 0x00000000, 0x01ff03ff,
161 static void fake_bios_gpu_setup(void)
165 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
166 GPU_writeStatus(gpu_ctl_def[i]);
168 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
169 GPU_writeData(gpu_data_def[i]);
174 struct iso_directory_record *dir;
179 // not the best place to do it, but since BIOS boot logo killer
180 // is just below, do it here
181 fake_bios_gpu_setup();
183 if (!Config.HLE && !Config.SlowBoot) {
185 psxRegs.pc = psxRegs.GPR.n.ra;
189 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
193 // skip head and sub, and go to the root directory record
194 dir = (struct iso_directory_record*) &buf[12+156];
196 mmssdd(dir->extent, (char*)time);
200 // Load SYSTEM.CNF and scan for the main executable
201 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
202 // if SYSTEM.CNF is missing, start an existing PSX.EXE
203 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
208 // read the SYSTEM.CNF
211 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
212 if (GetCdromFile(mdir, time, exename) == -1) {
213 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
214 if (GetCdromFile(mdir, time, exename) == -1) {
215 char *ptr = strstr((char *)buf + 12, "cdrom:");
218 while (*ptr == '\\' || *ptr == '/') ptr++;
219 strncpy(exename, ptr, 255);
222 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
224 if (GetCdromFile(mdir, time, exename) == -1)
231 // Read the EXE-Header
235 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
237 psxRegs.pc = SWAP32(tmpHead.pc0);
238 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
239 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
240 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
242 tmpHead.t_size = SWAP32(tmpHead.t_size);
243 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
245 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
247 // Read the rest of the main executable
248 while (tmpHead.t_size & ~2047) {
249 void *ptr = (void *)PSXM(tmpHead.t_addr);
254 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
256 tmpHead.t_size -= 2048;
257 tmpHead.t_addr += 2048;
263 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
264 struct iso_directory_record *dir;
271 sscanf(filename, "cdrom:\\%255s", exename);
273 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
277 // skip head and sub, and go to the root directory record
278 dir = (struct iso_directory_record *)&buf[12 + 156];
280 mmssdd(dir->extent, (char*)time);
284 if (GetCdromFile(mdir, time, exename) == -1) return -1;
288 memcpy(head, buf + 12, sizeof(EXE_HEADER));
292 psxCpu->Clear(addr, size / 4);
294 while (size & ~2047) {
300 memcpy(mem, buf + 12, 2048);
310 struct iso_directory_record *dir;
311 unsigned char time[4];
313 unsigned char mdir[4096];
321 time[2] = itob(0x10);
325 memset(CdromLabel, 0, sizeof(CdromLabel));
326 memset(CdromId, 0, sizeof(CdromId));
327 memset(exename, 0, sizeof(exename));
329 strncpy(CdromLabel, buf + 52, 32);
331 // skip head and sub, and go to the root directory record
332 dir = (struct iso_directory_record *)&buf[12 + 156];
334 mmssdd(dir->extent, (char *)time);
338 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
341 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
342 if (GetCdromFile(mdir, time, exename) == -1) {
343 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
344 if (GetCdromFile(mdir, time, exename) == -1) {
345 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
348 while (*ptr == '\\' || *ptr == '/') ptr++;
349 strncpy(exename, ptr, 255);
352 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
354 if (GetCdromFile(mdir, time, exename) == -1)
355 return -1; // main executable not found
360 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
361 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
363 size_t i, len = strlen(exename) - offset;
364 for (i = 0; i < len; i++)
365 exename[i] = exename[i + offset];
368 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
369 strcpy(exename, "PSX.EXE;1");
370 strcpy(CdromId, "SLUS99999");
372 return -1; // SYSTEM.CNF and PSX.EXE not found
374 if (CdromId[0] == '\0') {
375 len = strlen(exename);
377 for (i = 0; i < len; ++i) {
378 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
380 if (isalnum(exename[i]))
381 CdromId[c++] = exename[i];
385 if (CdromId[0] == '\0')
386 strcpy(CdromId, "SLUS99999");
388 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
389 if (CdromId[2] == 'e' || CdromId[2] == 'E')
390 Config.PsxType = PSX_TYPE_PAL; // pal
391 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
394 if (CdromLabel[0] == ' ') {
395 memcpy(CdromLabel, CdromId, 9);
397 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
398 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
399 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
406 static int PSXGetFileType(FILE *f) {
407 unsigned long current;
413 fseek(f, 0L, SEEK_SET);
414 if (fread(&mybuf, sizeof(mybuf), 1, f) != sizeof(mybuf))
417 fseek(f, current, SEEK_SET);
419 exe_hdr = (EXE_HEADER *)mybuf;
420 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
423 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
426 coff_hdr = (FILHDR *)mybuf;
427 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
434 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
439 // temporary pandora workaround..
441 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
446 tmp = malloc(size * nmemb);
448 ret = fread(tmp, size, nmemb, stream);
449 memcpy(ptr, tmp, size * nmemb);
455 int Load(const char *ExePath) {
461 u32 section_address, section_size;
464 strcpy(CdromId, "SLUS99999");
465 strcpy(CdromLabel, "SLUS_999.99");
467 tmpFile = fopen(ExePath, "rb");
468 if (tmpFile == NULL) {
469 SysPrintf(_("Error opening file: %s.\n"), ExePath);
472 type = PSXGetFileType(tmpFile);
475 if (fread(&tmpHead, sizeof(EXE_HEADER), 1, tmpFile) != sizeof(EXE_HEADER))
477 section_address = SWAP32(tmpHead.t_addr);
478 section_size = SWAP32(tmpHead.t_size);
479 mem = PSXM(section_address);
481 fseek(tmpFile, 0x800, SEEK_SET);
482 fread_to_ram(mem, section_size, 1, tmpFile);
483 psxCpu->Clear(section_address, section_size / 4);
485 psxRegs.pc = SWAP32(tmpHead.pc0);
486 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
487 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
488 if (psxRegs.GPR.n.sp == 0)
489 psxRegs.GPR.n.sp = 0x801fff00;
493 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
495 if (fread(&opcode, sizeof(opcode), 1, tmpFile) != sizeof(opcode))
498 case 1: /* Section loading */
499 if (fread(§ion_address, sizeof(section_address), 1, tmpFile) != sizeof(section_address))
501 if (fread(§ion_size, sizeof(section_size), 1, tmpFile) != sizeof(section_size))
503 section_address = SWAPu32(section_address);
504 section_size = SWAPu32(section_size);
506 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
508 mem = PSXM(section_address);
510 fread_to_ram(mem, section_size, 1, tmpFile);
511 psxCpu->Clear(section_address, section_size / 4);
514 case 3: /* register loading (PC only?) */
515 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
516 if (fread(&psxRegs.pc, sizeof(psxRegs.pc), 1, tmpFile) != sizeof(psxRegs.pc))
518 psxRegs.pc = SWAPu32(psxRegs.pc);
520 case 0: /* End of file */
523 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
527 } while (opcode != 0 && retval == 0);
530 SysPrintf(_("COFF files not supported.\n"));
534 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
535 SysPrintf(_("(did you forget -cdfile ?)\n"));
543 CdromLabel[0] = '\0';
551 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
559 static void *zlib_open(const char *name, const char *mode)
561 return gzopen(name, mode);
564 static int zlib_read(void *file, void *buf, u32 len)
566 return gzread(file, buf, len);
569 static int zlib_write(void *file, const void *buf, u32 len)
571 return gzwrite(file, buf, len);
574 static long zlib_seek(void *file, long offs, int whence)
576 return gzseek(file, offs, whence);
579 static void zlib_close(void *file)
584 struct PcsxSaveFuncs SaveFuncs = {
585 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
588 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
590 // Savestate Versioning!
591 // If you make changes to the savestate version, please increment the value below.
592 static const u32 SaveVersion = 0x8b410006;
594 int SaveState(const char *file) {
601 f = SaveFuncs.open(file, "wb");
602 if (f == NULL) return -1;
604 new_dyna_before_save();
606 SaveFuncs.write(f, (void *)PcsxHeader, 32);
607 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
608 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
610 pMem = (unsigned char *)malloc(128 * 96 * 3);
611 if (pMem == NULL) return -1;
612 GPU_getScreenPic(pMem);
613 SaveFuncs.write(f, pMem, 128 * 96 * 3);
619 SaveFuncs.write(f, psxM, 0x00200000);
620 SaveFuncs.write(f, psxR, 0x00080000);
621 SaveFuncs.write(f, psxH, 0x00010000);
622 SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
625 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
626 gpufP->ulFreezeVersion = 1;
627 GPU_freeze(1, gpufP);
628 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
632 spufP = (SPUFreeze_t *) malloc(16);
633 SPU_freeze(2, spufP, psxRegs.cycle);
634 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
636 spufP = (SPUFreeze_t *) malloc(Size);
637 SPU_freeze(1, spufP, psxRegs.cycle);
638 SaveFuncs.write(f, spufP, Size);
646 new_dyna_freeze(f, 1);
650 new_dyna_after_save();
655 int LoadState(const char *file) {
664 f = SaveFuncs.open(file, "rb");
665 if (f == NULL) return -1;
667 SaveFuncs.read(f, header, sizeof(header));
668 SaveFuncs.read(f, &version, sizeof(u32));
669 SaveFuncs.read(f, &hle, sizeof(boolean));
671 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
680 #if defined(LIGHTREC)
681 if (Config.Cpu != CPU_INTERPRETER)
682 psxCpu->Clear(0, UINT32_MAX); //clear all
686 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
688 SaveFuncs.read(f, psxM, 0x00200000);
689 SaveFuncs.read(f, psxR, 0x00080000);
690 SaveFuncs.read(f, psxH, 0x00010000);
691 SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
697 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
698 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
699 GPU_freeze(0, gpufP);
701 if (HW_GPU_STATUS == 0)
702 HW_GPU_STATUS = GPU_readStatus();
705 SaveFuncs.read(f, &Size, 4);
706 spufP = (SPUFreeze_t *)malloc(Size);
707 SaveFuncs.read(f, spufP, Size);
708 SPU_freeze(0, spufP, psxRegs.cycle);
716 new_dyna_freeze(f, 0);
723 int CheckState(const char *file) {
729 f = SaveFuncs.open(file, "rb");
730 if (f == NULL) return -1;
732 SaveFuncs.read(f, header, sizeof(header));
733 SaveFuncs.read(f, &version, sizeof(u32));
734 SaveFuncs.read(f, &hle, sizeof(boolean));
738 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
744 // NET Function Helpers
747 if (NET_recvData == NULL || NET_sendData == NULL)
750 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
751 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
752 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
753 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
754 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
755 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
763 if (NET_recvData == NULL || NET_sendData == NULL)
766 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
767 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
768 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
769 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
770 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
775 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
776 if (tmp != Config.Cpu) {
778 #if defined(NEW_DYNAREC) || defined(LIGHTREC)
779 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
780 else psxCpu = &psxRec;
784 if (psxCpu->Init() == -1) {
785 SysClose(); return -1;
793 // remove the leading and trailing spaces in a string
794 void trim(char *str) {
798 // skip leading blanks
799 while (str[pos] <= ' ' && str[pos] > 0)
803 *(dest++) = str[pos];
807 *(dest--) = '\0'; // store the null
809 // remove trailing blanks
810 while (dest >= str && *dest <= ' ' && *dest > 0)
814 // lookup table for crc calculation
815 static unsigned short crctab[256] = {
816 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
817 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
818 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
819 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
820 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
821 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
822 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
823 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
824 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
825 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
826 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
827 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
828 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
829 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
830 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
831 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
832 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
833 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
834 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
835 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
836 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
837 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
838 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
839 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
840 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
841 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
842 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
843 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
844 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
847 u16 calcCrc(u8 *d, int len) {
851 for (i = 0; i < len; i++) {
852 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);