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);
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);
296 while (size & ~2047) {
302 memcpy(mem, buf + 12, 2048);
312 struct iso_directory_record *dir;
313 unsigned char time[4];
315 unsigned char mdir[4096];
323 time[2] = itob(0x10);
327 memset(CdromLabel, 0, sizeof(CdromLabel));
328 memset(CdromId, 0, sizeof(CdromId));
329 memset(exename, 0, sizeof(exename));
331 strncpy(CdromLabel, buf + 52, 32);
333 // skip head and sub, and go to the root directory record
334 dir = (struct iso_directory_record *)&buf[12 + 156];
336 mmssdd(dir->extent, (char *)time);
340 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
343 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
344 if (GetCdromFile(mdir, time, exename) == -1) {
345 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
346 if (GetCdromFile(mdir, time, exename) == -1) {
347 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
350 while (*ptr == '\\' || *ptr == '/') ptr++;
351 strncpy(exename, ptr, 255);
354 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
356 if (GetCdromFile(mdir, time, exename) == -1)
357 return -1; // main executable not found
362 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
363 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
365 size_t i, len = strlen(exename) - offset;
366 for (i = 0; i < len; i++)
367 exename[i] = exename[i + offset];
370 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
371 strcpy(exename, "PSX.EXE;1");
372 strcpy(CdromId, "SLUS99999");
374 return -1; // SYSTEM.CNF and PSX.EXE not found
376 if (CdromId[0] == '\0') {
377 len = strlen(exename);
379 for (i = 0; i < len; ++i) {
380 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
382 if (isalnum(exename[i]))
383 CdromId[c++] = exename[i];
387 if (CdromId[0] == '\0')
388 strcpy(CdromId, "SLUS99999");
390 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
391 if (CdromId[2] == 'e' || CdromId[2] == 'E')
392 Config.PsxType = PSX_TYPE_PAL; // pal
393 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
396 if (CdromLabel[0] == ' ') {
397 memcpy(CdromLabel, CdromId, 9);
399 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
400 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
401 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
408 static int PSXGetFileType(FILE *f) {
409 unsigned long current;
415 fseek(f, 0L, SEEK_SET);
416 if (fread(&mybuf, sizeof(mybuf), 1, f) != sizeof(mybuf))
419 fseek(f, current, SEEK_SET);
421 exe_hdr = (EXE_HEADER *)mybuf;
422 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
425 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
428 coff_hdr = (FILHDR *)mybuf;
429 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
436 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
441 // temporary pandora workaround..
443 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
448 tmp = malloc(size * nmemb);
450 ret = fread(tmp, size, nmemb, stream);
451 memcpy(ptr, tmp, size * nmemb);
457 int Load(const char *ExePath) {
463 u32 section_address, section_size;
466 strcpy(CdromId, "SLUS99999");
467 strcpy(CdromLabel, "SLUS_999.99");
469 tmpFile = fopen(ExePath, "rb");
470 if (tmpFile == NULL) {
471 SysPrintf(_("Error opening file: %s.\n"), ExePath);
474 type = PSXGetFileType(tmpFile);
477 if (fread(&tmpHead, sizeof(EXE_HEADER), 1, tmpFile) != sizeof(EXE_HEADER))
479 section_address = SWAP32(tmpHead.t_addr);
480 section_size = SWAP32(tmpHead.t_size);
481 mem = PSXM(section_address);
483 fseek(tmpFile, 0x800, SEEK_SET);
484 fread_to_ram(mem, section_size, 1, tmpFile);
485 psxCpu->Clear(section_address, section_size / 4);
487 psxRegs.pc = SWAP32(tmpHead.pc0);
488 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
489 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
490 if (psxRegs.GPR.n.sp == 0)
491 psxRegs.GPR.n.sp = 0x801fff00;
495 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
497 if (fread(&opcode, sizeof(opcode), 1, tmpFile) != sizeof(opcode))
500 case 1: /* Section loading */
501 if (fread(§ion_address, sizeof(section_address), 1, tmpFile) != sizeof(section_address))
503 if (fread(§ion_size, sizeof(section_size), 1, tmpFile) != sizeof(section_size))
505 section_address = SWAPu32(section_address);
506 section_size = SWAPu32(section_size);
508 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
510 mem = PSXM(section_address);
512 fread_to_ram(mem, section_size, 1, tmpFile);
513 psxCpu->Clear(section_address, section_size / 4);
516 case 3: /* register loading (PC only?) */
517 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
518 if (fread(&psxRegs.pc, sizeof(psxRegs.pc), 1, tmpFile) != sizeof(psxRegs.pc))
520 psxRegs.pc = SWAPu32(psxRegs.pc);
522 case 0: /* End of file */
525 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
529 } while (opcode != 0 && retval == 0);
532 SysPrintf(_("COFF files not supported.\n"));
536 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
537 SysPrintf(_("(did you forget -cdfile ?)\n"));
545 CdromLabel[0] = '\0';
553 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
561 static void *zlib_open(const char *name, const char *mode)
563 return gzopen(name, mode);
566 static int zlib_read(void *file, void *buf, u32 len)
568 return gzread(file, buf, len);
571 static int zlib_write(void *file, const void *buf, u32 len)
573 return gzwrite(file, buf, len);
576 static long zlib_seek(void *file, long offs, int whence)
578 return gzseek(file, offs, whence);
581 static void zlib_close(void *file)
586 struct PcsxSaveFuncs SaveFuncs = {
587 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
590 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
592 // Savestate Versioning!
593 // If you make changes to the savestate version, please increment the value below.
594 static const u32 SaveVersion = 0x8b410006;
596 int SaveState(const char *file) {
603 f = SaveFuncs.open(file, "wb");
604 if (f == NULL) return -1;
606 new_dyna_before_save();
608 SaveFuncs.write(f, (void *)PcsxHeader, 32);
609 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
610 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
612 pMem = (unsigned char *)malloc(128 * 96 * 3);
613 if (pMem == NULL) return -1;
614 GPU_getScreenPic(pMem);
615 SaveFuncs.write(f, pMem, 128 * 96 * 3);
621 SaveFuncs.write(f, psxM, 0x00200000);
622 SaveFuncs.write(f, psxR, 0x00080000);
623 SaveFuncs.write(f, psxH, 0x00010000);
624 SaveFuncs.write(f, (void *)&psxRegs, sizeof(psxRegs));
627 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
628 gpufP->ulFreezeVersion = 1;
629 GPU_freeze(1, gpufP);
630 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
634 spufP = (SPUFreeze_t *) malloc(16);
635 SPU_freeze(2, spufP, psxRegs.cycle);
636 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
638 spufP = (SPUFreeze_t *) malloc(Size);
639 SPU_freeze(1, spufP, psxRegs.cycle);
640 SaveFuncs.write(f, spufP, Size);
648 new_dyna_freeze(f, 1);
652 new_dyna_after_save();
657 int LoadState(const char *file) {
666 f = SaveFuncs.open(file, "rb");
667 if (f == NULL) return -1;
669 SaveFuncs.read(f, header, sizeof(header));
670 SaveFuncs.read(f, &version, sizeof(u32));
671 SaveFuncs.read(f, &hle, sizeof(boolean));
673 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
682 #if defined(LIGHTREC)
683 if (Config.Cpu != CPU_INTERPRETER)
684 psxCpu->Clear(0, UINT32_MAX); //clear all
688 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
690 SaveFuncs.read(f, psxM, 0x00200000);
691 SaveFuncs.read(f, psxR, 0x00080000);
692 SaveFuncs.read(f, psxH, 0x00010000);
693 SaveFuncs.read(f, (void *)&psxRegs, sizeof(psxRegs));
699 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
700 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
701 GPU_freeze(0, gpufP);
703 if (HW_GPU_STATUS == 0)
704 HW_GPU_STATUS = GPU_readStatus();
707 SaveFuncs.read(f, &Size, 4);
708 spufP = (SPUFreeze_t *)malloc(Size);
709 SaveFuncs.read(f, spufP, Size);
710 SPU_freeze(0, spufP, psxRegs.cycle);
718 new_dyna_freeze(f, 0);
725 int CheckState(const char *file) {
731 f = SaveFuncs.open(file, "rb");
732 if (f == NULL) return -1;
734 SaveFuncs.read(f, header, sizeof(header));
735 SaveFuncs.read(f, &version, sizeof(u32));
736 SaveFuncs.read(f, &hle, sizeof(boolean));
740 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
746 // NET Function Helpers
749 if (NET_recvData == NULL || NET_sendData == NULL)
752 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
753 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
754 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
755 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
756 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
757 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
765 if (NET_recvData == NULL || NET_sendData == NULL)
768 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
769 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
770 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
771 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
772 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
777 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
778 if (tmp != Config.Cpu) {
780 #if defined(NEW_DYNAREC) || defined(LIGHTREC)
781 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
782 else psxCpu = &psxRec;
786 if (psxCpu->Init() == -1) {
787 SysClose(); return -1;
795 // remove the leading and trailing spaces in a string
796 void trim(char *str) {
800 // skip leading blanks
801 while (str[pos] <= ' ' && str[pos] > 0)
805 *(dest++) = str[pos];
809 *(dest--) = '\0'; // store the null
811 // remove trailing blanks
812 while (dest >= str && *dest <= ' ' && *dest > 0)
816 // lookup table for crc calculation
817 static unsigned short crctab[256] = {
818 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
819 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
820 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
821 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
822 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
823 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
824 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
825 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
826 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
827 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
828 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
829 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
830 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
831 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
832 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
833 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
834 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
835 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
836 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
837 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
838 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
839 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
840 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
841 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
842 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
843 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
844 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
845 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
846 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
849 u16 calcCrc(u8 *d, int len) {
853 for (i = 0; i < len; i++) {
854 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);