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