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 #include "lightrec/plugin.h"
34 char CdromId[10] = "";
35 char CdromLabel[33] = "";
37 // PSX Executable types
43 #define ISODCL(from, to) (to - from + 1)
45 struct iso_directory_record {
46 char length [ISODCL (1, 1)]; /* 711 */
47 char ext_attr_length [ISODCL (2, 2)]; /* 711 */
48 char extent [ISODCL (3, 10)]; /* 733 */
49 char size [ISODCL (11, 18)]; /* 733 */
50 char date [ISODCL (19, 25)]; /* 7 by 711 */
51 char flags [ISODCL (26, 26)];
52 char file_unit_size [ISODCL (27, 27)]; /* 711 */
53 char interleave [ISODCL (28, 28)]; /* 711 */
54 char volume_sequence_number [ISODCL (29, 32)]; /* 723 */
55 unsigned char name_len [ISODCL (33, 33)]; /* 711 */
59 static void mmssdd( char *b, char *p )
62 unsigned char *ub = (void *)b;
63 int block = (ub[3] << 24) | (ub[2] << 16) | (ub[1] << 8) | ub[0];
66 m = block / 4500; // minutes
67 block = block - m * 4500; // minutes rest
68 s = block / 75; // seconds
69 d = block - s * 75; // seconds rest
71 m = ((m / 10) << 4) | m % 10;
72 s = ((s / 10) << 4) | s % 10;
73 d = ((d / 10) << 4) | d % 10;
81 time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
86 if (time[1] == 60) { \
91 time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
94 if (CDR_readTrack(time) == -1) return -1; \
95 buf = (void *)CDR_getBuffer(); \
96 if (buf == NULL) return -1; \
97 else CheckPPFCache((u8 *)buf, time[0], time[1], time[2]);
99 #define READDIR(_dir) \
101 memcpy(_dir, buf + 12, 2048); \
105 memcpy(_dir + 2048, buf + 12, 2048);
107 int GetCdromFile(u8 *mdir, u8 *time, char *filename) {
108 struct iso_directory_record *dir;
114 // only try to scan if a filename is given
115 if (!strlen(filename)) return -1;
119 dir = (struct iso_directory_record*) &mdir[i];
120 if (dir->length[0] == 0) {
123 i += (u8)dir->length[0];
125 if (dir->flags[0] & 0x2) { // it's a dir
126 if (!strnicmp((char *)&dir->name[0], filename, dir->name_len[0])) {
127 if (filename[dir->name_len[0]] != '\\') continue;
129 filename += dir->name_len[0] + 1;
131 mmssdd(dir->extent, (char *)time);
137 if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
138 mmssdd(dir->extent, (char *)time);
147 static const unsigned int gpu_ctl_def[] = {
148 0x00000000, 0x01000000, 0x03000000, 0x04000000,
149 0x05000800, 0x06c60260, 0x0703fc10, 0x08000027,
152 static const unsigned int gpu_data_def[] = {
153 0xe100360b, 0xe2000000, 0xe3000800, 0xe4077e7f,
154 0xe5001000, 0xe6000000,
155 0x02000000, 0x00000000, 0x01ff03ff,
158 static void fake_bios_gpu_setup(void)
162 for (i = 0; i < sizeof(gpu_ctl_def) / sizeof(gpu_ctl_def[0]); i++)
163 GPU_writeStatus(gpu_ctl_def[i]);
165 for (i = 0; i < sizeof(gpu_data_def) / sizeof(gpu_data_def[0]); i++)
166 GPU_writeData(gpu_data_def[i]);
171 struct iso_directory_record *dir;
176 // not the best place to do it, but since BIOS boot logo killer
177 // is just below, do it here
178 fake_bios_gpu_setup();
180 if (!Config.HLE && !Config.SlowBoot) {
182 psxRegs.pc = psxRegs.GPR.n.ra;
186 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
190 // skip head and sub, and go to the root directory record
191 dir = (struct iso_directory_record*) &buf[12+156];
193 mmssdd(dir->extent, (char*)time);
197 // Load SYSTEM.CNF and scan for the main executable
198 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
199 // if SYSTEM.CNF is missing, start an existing PSX.EXE
200 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
205 // read the SYSTEM.CNF
208 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
209 if (GetCdromFile(mdir, time, exename) == -1) {
210 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
211 if (GetCdromFile(mdir, time, exename) == -1) {
212 char *ptr = strstr((char *)buf + 12, "cdrom:");
215 while (*ptr == '\\' || *ptr == '/') ptr++;
216 strncpy(exename, ptr, 255);
219 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
221 if (GetCdromFile(mdir, time, exename) == -1)
228 // Read the EXE-Header
232 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
234 psxRegs.pc = SWAP32(tmpHead.pc0);
235 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
236 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
237 if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
239 tmpHead.t_size = SWAP32(tmpHead.t_size);
240 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
242 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
245 // Read the rest of the main executable
246 while (tmpHead.t_size & ~2047) {
247 void *ptr = (void *)PSXM(tmpHead.t_addr);
252 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
254 tmpHead.t_size -= 2048;
255 tmpHead.t_addr += 2048;
261 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
262 struct iso_directory_record *dir;
269 sscanf(filename, "cdrom:\\%255s", exename);
271 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
275 // skip head and sub, and go to the root directory record
276 dir = (struct iso_directory_record *)&buf[12 + 156];
278 mmssdd(dir->extent, (char*)time);
282 if (GetCdromFile(mdir, time, exename) == -1) return -1;
286 memcpy(head, buf + 12, sizeof(EXE_HEADER));
290 psxCpu->Clear(addr, size / 4);
293 while (size & ~2047) {
298 if (mem != INVALID_PTR)
299 memcpy(mem, buf + 12, 2048);
309 struct iso_directory_record *dir;
310 unsigned char time[4];
312 unsigned char mdir[4096];
320 time[2] = itob(0x10);
324 memset(CdromLabel, 0, sizeof(CdromLabel));
325 memset(CdromId, 0, sizeof(CdromId));
326 memset(exename, 0, sizeof(exename));
328 strncpy(CdromLabel, buf + 52, 32);
330 // skip head and sub, and go to the root directory record
331 dir = (struct iso_directory_record *)&buf[12 + 156];
333 mmssdd(dir->extent, (char *)time);
337 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
340 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
341 if (GetCdromFile(mdir, time, exename) == -1) {
342 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
343 if (GetCdromFile(mdir, time, exename) == -1) {
344 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
347 while (*ptr == '\\' || *ptr == '/') ptr++;
348 strncpy(exename, ptr, 255);
351 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
353 if (GetCdromFile(mdir, time, exename) == -1)
354 return -1; // main executable not found
359 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
360 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
362 size_t i, len = strlen(exename) - offset;
363 for (i = 0; i < len; i++)
364 exename[i] = exename[i + offset];
367 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
368 strcpy(exename, "PSX.EXE;1");
369 strcpy(CdromId, "SLUS99999");
371 return -1; // SYSTEM.CNF and PSX.EXE not found
373 if (CdromId[0] == '\0') {
374 len = strlen(exename);
376 for (i = 0; i < len; ++i) {
377 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
379 if (isalnum(exename[i]))
380 CdromId[c++] = exename[i];
384 if (CdromId[0] == '\0')
385 strcpy(CdromId, "SLUS99999");
387 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
389 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
390 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
391 !strncmp(CdromId, "DTLS3035", 8) ||
392 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
393 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
394 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
395 Config.PsxType = PSX_TYPE_PAL; // pal
396 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
399 if (CdromLabel[0] == ' ') {
400 strncpy(CdromLabel, CdromId, 9);
402 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
403 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
404 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
413 static int PSXGetFileType(FILE *f) {
414 unsigned long current;
420 fseek(f, 0L, SEEK_SET);
421 if (fread(&mybuf, sizeof(mybuf), 1, f) != sizeof(mybuf))
424 fseek(f, current, SEEK_SET);
426 exe_hdr = (EXE_HEADER *)mybuf;
427 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
430 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
433 coff_hdr = (FILHDR *)mybuf;
434 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
441 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
446 // temporary pandora workaround..
448 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
453 tmp = malloc(size * nmemb);
455 ret = fread(tmp, size, nmemb, stream);
456 memcpy(ptr, tmp, size * nmemb);
462 int Load(const char *ExePath) {
468 u32 section_address, section_size;
471 strcpy(CdromId, "SLUS99999");
472 strcpy(CdromLabel, "SLUS_999.99");
474 tmpFile = fopen(ExePath, "rb");
475 if (tmpFile == NULL) {
476 SysPrintf(_("Error opening file: %s.\n"), ExePath);
479 type = PSXGetFileType(tmpFile);
482 if (fread(&tmpHead, sizeof(EXE_HEADER), 1, tmpFile) != sizeof(EXE_HEADER))
484 section_address = SWAP32(tmpHead.t_addr);
485 section_size = SWAP32(tmpHead.t_size);
486 mem = PSXM(section_address);
487 if (mem != INVALID_PTR) {
488 fseek(tmpFile, 0x800, SEEK_SET);
489 fread_to_ram(mem, section_size, 1, tmpFile);
490 psxCpu->Clear(section_address, section_size / 4);
492 psxRegs.pc = SWAP32(tmpHead.pc0);
493 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
494 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
495 if (psxRegs.GPR.n.sp == 0)
496 psxRegs.GPR.n.sp = 0x801fff00;
500 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
502 if (fread(&opcode, sizeof(opcode), 1, tmpFile) != sizeof(opcode))
505 case 1: /* Section loading */
506 if (fread(§ion_address, sizeof(section_address), 1, tmpFile) != sizeof(section_address))
508 if (fread(§ion_size, sizeof(section_size), 1, tmpFile) != sizeof(section_size))
510 section_address = SWAPu32(section_address);
511 section_size = SWAPu32(section_size);
513 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
515 mem = PSXM(section_address);
516 if (mem != INVALID_PTR) {
517 fread_to_ram(mem, section_size, 1, tmpFile);
518 psxCpu->Clear(section_address, section_size / 4);
521 case 3: /* register loading (PC only?) */
522 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
523 if (fread(&psxRegs.pc, sizeof(psxRegs.pc), 1, tmpFile) != sizeof(psxRegs.pc))
525 psxRegs.pc = SWAPu32(psxRegs.pc);
527 case 0: /* End of file */
530 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
534 } while (opcode != 0 && retval == 0);
537 SysPrintf(_("COFF files not supported.\n"));
541 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
542 SysPrintf(_("(did you forget -cdfile ?)\n"));
550 CdromLabel[0] = '\0';
558 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
566 static void *zlib_open(const char *name, const char *mode)
568 return gzopen(name, mode);
571 static int zlib_read(void *file, void *buf, u32 len)
573 return gzread(file, buf, len);
576 static int zlib_write(void *file, const void *buf, u32 len)
578 return gzwrite(file, buf, len);
581 static long zlib_seek(void *file, long offs, int whence)
583 return gzseek(file, offs, whence);
586 static void zlib_close(void *file)
591 struct PcsxSaveFuncs SaveFuncs = {
592 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
595 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
597 // Savestate Versioning!
598 // If you make changes to the savestate version, please increment the value below.
599 static const u32 SaveVersion = 0x8b410006;
601 int SaveState(const char *file) {
608 f = SaveFuncs.open(file, "wb");
609 if (f == NULL) return -1;
611 new_dyna_before_save();
613 if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
614 lightrec_plugin_prepare_save_state();
616 SaveFuncs.write(f, (void *)PcsxHeader, 32);
617 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
618 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
620 pMem = (unsigned char *)malloc(128 * 96 * 3);
621 if (pMem == NULL) return -1;
622 GPU_getScreenPic(pMem);
623 SaveFuncs.write(f, pMem, 128 * 96 * 3);
629 SaveFuncs.write(f, psxM, 0x00200000);
630 SaveFuncs.write(f, psxR, 0x00080000);
631 SaveFuncs.write(f, psxH, 0x00010000);
632 // only partial save of psxRegisters to maintain savestate compat
633 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
636 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
637 gpufP->ulFreezeVersion = 1;
638 GPU_freeze(1, gpufP);
639 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
643 spufP = (SPUFreeze_t *) malloc(16);
644 SPU_freeze(2, spufP, psxRegs.cycle);
645 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
647 spufP = (SPUFreeze_t *) malloc(Size);
648 SPU_freeze(1, spufP, psxRegs.cycle);
649 SaveFuncs.write(f, spufP, Size);
657 new_dyna_freeze(f, 1);
661 new_dyna_after_save();
666 int LoadState(const char *file) {
675 f = SaveFuncs.open(file, "rb");
676 if (f == NULL) return -1;
678 SaveFuncs.read(f, header, sizeof(header));
679 SaveFuncs.read(f, &version, sizeof(u32));
680 SaveFuncs.read(f, &hle, sizeof(boolean));
682 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
691 if (!drc_is_lightrec() || Config.Cpu == CPU_INTERPRETER)
693 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
695 SaveFuncs.read(f, psxM, 0x00200000);
696 SaveFuncs.read(f, psxR, 0x00080000);
697 SaveFuncs.read(f, psxH, 0x00010000);
698 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
699 psxRegs.gteBusyCycle = psxRegs.cycle;
701 if (drc_is_lightrec() && Config.Cpu != CPU_INTERPRETER)
702 lightrec_plugin_prepare_load_state();
708 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
709 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
710 GPU_freeze(0, gpufP);
712 if (HW_GPU_STATUS == 0)
713 HW_GPU_STATUS = SWAP32(GPU_readStatus());
716 SaveFuncs.read(f, &Size, 4);
717 spufP = (SPUFreeze_t *)malloc(Size);
718 SaveFuncs.read(f, spufP, Size);
719 SPU_freeze(0, spufP, psxRegs.cycle);
727 new_dyna_freeze(f, 0);
734 int CheckState(const char *file) {
740 f = SaveFuncs.open(file, "rb");
741 if (f == NULL) return -1;
743 SaveFuncs.read(f, header, sizeof(header));
744 SaveFuncs.read(f, &version, sizeof(u32));
745 SaveFuncs.read(f, &hle, sizeof(boolean));
749 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
755 // NET Function Helpers
758 if (NET_recvData == NULL || NET_sendData == NULL)
761 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
762 NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
763 NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
764 NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
765 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
766 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
774 if (NET_recvData == NULL || NET_sendData == NULL)
777 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
778 NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
779 NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
780 NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
781 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
786 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
787 if (tmp != Config.Cpu) {
790 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
791 else psxCpu = &psxRec;
795 if (psxCpu->Init() == -1) {
796 SysClose(); return -1;
804 // remove the leading and trailing spaces in a string
805 void trim(char *str) {
809 // skip leading blanks
810 while (str[pos] <= ' ' && str[pos] > 0)
814 *(dest++) = str[pos];
818 *(dest--) = '\0'; // store the null
820 // remove trailing blanks
821 while (dest >= str && *dest <= ' ' && *dest > 0)
825 // lookup table for crc calculation
826 static unsigned short crctab[256] = {
827 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
828 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
829 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
830 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
831 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
832 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
833 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
834 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
835 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
836 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
837 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
838 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
839 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
840 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
841 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
842 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
843 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
844 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
845 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
846 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
847 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
848 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
849 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
850 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
851 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
852 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
853 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
854 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
855 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
858 u16 calcCrc(u8 *d, int len) {
862 for (i = 0; i < len; i++) {
863 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);