gpu-gles: schtruck/fpse merge: rearrangement
[pcsx_rearmed.git] / libpcsxcore / misc.c
1 /***************************************************************************
2  *   Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team              *
3  *                                                                         *
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.                                   *
8  *                                                                         *
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.                          *
13  *                                                                         *
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  ***************************************************************************/
19
20 /*
21 * Miscellaneous functions, including savestates and CD-ROM loading.
22 */
23
24 #include "misc.h"
25 #include "cdrom.h"
26 #include "mdec.h"
27 #include "gpu.h"
28 #include "ppf.h"
29
30 char CdromId[10] = "";
31 char CdromLabel[33] = "";
32
33 // PSX Executable types
34 #define PSX_EXE     1
35 #define CPE_EXE     2
36 #define COFF_EXE    3
37 #define INVALID_EXE 4
38
39 #define ISODCL(from, to) (to - from + 1)
40
41 struct iso_directory_record {
42         char length                     [ISODCL (1, 1)]; /* 711 */
43         char ext_attr_length            [ISODCL (2, 2)]; /* 711 */
44         char extent                     [ISODCL (3, 10)]; /* 733 */
45         char size                       [ISODCL (11, 18)]; /* 733 */
46         char date                       [ISODCL (19, 25)]; /* 7 by 711 */
47         char flags                      [ISODCL (26, 26)];
48         char file_unit_size             [ISODCL (27, 27)]; /* 711 */
49         char interleave                 [ISODCL (28, 28)]; /* 711 */
50         char volume_sequence_number     [ISODCL (29, 32)]; /* 723 */
51         unsigned char name_len          [ISODCL (33, 33)]; /* 711 */
52         char name                       [1];
53 };
54
55 void mmssdd( char *b, char *p )
56 {
57         int m, s, d;
58 #if defined(__arm__)
59         int block = (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
60 #elif defined(__BIGENDIAN__)
61         int block = (b[0] & 0xff) | ((b[1] & 0xff) << 8) | ((b[2] & 0xff) << 16) | (b[3] << 24);
62 #else
63         int block = *((int*)b);
64 #endif
65
66         block += 150;
67         m = block / 4500;                       // minutes
68         block = block - m * 4500;       // minutes rest
69         s = block / 75;                         // seconds
70         d = block - s * 75;                     // seconds rest
71
72         m = ((m / 10) << 4) | m % 10;
73         s = ((s / 10) << 4) | s % 10;
74         d = ((d / 10) << 4) | d % 10;   
75
76         p[0] = m;
77         p[1] = s;
78         p[2] = d;
79 }
80
81 #define incTime() \
82         time[0] = btoi(time[0]); time[1] = btoi(time[1]); time[2] = btoi(time[2]); \
83         time[2]++; \
84         if(time[2] == 75) { \
85                 time[2] = 0; \
86                 time[1]++; \
87                 if (time[1] == 60) { \
88                         time[1] = 0; \
89                         time[0]++; \
90                 } \
91         } \
92         time[0] = itob(time[0]); time[1] = itob(time[1]); time[2] = itob(time[2]);
93
94 #define READTRACK() \
95         if (CDR_readTrack(time) == -1) return -1; \
96         buf = CDR_getBuffer(); \
97         if (buf == NULL) return -1; else CheckPPFCache(buf, time[0], time[1], time[2]);
98
99 #define READDIR(_dir) \
100         READTRACK(); \
101         memcpy(_dir, buf + 12, 2048); \
102  \
103         incTime(); \
104         READTRACK(); \
105         memcpy(_dir + 2048, buf + 12, 2048);
106
107 int GetCdromFile(u8 *mdir, u8 *time, s8 *filename) {
108         struct iso_directory_record *dir;
109         char ddir[4096];
110         u8 *buf;
111         int i;
112
113         // only try to scan if a filename is given
114         if (!strlen(filename)) return -1;
115
116         i = 0;
117         while (i < 4096) {
118                 dir = (struct iso_directory_record*) &mdir[i];
119                 if (dir->length[0] == 0) {
120                         return -1;
121                 }
122                 i += dir->length[0];
123
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;
127
128                                 filename += dir->name_len[0] + 1;
129
130                                 mmssdd(dir->extent, (char *)time);
131                                 READDIR(ddir);
132                                 i = 0;
133                                 mdir = ddir;
134                         }
135                 } else {
136                         if (!strnicmp((char *)&dir->name[0], filename, strlen(filename))) {
137                                 mmssdd(dir->extent, (char *)time);
138                                 break;
139                         }
140                 }
141         }
142         return 0;
143 }
144
145 int LoadCdrom() {
146         EXE_HEADER tmpHead;
147         struct iso_directory_record *dir;
148         u8 time[4], *buf;
149         u8 mdir[4096];
150         s8 exename[256];
151
152         if (!Config.HLE) {
153                 psxRegs.pc = psxRegs.GPR.n.ra;
154                 return 0;
155         }
156
157         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
158
159         READTRACK();
160
161         // skip head and sub, and go to the root directory record
162         dir = (struct iso_directory_record*) &buf[12+156]; 
163
164         mmssdd(dir->extent, (char*)time);
165
166         READDIR(mdir);
167
168         // Load SYSTEM.CNF and scan for the main executable
169         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") == -1) {
170                 // if SYSTEM.CNF is missing, start an existing PSX.EXE
171                 if (GetCdromFile(mdir, time, "PSX.EXE;1") == -1) return -1;
172
173                 READTRACK();
174         }
175         else {
176                 // read the SYSTEM.CNF
177                 READTRACK();
178
179                 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
180                 if (GetCdromFile(mdir, time, exename) == -1) {
181                         sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
182                         if (GetCdromFile(mdir, time, exename) == -1) {
183                                 char *ptr = strstr(buf + 12, "cdrom:");
184                                 if (ptr != NULL) {
185                                         ptr += 6;
186                                         while (*ptr == '\\' || *ptr == '/') ptr++;
187                                         strncpy(exename, ptr, 255);
188                                         exename[255] = '\0';
189                                         ptr = exename;
190                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
191                                         *ptr = '\0';
192                                         if (GetCdromFile(mdir, time, exename) == -1)
193                                                 return -1;
194                                 } else
195                                         return -1;
196                         }
197                 }
198
199                 // Read the EXE-Header
200                 READTRACK();
201         }
202
203         memcpy(&tmpHead, buf + 12, sizeof(EXE_HEADER));
204
205         psxRegs.pc = SWAP32(tmpHead.pc0);
206         psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
207         psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr); 
208         if (psxRegs.GPR.n.sp == 0) psxRegs.GPR.n.sp = 0x801fff00;
209
210         tmpHead.t_size = SWAP32(tmpHead.t_size);
211         tmpHead.t_addr = SWAP32(tmpHead.t_addr);
212
213         psxCpu->Clear(tmpHead.t_addr, tmpHead.t_size / 4);
214
215         // Read the rest of the main executable
216         while (tmpHead.t_size & ~2047) {
217                 void *ptr = (void *)PSXM(tmpHead.t_addr);
218
219                 incTime();
220                 READTRACK();
221
222                 if (ptr != NULL) memcpy(ptr, buf+12, 2048);
223
224                 tmpHead.t_size -= 2048;
225                 tmpHead.t_addr += 2048;
226         }
227
228         return 0;
229 }
230
231 int LoadCdromFile(const char *filename, EXE_HEADER *head) {
232         struct iso_directory_record *dir;
233         u8 time[4],*buf;
234         u8 mdir[4096], exename[256];
235         u32 size, addr;
236
237         sscanf(filename, "cdrom:\\%256s", exename);
238
239         time[0] = itob(0); time[1] = itob(2); time[2] = itob(0x10);
240
241         READTRACK();
242
243         // skip head and sub, and go to the root directory record
244         dir = (struct iso_directory_record *)&buf[12 + 156]; 
245
246         mmssdd(dir->extent, (char*)time);
247
248         READDIR(mdir);
249
250         if (GetCdromFile(mdir, time, exename) == -1) return -1;
251
252         READTRACK();
253
254         memcpy(head, buf + 12, sizeof(EXE_HEADER));
255         size = head->t_size;
256         addr = head->t_addr;
257
258         psxCpu->Clear(addr, size / 4);
259
260         while (size & ~2047) {
261                 incTime();
262                 READTRACK();
263
264                 memcpy((void *)PSXM(addr), buf + 12, 2048);
265
266                 size -= 2048;
267                 addr += 2048;
268         }
269
270         return 0;
271 }
272
273 int CheckCdrom() {
274         struct iso_directory_record *dir;
275         unsigned char time[4], *buf;
276         unsigned char mdir[4096];
277         char exename[256];
278         int i, c;
279
280         FreePPFCache();
281
282         time[0] = itob(0);
283         time[1] = itob(2);
284         time[2] = itob(0x10);
285
286         READTRACK();
287
288         CdromLabel[0] = '\0';
289         CdromId[0] = '\0';
290
291         strncpy(CdromLabel, buf + 52, 32);
292
293         // skip head and sub, and go to the root directory record
294         dir = (struct iso_directory_record *)&buf[12 + 156]; 
295
296         mmssdd(dir->extent, (char *)time);
297
298         READDIR(mdir);
299
300         if (GetCdromFile(mdir, time, "SYSTEM.CNF;1") != -1) {
301                 READTRACK();
302
303                 sscanf((char *)buf + 12, "BOOT = cdrom:\\%256s", exename);
304                 if (GetCdromFile(mdir, time, exename) == -1) {
305                         sscanf((char *)buf + 12, "BOOT = cdrom:%256s", exename);
306                         if (GetCdromFile(mdir, time, exename) == -1) {
307                                 char *ptr = strstr(buf + 12, "cdrom:");                 // possibly the executable is in some subdir
308                                 if (ptr != NULL) {
309                                         ptr += 6;
310                                         while (*ptr == '\\' || *ptr == '/') ptr++;
311                                         strncpy(exename, ptr, 255);
312                                         exename[255] = '\0';
313                                         ptr = exename;
314                                         while (*ptr != '\0' && *ptr != '\r' && *ptr != '\n') ptr++;
315                                         *ptr = '\0';
316                                         if (GetCdromFile(mdir, time, exename) == -1)
317                                                 return -1;              // main executable not found
318                                 } else
319                                         return -1;
320                         }
321                 }
322         } else if (GetCdromFile(mdir, time, "PSX.EXE;1") != -1) {
323                 strcpy(exename, "PSX.EXE;1");
324                 strcpy(CdromId, "SLUS99999");
325         } else
326                 return -1;              // SYSTEM.CNF and PSX.EXE not found
327
328         if (CdromId[0] == '\0') {
329                 i = strlen(exename);
330                 if (i >= 2) {
331                         if (exename[i - 2] == ';') i-= 2;
332                         c = 8; i--;
333                         while (i >= 0 && c >= 0) {
334                                 if (isalnum(exename[i])) CdromId[c--] = exename[i];
335                                 i--;
336                         }
337                 }
338         }
339
340         if (Config.PsxAuto) { // autodetect system (pal or ntsc)
341                 if (CdromId[2] == 'e' || CdromId[2] == 'E')
342                         Config.PsxType = PSX_TYPE_PAL; // pal
343                 else Config.PsxType = PSX_TYPE_NTSC; // ntsc
344         }
345
346         if (CdromLabel[0] == ' ') {
347                 strncpy(CdromLabel, CdromId, 9);
348         }
349         SysPrintf(_("CD-ROM Label: %.32s\n"), CdromLabel);
350         SysPrintf(_("CD-ROM ID: %.9s\n"), CdromId);
351
352         BuildPPFCache();
353
354         return 0;
355 }
356
357 static int PSXGetFileType(FILE *f) {
358         unsigned long current;
359         u8 mybuf[2048];
360         EXE_HEADER *exe_hdr;
361         FILHDR *coff_hdr;
362
363         current = ftell(f);
364         fseek(f, 0L, SEEK_SET);
365         fread(mybuf, 2048, 1, f);
366         fseek(f, current, SEEK_SET);
367
368         exe_hdr = (EXE_HEADER *)mybuf;
369         if (memcmp(exe_hdr->id, "PS-X EXE", 8) == 0)
370                 return PSX_EXE;
371
372         if (mybuf[0] == 'C' && mybuf[1] == 'P' && mybuf[2] == 'E')
373                 return CPE_EXE;
374
375         coff_hdr = (FILHDR *)mybuf;
376         if (SWAPu16(coff_hdr->f_magic) == 0x0162)
377                 return COFF_EXE;
378
379         return INVALID_EXE;
380 }
381
382 int Load(const char *ExePath) {
383         FILE *tmpFile;
384         EXE_HEADER tmpHead;
385         int type;
386         int retval = 0;
387         u8 opcode;
388         u32 section_address, section_size;
389         void *mem;
390
391         strncpy(CdromId, "SLUS99999", 9);
392         strncpy(CdromLabel, "SLUS_999.99", 11);
393
394         tmpFile = fopen(ExePath, "rb");
395         if (tmpFile == NULL) {
396                 SysPrintf(_("Error opening file: %s.\n"), ExePath);
397                 retval = -1;
398         } else {
399                 type = PSXGetFileType(tmpFile);
400                 switch (type) {
401                         case PSX_EXE:
402                                 fread(&tmpHead,sizeof(EXE_HEADER),1,tmpFile);
403                                 section_address = SWAP32(tmpHead.t_addr);
404                                 section_size = SWAP32(tmpHead.t_size);
405                                 mem = PSXM(section_address);
406                                 if (mem != NULL) {
407                                         fseek(tmpFile, 0x800, SEEK_SET);                
408                                         fread(mem, section_size, 1, tmpFile);
409                                         psxCpu->Clear(section_address, section_size / 4);
410                                 }
411                                 fclose(tmpFile);
412                                 psxRegs.pc = SWAP32(tmpHead.pc0);
413                                 psxRegs.GPR.n.gp = SWAP32(tmpHead.gp0);
414                                 psxRegs.GPR.n.sp = SWAP32(tmpHead.s_addr); 
415                                 if (psxRegs.GPR.n.sp == 0)
416                                         psxRegs.GPR.n.sp = 0x801fff00;
417                                 retval = 0;
418                                 break;
419                         case CPE_EXE:
420                                 fseek(tmpFile, 6, SEEK_SET); /* Something tells me we should go to 4 and read the "08 00" here... */
421                                 do {
422                                         fread(&opcode, 1, 1, tmpFile);
423                                         switch (opcode) {
424                                                 case 1: /* Section loading */
425                                                         fread(&section_address, 4, 1, tmpFile);
426                                                         fread(&section_size, 4, 1, tmpFile);
427                                                         section_address = SWAPu32(section_address);
428                                                         section_size = SWAPu32(section_size);
429 #ifdef EMU_LOG
430                                                         EMU_LOG("Loading %08X bytes from %08X to %08X\n", section_size, ftell(tmpFile), section_address);
431 #endif
432                                                         mem = PSXM(section_address);
433                                                         if (mem != NULL) {
434                                                                 fread(mem, section_size, 1, tmpFile);
435                                                                 psxCpu->Clear(section_address, section_size / 4);
436                                                         }
437                                                         break;
438                                                 case 3: /* register loading (PC only?) */
439                                                         fseek(tmpFile, 2, SEEK_CUR); /* unknown field */
440                                                         fread(&psxRegs.pc, 4, 1, tmpFile);
441                                                         psxRegs.pc = SWAPu32(psxRegs.pc);
442                                                         break;
443                                                 case 0: /* End of file */
444                                                         break;
445                                                 default:
446                                                         SysPrintf(_("Unknown CPE opcode %02x at position %08x.\n"), opcode, ftell(tmpFile) - 1);
447                                                         retval = -1;
448                                                         break;
449                                         }
450                                 } while (opcode != 0 && retval == 0);
451                                 break;
452                         case COFF_EXE:
453                                 SysPrintf(_("COFF files not supported.\n"));
454                                 retval = -1;
455                                 break;
456                         case INVALID_EXE:
457                                 SysPrintf(_("This file does not appear to be a valid PSX file.\n"));
458                                 retval = -1;
459                                 break;
460                 }
461         }
462
463         if (retval != 0) {
464                 CdromId[0] = '\0';
465                 CdromLabel[0] = '\0';
466         }
467
468         return retval;
469 }
470
471 // STATES
472
473 static const char PcsxHeader[32] = "STv4 PCSX v" PACKAGE_VERSION;
474
475 // Savestate Versioning!
476 // If you make changes to the savestate version, please increment the value below.
477 static const u32 SaveVersion = 0x8b410006;
478
479 int SaveState(const char *file) {
480         gzFile f;
481         GPUFreeze_t *gpufP;
482         SPUFreeze_t *spufP;
483         int Size;
484         unsigned char *pMem;
485
486         f = gzopen(file, "wb");
487         if (f == NULL) return -1;
488
489         new_dyna_save();
490
491         gzwrite(f, (void *)PcsxHeader, 32);
492         gzwrite(f, (void *)&SaveVersion, sizeof(u32));
493         gzwrite(f, (void *)&Config.HLE, sizeof(boolean));
494
495         pMem = (unsigned char *)malloc(128 * 96 * 3);
496         if (pMem == NULL) return -1;
497         GPU_getScreenPic(pMem);
498         gzwrite(f, pMem, 128 * 96 * 3);
499         free(pMem);
500
501         if (Config.HLE)
502                 psxBiosFreeze(1);
503
504         gzwrite(f, psxM, 0x00200000);
505         gzwrite(f, psxR, 0x00080000);
506         gzwrite(f, psxH, 0x00010000);
507         gzwrite(f, (void *)&psxRegs, sizeof(psxRegs));
508
509         // gpu
510         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
511         gpufP->ulFreezeVersion = 1;
512         GPU_freeze(1, gpufP);
513         gzwrite(f, gpufP, sizeof(GPUFreeze_t));
514         free(gpufP);
515
516         // spu
517         spufP = (SPUFreeze_t *) malloc(16);
518         SPU_freeze(2, spufP);
519         Size = spufP->Size; gzwrite(f, &Size, 4);
520         free(spufP);
521         spufP = (SPUFreeze_t *) malloc(Size);
522         SPU_freeze(1, spufP);
523         gzwrite(f, spufP, Size);
524         free(spufP);
525
526         sioFreeze(f, 1);
527         cdrFreeze(f, 1);
528         psxHwFreeze(f, 1);
529         psxRcntFreeze(f, 1);
530         mdecFreeze(f, 1);
531
532         gzclose(f);
533
534         new_dyna_after_save();
535
536         return 0;
537 }
538
539 int LoadState(const char *file) {
540         gzFile f;
541         GPUFreeze_t *gpufP;
542         SPUFreeze_t *spufP;
543         int Size;
544         char header[32];
545         u32 version;
546         boolean hle;
547
548         f = gzopen(file, "rb");
549         if (f == NULL) return -1;
550
551         gzread(f, header, sizeof(header));
552         gzread(f, &version, sizeof(u32));
553         gzread(f, &hle, sizeof(boolean));
554
555         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion) {
556                 gzclose(f);
557                 return -1;
558         }
559         Config.HLE = hle;
560
561         if (Config.HLE)
562                 psxBiosInit();
563
564         psxCpu->Reset();
565         gzseek(f, 128 * 96 * 3, SEEK_CUR);
566
567         gzread(f, psxM, 0x00200000);
568         gzread(f, psxR, 0x00080000);
569         gzread(f, psxH, 0x00010000);
570         gzread(f, (void *)&psxRegs, sizeof(psxRegs));
571
572         if (Config.HLE)
573                 psxBiosFreeze(0);
574
575         // gpu
576         gpufP = (GPUFreeze_t *)malloc(sizeof(GPUFreeze_t));
577         gzread(f, gpufP, sizeof(GPUFreeze_t));
578         GPU_freeze(0, gpufP);
579         free(gpufP);
580         if (HW_GPU_STATUS == 0)
581                 HW_GPU_STATUS = GPU_readStatus();
582
583         // spu
584         gzread(f, &Size, 4);
585         spufP = (SPUFreeze_t *)malloc(Size);
586         gzread(f, spufP, Size);
587         SPU_freeze(0, spufP);
588         free(spufP);
589
590         sioFreeze(f, 0);
591         cdrFreeze(f, 0);
592         psxHwFreeze(f, 0);
593         psxRcntFreeze(f, 0);
594         mdecFreeze(f, 0);
595
596         gzclose(f);
597         new_dyna_restore();
598
599         return 0;
600 }
601
602 int CheckState(const char *file) {
603         gzFile f;
604         char header[32];
605         u32 version;
606         boolean hle;
607
608         f = gzopen(file, "rb");
609         if (f == NULL) return -1;
610
611         gzread(f, header, sizeof(header));
612         gzread(f, &version, sizeof(u32));
613         gzread(f, &hle, sizeof(boolean));
614
615         gzclose(f);
616
617         if (strncmp("STv4 PCSX", header, 9) != 0 || version != SaveVersion)
618                 return -1;
619
620         return 0;
621 }
622
623 // NET Function Helpers
624
625 int SendPcsxInfo() {
626         if (NET_recvData == NULL || NET_sendData == NULL)
627                 return 0;
628
629         NET_sendData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
630         NET_sendData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
631         NET_sendData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
632         NET_sendData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
633         NET_sendData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
634         NET_sendData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
635
636         return 0;
637 }
638
639 int RecvPcsxInfo() {
640         int tmp;
641
642         if (NET_recvData == NULL || NET_sendData == NULL)
643                 return 0;
644
645         NET_recvData(&Config.Xa, sizeof(Config.Xa), PSE_NET_BLOCKING);
646         NET_recvData(&Config.Sio, sizeof(Config.Sio), PSE_NET_BLOCKING);
647         NET_recvData(&Config.SpuIrq, sizeof(Config.SpuIrq), PSE_NET_BLOCKING);
648         NET_recvData(&Config.RCntFix, sizeof(Config.RCntFix), PSE_NET_BLOCKING);
649         NET_recvData(&Config.PsxType, sizeof(Config.PsxType), PSE_NET_BLOCKING);
650
651         SysUpdate();
652
653         tmp = Config.Cpu;
654         NET_recvData(&Config.Cpu, sizeof(Config.Cpu), PSE_NET_BLOCKING);
655         if (tmp != Config.Cpu) {
656                 psxCpu->Shutdown();
657 #ifdef PSXREC
658                 if (Config.Cpu == CPU_INTERPRETER) psxCpu = &psxInt;
659                 else psxCpu = &psxRec;
660 #else
661                 psxCpu = &psxInt;
662 #endif
663                 if (psxCpu->Init() == -1) {
664                         SysClose(); return -1;
665                 }
666                 psxCpu->Reset();
667         }
668
669         return 0;
670 }
671
672 // remove the leading and trailing spaces in a string
673 void trim(char *str) {
674         int pos = 0;
675         char *dest = str;
676
677         // skip leading blanks
678         while (str[pos] <= ' ' && str[pos] > 0)
679                 pos++;
680
681         while (str[pos]) {
682                 *(dest++) = str[pos];
683                 pos++;
684         }
685
686         *(dest--) = '\0'; // store the null
687
688         // remove trailing blanks
689         while (dest >= str && *dest <= ' ' && *dest > 0)
690                 *(dest--) = '\0';
691 }
692
693 // lookup table for crc calculation
694 static unsigned short crctab[256] = {
695         0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108,
696         0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210,
697         0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B,
698         0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401,
699         0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE,
700         0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6,
701         0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D,
702         0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
703         0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5,
704         0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC,
705         0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4,
706         0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD,
707         0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13,
708         0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A,
709         0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E,
710         0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
711         0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1,
712         0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB,
713         0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0,
714         0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8,
715         0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657,
716         0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9,
717         0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882,
718         0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
719         0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E,
720         0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07,
721         0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D,
722         0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74,
723         0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
724 };
725
726 u16 calcCrc(u8 *d, int len) {
727         u16 crc = 0;
728         int i;
729
730         for (i = 0; i < len; i++) {
731                 crc = crctab[(crc >> 8) ^ d[i]] ^ (crc << 8);
732         }
733
734         return ~crc;
735 }