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]);
168 static void SetBootRegs(u32 pc, u32 gp, u32 sp)
170 //printf("%s %08x %08x %08x\n", __func__, pc, gp, sp);
171 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
174 psxRegs.GPR.n.gp = gp;
175 psxRegs.GPR.n.sp = sp ? sp : 0x801fff00;
177 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
182 struct iso_directory_record *dir;
187 // not the best place to do it, but since BIOS boot logo killer
188 // is just below, do it here
189 fake_bios_gpu_setup();
191 if (!Config.HLE && !Config.SlowBoot) {
193 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
194 psxRegs.pc = psxRegs.GPR.n.ra;
198 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
202 // skip head and sub, and go to the root directory record
203 dir = (struct iso_directory_record*) &buf[12+156];
205 mmssdd(dir->extent, (char*)time);
209 // Load SYSTEM.CNF and scan for the main executable
210 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
211 // if SYSTEM.CNF is missing, start an existing PSX.EXE
212 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
217 // read the SYSTEM.CNF
220 sscanf((char *)buf + 12, "BOOT = cdrom:\\%255s", exename);
221 if (GetCdromFile(mdir, time, exename) == -1) {
222 sscanf((char *)buf + 12, "BOOT = cdrom:%255s", exename);
223 if (GetCdromFile(mdir, time, exename) == -1) {
224 char *ptr = strstr((char *)buf + 12, "cdrom:");
227 while (*ptr == '\\' || *ptr == '/') ptr++;
228 strncpy(exename, ptr, 255);
231 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
233 if (GetCdromFile(mdir, time, exename) == -1)
240 // Read the EXE-Header
244 memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
246 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0), SWAP32(tmpHead.s_addr));
248 tmpHead.t_size = SWAP32(tmpHead.t_size);
249 tmpHead.t_addr = SWAP32(tmpHead.t_addr);
251 psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
254 // Read the rest of the main executable
255 while (tmpHead.t_size & ~2047) {
256 void *ptr = (void *)PSXM(tmpHead.t_addr);
261 if (ptr != INVALID_PTR) memcpy(ptr, buf+12, 2048);
263 tmpHead.t_size -= 2048;
264 tmpHead.t_addr += 2048;
270 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
271 struct iso_directory_record *dir;
278 sscanf(filename, "cdrom:\\%255s", exename);
280 time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
284 // skip head and sub, and go to the root directory record
285 dir = (struct iso_directory_record *)&buf[12 + 156];
287 mmssdd(dir->extent, (char*)time);
291 if (GetCdromFile(mdir, time, exename) == -1) return -1;
295 memcpy(head, buf + 12, sizeof(EXE_HEADER));
299 psxCpu->Clear(addr, size / 4);
302 while (size & ~2047) {
307 if (mem != INVALID_PTR)
308 memcpy(mem, buf + 12, 2048);
318 struct iso_directory_record *dir;
319 unsigned char time[4];
321 unsigned char mdir[4096];
329 time[2] = itob(0x10);
333 memset(CdromLabel, 0, sizeof(CdromLabel));
334 memset(CdromId, 0, sizeof(CdromId));
335 memset(exename, 0, sizeof(exename));
337 strncpy(CdromLabel, buf + 52, 32);
339 // skip head and sub, and go to the root directory record
340 dir = (struct iso_directory_record *)&buf[12 + 156];
342 mmssdd(dir->extent, (char *)time);
346 if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
349 sscanf(buf + 12, "BOOT = cdrom:\\%255s", exename);
350 if (GetCdromFile(mdir, time, exename) == -1) {
351 sscanf(buf + 12, "BOOT = cdrom:%255s", exename);
352 if (GetCdromFile(mdir, time, exename) == -1) {
353 char *ptr = strstr(buf + 12, "cdrom:"); // possibly the executable is in some subdir
356 while (*ptr == '\\' || *ptr == '/') ptr++;
357 strncpy(exename, ptr, 255);
360 while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
362 if (GetCdromFile(mdir, time, exename) == -1)
363 return -1; // main executable not found
368 /* Workaround for Wild Arms EU/US which has non-standard string causing incorrect region detection */
369 if (exename[0] == 'E' && exename[1] == 'X' && exename[2] == 'E' && exename[3] == '\\') {
371 size_t i, len = strlen(exename) - offset;
372 for (i = 0; i < len; i++)
373 exename[i] = exename[i + offset];
376 } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
377 strcpy(exename, "PSX.EXE;1");
378 strcpy(CdromId, "SLUS99999");
380 return -1; // SYSTEM.CNF and PSX.EXE not found
382 if (CdromId[0] == '\0') {
383 len = strlen(exename);
385 for (i = 0; i < len; ++i) {
386 if (exename[i] == ';' || c >= sizeof(CdromId) - 1)
388 if (isalnum(exename[i]))
389 CdromId[c++] = exename[i];
393 if (CdromId[0] == '\0')
394 strcpy(CdromId, "SLUS99999");
396 if (Config.PsxAuto) { // autodetect system (pal or ntsc)
398 /* Make sure Wild Arms SCUS-94608 is not detected as a PAL game. */
399 ((CdromId[0] == 's' || CdromId[0] == 'S') && (CdromId[2] == 'e' || CdromId[2] == 'E')) ||
400 !strncmp(CdromId, "DTLS3035", 8) ||
401 !strncmp(CdromId, "PBPX95001", 9) || // according to redump.org, these PAL
402 !strncmp(CdromId, "PBPX95007", 9) || // discs have a non-standard ID;
403 !strncmp(CdromId, "PBPX95008", 9)) // add more serials if they are discovered.
404 Config.PsxType = PSX_TYPE_PAL; // pal
405 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
408 if (CdromLabel[0] == ' ') {
409 strncpy(CdromLabel, CdromId, 9);
411 SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
412 SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
413 SysPrintf(_("CD-ROM EXE Name: %.255s\n"), exename);
422 static int PSXGetFileType(FILE *f) {
423 unsigned long current;
429 fseek(f, 0L, SEEK_SET);
430 if (fread(&mybuf, 1, sizeof(mybuf), f) != sizeof(mybuf))
433 fseek(f, current, SEEK_SET);
435 exe_hdr = (EXE_HEADER *)mybuf;
436 if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
439 if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
442 coff_hdr = (FILHDR *)mybuf;
443 if (SWAPu16(coff_hdr->f_magic) == 0x0162)
450 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
455 // temporary pandora workaround..
457 size_t fread_to_ram(void *ptr, size_t size, size_t nmemb, FILE *stream)
462 tmp = malloc(size * nmemb);
464 ret = fread(tmp, size, nmemb, stream);
465 memcpy(ptr, tmp, size * nmemb);
471 int Load(const char *ExePath) {
477 u32 section_address, section_size;
480 strcpy(CdromId, "SLUS99999");
481 strcpy(CdromLabel, "SLUS_999.99");
483 tmpFile = fopen(ExePath, "rb");
484 if (tmpFile == NULL) {
485 SysPrintf(_("Error opening file: %s.\n"), ExePath);
488 type = PSXGetFileType(tmpFile);
491 if (fread(&tmpHead, 1, sizeof(EXE_HEADER), tmpFile) != sizeof(EXE_HEADER))
493 section_address = SWAP32(tmpHead.t_addr);
494 section_size = SWAP32(tmpHead.t_size);
495 mem = PSXM(section_address);
496 if (mem != INVALID_PTR) {
497 fseek(tmpFile, 0x800, SEEK_SET);
498 fread_to_ram(mem, section_size, 1, tmpFile);
499 psxCpu->Clear(section_address, section_size / 4);
501 SetBootRegs(SWAP32(tmpHead.pc0), SWAP32(tmpHead.gp0),
502 SWAP32(tmpHead.s_addr));
506 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
508 if (fread(&opcode, 1, sizeof(opcode), tmpFile) != sizeof(opcode))
511 case 1: /* Section loading */
512 if (fread(§ion_address, 1, sizeof(section_address), tmpFile) != sizeof(section_address))
514 if (fread(§ion_size, 1, sizeof(section_size), tmpFile) != sizeof(section_size))
516 section_address = SWAPu32(section_address);
517 section_size = SWAPu32(section_size);
519 EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
521 mem = PSXM(section_address);
522 if (mem != INVALID_PTR) {
523 fread_to_ram(mem, section_size, 1, tmpFile);
524 psxCpu->Clear(section_address, section_size / 4);
527 case 3: /* register loading (PC only?) */
528 fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
529 if (fread(&psxRegs.pc, 1, sizeof(psxRegs.pc), tmpFile) != sizeof(psxRegs.pc))
531 psxRegs.pc = SWAPu32(psxRegs.pc);
533 case 0: /* End of file */
536 SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
540 } while (opcode != 0 && retval == 0);
543 SysPrintf(_("COFF files not supported.\n"));
547 SysPrintf(_("This file does not appear to be a valid PSX EXE file.\n"));
548 SysPrintf(_("(did you forget -cdfile ?)\n"));
556 CdromLabel[0] = '\0';
565 SysPrintf(_("File IO error in <%s:%s>.\n"), __FILE__, __func__);
573 static void *zlib_open(const char *name, const char *mode)
575 return gzopen(name, mode);
578 static int zlib_read(void *file, void *buf, u32 len)
580 return gzread(file, buf, len);
583 static int zlib_write(void *file, const void *buf, u32 len)
585 return gzwrite(file, buf, len);
588 static long zlib_seek(void *file, long offs, int whence)
590 return gzseek(file, offs, whence);
593 static void zlib_close(void *file)
598 struct PcsxSaveFuncs SaveFuncs = {
599 zlib_open, zlib_read, zlib_write, zlib_seek, zlib_close
602 static const char PcsxHeader[32] = "STv4 PCSX v" PCSX_VERSION;
604 // Savestate Versioning!
605 // If you make changes to the savestate version, please increment the value below.
606 static const u32 SaveVersion = 0x8b410006;
608 int SaveState(const char *file) {
611 SPUFreezeHdr_t *spufH;
616 f = SaveFuncs.open(file, "wb");
617 if (f == NULL) return -1;
619 psxCpu->Notify(R3000ACPU_NOTIFY_BEFORE_SAVE, NULL);
621 SaveFuncs.write(f, (void *)PcsxHeader, 32);
622 SaveFuncs.write(f, (void *)&SaveVersion, sizeof(u32));
623 SaveFuncs.write(f, (void *)&Config.HLE, sizeof(boolean));
625 pMem = (unsigned char *)malloc(128 * 96 * 3);
626 if (pMem == NULL) return -1;
627 GPU_getScreenPic(pMem);
628 SaveFuncs.write(f, pMem, 128 * 96 * 3);
634 SaveFuncs.write(f, psxM, 0x00200000);
635 SaveFuncs.write(f, psxR, 0x00080000);
636 SaveFuncs.write(f, psxH, 0x00010000);
637 // only partial save of psxRegisters to maintain savestate compat
638 SaveFuncs.write(f, &psxRegs, offsetof(psxRegisters, gteBusyCycle));
641 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
642 gpufP->ulFreezeVersion = 1;
643 GPU_freeze(1, gpufP);
644 SaveFuncs.write(f, gpufP, sizeof(GPUFreeze_t));
648 spufH = malloc(sizeof(*spufH));
649 SPU_freeze(2, (SPUFreeze_t *)spufH, psxRegs.cycle);
650 Size = spufH->Size; SaveFuncs.write(f, &Size, 4);
652 spufP = (SPUFreeze_t *) malloc(Size);
653 SPU_freeze(1, spufP, psxRegs.cycle);
654 SaveFuncs.write(f, spufP, Size);
662 new_dyna_freeze(f, 1);
669 int LoadState(const char *file) {
678 f = SaveFuncs.open(file, "rb");
679 if (f == NULL) return -1;
681 SaveFuncs.read(f, header, sizeof(header));
682 SaveFuncs.read(f, &version, sizeof(u32));
683 SaveFuncs.read(f, &hle, sizeof(boolean));
685 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
694 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 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
707 gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
708 SaveFuncs.read(f, gpufP, sizeof(GPUFreeze_t));
709 GPU_freeze(0, gpufP);
711 if (HW_GPU_STATUS == 0)
712 HW_GPU_STATUS = SWAP32(GPU_readStatus());
715 SaveFuncs.read(f, &Size, 4);
716 spufP = (SPUFreeze_t *)malloc(Size);
717 SaveFuncs.read(f, spufP, Size);
718 SPU_freeze(0, spufP, psxRegs.cycle);
726 new_dyna_freeze(f, 0);
733 int CheckState(const char *file) {
739 f = SaveFuncs.open(file, "rb");
740 if (f == NULL) return -1;
742 SaveFuncs.read(f, header, sizeof(header));
743 SaveFuncs.read(f, &version, sizeof(u32));
744 SaveFuncs.read(f, &hle, sizeof(boolean));
748 if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
754 // NET Function Helpers
757 if (NET_recvData == NULL || NET_sendData == NULL)
761 boolean SpuIrq_old = 0;
762 boolean RCntFix_old = 0;
763 NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
764 NET_sendData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
765 NET_sendData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
766 NET_sendData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
767 NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
768 NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
776 if (NET_recvData == NULL || NET_sendData == NULL)
780 boolean SpuIrq_old = 0;
781 boolean RCntFix_old = 0;
782 NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
783 NET_recvData(&Sio_old, sizeof(Sio_old), PSE_NET_BLOCKING);
784 NET_recvData(&SpuIrq_old, sizeof(SpuIrq_old), PSE_NET_BLOCKING);
785 NET_recvData(&RCntFix_old, sizeof(RCntFix_old), PSE_NET_BLOCKING);
786 NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
791 NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
792 if (tmp != Config.Cpu) {
795 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
796 else psxCpu = &psxRec;
800 if (psxCpu->Init() == -1) {
801 SysClose(); return -1;
804 psxCpu->Notify(R3000ACPU_NOTIFY_AFTER_LOAD, NULL);
810 // remove the leading and trailing spaces in a string
811 void trim(char *str) {
815 // skip leading blanks
816 while (str[pos] <= ' ' && str[pos] > 0)
820 *(dest++) = str[pos];
824 *(dest--) = '\0'; // store the null
826 // remove trailing blanks
827 while (dest >= str && *dest <= ' ' && *dest > 0)
831 // lookup table for crc calculation
832 static unsigned short crctab[256] = {
833 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
834 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
835 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
836 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
837 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
838 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
839 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
840 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
841 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
842 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
843 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
844 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
845 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
846 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
847 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
848 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
849 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
850 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
851 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
852 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
853 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
854 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
855 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
856 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
857 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
858 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
859 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
860 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
861 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
864 u16 calcCrc(u8 *d, int len) {
868 for (i = 0; i < len; i++) {
869 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);