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)) 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();
179 if (!Config.HLE && !Config.SlowBoot) {
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 != INVALID_PTR) 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) {
297 if (mem != INVALID_PTR)
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 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
359 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
361 size_t i, len = strlen(exename) - offset;
362 for (i = 0; i < len; i++)
363 exename[i] = exename[i + offset];
366 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
367 strcpy(exename, "PSX.EXE;1");
368 strcpy(CdromId, "SLUS99999");
370 return -1; // SYSTEM.CNF and PSX.EXE not found
372 if (CdromId[0] == '\0') {
373 len = strlen(exename);
375 for (i = 0; i < len; ++i) {
376 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
378 if (isalnum(exename[i]))
379 CdromId[c++] = exename[i];
383 if (CdromId[0] == '\0')
384 strcpy(CdromId, "SLUS99999");
386 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
388 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
389 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
390 !strncmp(CdromId, "DTLS3035", 8) ||
391 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
392 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
393 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
394 Config.PsxType = PSX_TYPE_PAL; // pal
395 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
398 if (CdromLabel[0] == ' ') {
399 strncpy(CdromLabel, CdromId, 9);
401 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
402 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
403 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
412 static int PSXGetFileType(FILE *f) {
413 unsigned long current;
419 fseek(f, 0L, SEEK_SET);
420 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
423 fseek(f, current, SEEK_SET);
425 exe_hdr = (EXE_HEADER *)mybuf;
426 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
429 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
432 coff_hdr = (FILHDR *)mybuf;
433 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
440 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
445 // temporary pandora workaround..
447 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
452 tmp = malloc(size * nmemb);
454 ret = fread(tmp, size, nmemb, stream);
455 memcpy(ptr, tmp, size * nmemb);
461 int Load(const char *ExePath) {
467 u32 section_address, section_size;
470 strcpy(CdromId, "SLUS99999");
471 strcpy(CdromLabel, "SLUS_999.99");
473 tmpFile = fopen(ExePath, "rb");
474 if (tmpFile == NULL) {
475 SysPrintf(_("Error opening file: %s.\n"), ExePath);
478 type = PSXGetFileType(tmpFile);
481 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
483 section_address = SWAP32(tmpHead.t_addr);
484 section_size = SWAP32(tmpHead.t_size);
485 mem = PSXM(section_address);
486 if (mem != INVALID_PTR) {
487 fseek(tmpFile, 0x800, SEEK_SET);
488 fread_to_ram(mem, section_size, 1, tmpFile);
489 psxCpu->Clear(section_address, section_size / 4);
491 psxRegs.pc = SWAP32(tmpHead.pc0);
492 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
493 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr);
494 if (psxRegs.GPR.n.sp == 0)
495 psxRegs.GPR.n.sp = 0x801fff00;
499 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
501 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
504 case 1: /* Section loading */
505 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
507 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
509 section_address = SWAPu32(section_address);
510 section_size = SWAPu32(section_size);
512 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
514 mem = PSXM(section_address);
515 if (mem != INVALID_PTR) {
516 fread_to_ram(mem, section_size, 1, tmpFile);
517 psxCpu->Clear(section_address, section_size / 4);
520 case 3: /* register loading (PC only?) */
521 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
522 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
524 psxRegs.pc = SWAPu32(psxRegs.pc);
526 case 0: /* End of file */
529 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
533 } while (opcode != 0 && retval == 0);
536 SysPrintf(_("COFF files not supported.\n"));
540 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
541 SysPrintf(_("(did you forget -cdfile ?)\n"));
549 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 SaveFuncs.write(f, (void *)PcsxHeader, 32);
614 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
615 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
617 pMem = (unsigned char *)malloc(128 * 96 * 3);
618 if (pMem == NULL) return -1;
619 GPU_getScreenPic(pMem);
620 SaveFuncs.write(f, pMem, 128 * 96 * 3);
626 SaveFuncs.write(f, psxM, 0x00200000);
627 SaveFuncs.write(f, psxR, 0x00080000);
628 SaveFuncs.write(f, psxH, 0x00010000);
629 // only partial save of psxRegisters to maintain savestate compat
630 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
633 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
634 gpufP->ulFreezeVersion = 1;
635 GPU_freeze(1, gpufP);
636 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
640 spufP = (SPUFreeze_t *) malloc(16);
641 SPU_freeze(2, spufP, psxRegs.cycle);
642 Size = spufP->Size; SaveFuncs.write(f, &Size, 4);
644 spufP = (SPUFreeze_t *) malloc(Size);
645 SPU_freeze(1, spufP, psxRegs.cycle);
646 SaveFuncs.write(f, spufP, Size);
654 new_dyna_freeze(f, 1);
658 new_dyna_after_save();
663 int LoadState(const char *file) {
672 f = SaveFuncs.open(file, "rb");
673 if (f == NULL) return -1;
675 SaveFuncs.read(f, header, sizeof(header));
676 SaveFuncs.read(f, &version, sizeof(u32));
677 SaveFuncs.read(f, &hle, sizeof(boolean));
679 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
689 SaveFuncs.seek(f, 128 * 96 * 3, SEEK_CUR);
691 SaveFuncs.read(f, psxM, 0x00200000);
692 SaveFuncs.read(f, psxR, 0x00080000);
693 SaveFuncs.read(f, psxH, 0x00010000);
694 SaveFuncs.read(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
695 psxRegs.gteBusyCycle = psxRegs.cycle;
701 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
702 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
703 GPU_freeze(0, gpufP);
705 if (HW_GPU_STATUS == 0)
706 HW_GPU_STATUS = SWAP32(GPU_readStatus());
709 SaveFuncs.read(f, &Size, 4);
710 spufP = (SPUFreeze_t *)malloc(Size);
711 SaveFuncs.read(f, spufP, Size);
712 SPU_freeze(0, spufP, psxRegs.cycle);
720 new_dyna_freeze(f, 0);
727 int CheckState(const char *file) {
733 f = SaveFuncs.open(file, "rb");
734 if (f == NULL) return -1;
736 SaveFuncs.read(f, header, sizeof(header));
737 SaveFuncs.read(f, &version, sizeof(u32));
738 SaveFuncs.read(f, &hle, sizeof(boolean));
742 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
748 // NET Function Helpers
751 if (NET_recvData == NULL || NET_sendData == NULL)
755 boolean SpuIrq_old = 0;
756 boolean RCntFix_old = 0;
757 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
758 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
759 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
760 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
761 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
762 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
770 if (NET_recvData == NULL || NET_sendData == NULL)
774 boolean SpuIrq_old = 0;
775 boolean RCntFix_old = 0;
776 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
777 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
778 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
779 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
780 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
785 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
786 if (tmp != Config.Cpu) {
789 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
790 else psxCpu = &psxRec;
794 if (psxCpu->Init() == -1) {
795 SysClose(); return -1;
803 // remove the leading and trailing spaces in a string
804 void trim(char *str) {
808 // skip leading blanks
809 while (str[pos] <= ' ' && str[pos] > 0)
813 *(dest++) = str[pos];
817 *(dest--) = '\0'; // store the null
819 // remove trailing blanks
820 while (dest >= str && *dest <= ' ' && *dest > 0)
824 // lookup table for crc calculation
825 static unsigned short crctab[256] = {
826 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
827 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
828 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
829 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
830 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
831 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
832 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
833 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
834 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
835 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
836 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
837 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
838 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
839 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
840 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
841 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
842 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
843 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
844 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
845 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
846 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
847 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
848 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
849 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
850 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
851 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
852 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
853 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
854 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
857 u16 calcCrc(u8 *d, int len) {
861 for (i = 0; i < len; i++) {
862 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);