SHOW:
|
|
- or go back to the newest paste.
1 | /** | |
2 | * exobin | |
3 | * Compress a binary thomson file. To be compiled & linked along with exomizer2 code. | |
4 | * | |
5 | * (c) Samuel Devulder 2013-2014. | |
6 | */ | |
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <string.h> | |
10 | #include <ctype.h> | |
11 | ||
12 | #include <sys/stat.h> | |
13 | #include <sys/types.h> | |
14 | #include <dirent.h> | |
15 | ||
16 | #include "membuf_io.h" | |
17 | #include "exo_helper.h" | |
18 | ||
19 | ||
20 | #ifndef TRUE | |
21 | #define TRUE 1 | |
22 | #define FALSE 0 | |
23 | #endif | |
24 | ||
25 | typedef struct { | |
26 | char mem[65536]; | |
27 | int min,max,exe; | |
28 | } BIN; | |
29 | ||
30 | #define HEXADDR_NONE -1 | |
31 | #define HEXADDR_AUTO -2 | |
32 | ||
33 | static void convert(char *infile, char *outfile, int hexaddr); | |
34 | static void *alloc(int size); | |
35 | static BIN *read_bin(char *infile); | |
36 | static void process(char *filename, int hexaddr); | |
37 | static int get8(FILE *f); | |
38 | static int get16(FILE *f); | |
39 | static int endsWithIgnoreCase(char *s, char *end); | |
40 | ||
41 | int total = 0, total2 = 0, total3 = 0, num = 0; | |
42 | ||
43 | int main(int ac, char **av) { | |
44 | int i; | |
45 | int hexaddr = HEXADDR_NONE; | |
46 | ||
47 | LOG_INIT_CONSOLE(LOG_WARNING); | |
48 | ||
49 | for(i=1; i<ac; ++i) { | |
50 | struct stat buf; | |
51 | ||
52 | if(!strcmp(av[i], "-h") || !strcmp(av[i], "--help") || !strcmp(av[i], "?")) { | |
53 | fprintf(stderr, "Usage: %s [?|-h|--help] [-x[HEXADDR]] <files.bin or folder>\n",av[0]); | |
54 | fprintf(stderr, "\n\n"); | |
55 | fprintf(stderr, "Compresse un binaire thomson. Le fichier resultat est place a cote\n"); | |
56 | fprintf(stderr, "du fichier source, mais avec l'extension EXO au lieu de BIN.\n"); | |
57 | fprintf(stderr, "\n"); | |
58 | fprintf(stderr, "L'option -x produit une binaire auto-extractible. HEXADDR contient\n"); | |
59 | fprintf(stderr, "l'addresse hexadecimale du chargement. Si HEXADDR est absent, une\n"); | |
60 | fprintf(stderr, "une adresse est choisie automatiquement (eventuellement en ram video).\n"); | |
61 | exit(EXIT_SUCCESS); | |
62 | } else if(av[i][0]=='-' && av[i][1]=='x') { | |
63 | if(av[i][2]) { | |
64 | char *s = av[i]+2; | |
65 | int t = 0; | |
66 | while((*s>='0' && *s<='9') || | |
67 | (*s>='a' && *s<='f') || | |
68 | (*s>='A' && *s<='F')) { | |
69 | t <<= 4; | |
70 | if(*s>='0' && *s<='9') t += *s - '0'; | |
71 | if(*s>='a' && *s<='f') t += *s - 'a' + 10; | |
72 | if(*s>='A' && *s<='F') t += *s - 'A' + 10; | |
73 | ++s; | |
74 | } | |
75 | hexaddr = t & 0xFFFF; | |
76 | } else { | |
77 | hexaddr = HEXADDR_AUTO; | |
78 | } | |
79 | } else if(!stat(av[i], &buf)) { | |
80 | if(S_ISDIR(buf.st_mode)) { | |
81 | DIR *dir = opendir(av[i]); | |
82 | if(dir) { | |
83 | struct dirent *dirent; | |
84 | while((dirent = readdir(dir))!=NULL) if(endsWithIgnoreCase(dirent->d_name, ".BIN")) { | |
85 | char *s = alloc(strlen(av[i]) + strlen(dirent->d_name) + 2); | |
86 | strcpy(s, av[i]); | |
87 | strcat(s, "/"); | |
88 | strcat(s, dirent->d_name); | |
89 | process(s, hexaddr); | |
90 | free(s); | |
91 | } | |
92 | closedir(dir); | |
93 | } | |
94 | } else if(S_ISREG(buf.st_mode)) { | |
95 | process(av[i], hexaddr); | |
96 | } | |
97 | } | |
98 | } | |
99 | ||
100 | ||
101 | LOG_FREE; | |
102 | ||
103 | return EXIT_SUCCESS; | |
104 | } | |
105 | ||
106 | /** | |
107 | * compress the given file | |
108 | */ | |
109 | static void process(char *filename, int hexaddr) { | |
110 | char *out = alloc(3+strlen(filename)); | |
111 | char *s; | |
112 | ||
113 | /* change extension */ | |
114 | strcpy(out, filename); | |
115 | for(s=out; *s; ++s) {} | |
116 | while(s>out && *s!='.') --s; | |
117 | if(*s=='.') strcpy(s, ".EXO"); | |
118 | else strcat(s, ".EXO"); | |
119 | ||
120 | convert(filename, out, hexaddr); | |
121 | ||
122 | free(out); | |
123 | } | |
124 | ||
125 | /** | |
126 | * A calloc() that checks out of memory. | |
127 | */ | |
128 | static void *alloc(int size) { | |
129 | void *p = calloc(size, 1); | |
130 | if(p==NULL) {fprintf(stderr, "Out of memory!\n"); exit(EXIT_FAILURE);} | |
131 | return p; | |
132 | } | |
133 | ||
134 | unsigned char binary[] = { | |
135 | 0x1A,0x50, /* ORCC #$50 */ | |
136 | 0x10,0xCE,0x60,0xCC, /* LDS #$60CC */ | |
137 | #define OFFSET 6 | |
138 | 0x86,0x80, /* 8000 86 80 lda #*<-8 */ | |
139 | 0x1F,0x8B, /* 8002 1F 8B tfr a,dp */ | |
140 | 0xCE,0x83,0xE7, /* 8004 CE 83E7 ldu #biba */ | |
141 | 0x31,0xC4, /* 8007 31 C4 leay ,u */ | |
142 | 0x5F, /* 8009 5F clrb */ | |
143 | 0xD7,0x89, /* 800A D7 89 stb <bitbuf+1 */ | |
144 | 0x4F, /* 800C 4F nxt clra */ | |
145 | 0x34,0x06, /* 800D 34 06 pshs a,b */ | |
146 | 0xC5,0x0F, /* 800F C5 0F bitb #$0f */ | |
147 | 0x26,0x03, /* 8011 26 03 bne skp */ | |
148 | 0x8E,0x00,0x01, /* 8013 8E 0001 ldx #$0001 */ | |
149 | 0xC6,0x04, /* 8016 C6 04 skp ldb #4 */ | |
150 | 0x8D,0x6A, /* 8018 8D 6A bsr getbits */ | |
151 | 0xE7,0xC0, /* 801A E7 C0 stb ,u+ */ | |
152 | 0x53, /* 801C 53 comb */ | |
153 | 0x69,0xE4, /* 801D 69 E4 roll rol ,s */ | |
154 | 0x49, /* 801F 49 rola */ | |
155 | 0x5C, /* 8020 5C incb */ | |
156 | 0x2B,0xFA, /* 8021 2B FA bmi roll */ | |
157 | 0xE6,0xE4, /* 8023 E6 E4 ldb ,s */ | |
158 | 0xAF,0xC1, /* 8025 AF C1 stx ,u++ */ | |
159 | 0x30,0x8B, /* 8027 30 8B leax d,x */ | |
160 | 0x35,0x06, /* 8029 35 06 puls a,b */ | |
161 | 0x5C, /* 802B 5C incb */ | |
162 | 0xC1,0x34, /* 802C C1 34 cmpb #52 */ | |
163 | 0x26,0xDC, /* 802E 26 DC bne nxt */ | |
164 | 0xCE,0xA9,0x02, /* 8030 CE A902 go ldu #DEB+LEN */ | |
165 | 0xC6,0x01, /* 8033 C6 01 mloop ldb #1 */ | |
166 | 0x8D,0x4D, /* 8035 8D 4D bsr getbits */ | |
167 | 0x26,0x17, /* 8037 26 17 bne cpy */ | |
168 | 0xD7,0x44, /* 8039 D7 44 stb <idx+1 */ | |
169 | 0x8C, /* 803B 8C fcb $8c */ | |
170 | 0x0C,0x44, /* 803C 0C 44 rbl inc <idx+1 */ | |
171 | 0x5C, /* 803E 5C incb */ | |
172 | 0x8D,0x43, /* 803F 8D 43 bsr getbits */ | |
173 | 0x27,0xF9, /* 8041 27 F9 beq rbl */ | |
174 | 0xC6,0x00, /* 8043 C6 00 idx ldb #$00 */ | |
175 | 0xC1,0x10, /* 8045 C1 10 cmpb #$10 */ | |
176 | 0x10,0x27,0x1F,0xB5, /* 8047 1027 1FB5 lbeq EXE */ | |
177 | 0x25,0x0F, /* 804B 25 0F blo coffs */ | |
178 | 0x5A, /* 804D 5A decb */ | |
179 | 0x8D,0x34, /* 804E 8D 34 bsr getbits */ | |
180 | 0x1F,0x01, /* 8050 1F 01 cpy tfr d,x */ | |
181 | 0xA6,0xA2, /* 8052 A6 A2 cpyl lda ,-y */ | |
182 | 0xA7,0xC2, /* 8054 A7 C2 sta ,-u */ | |
183 | 0x30,0x1F, /* 8056 30 1F leax -1,x */ | |
184 | 0x26,0xF8, /* 8058 26 F8 bne cpyl */ | |
185 | 0x20,0xD7, /* 805A 20 D7 bra mloop */ | |
186 | 0x8D,0x3F, /* 805C 8D 3F coffs bsr cook */ | |
187 | 0x34,0x06, /* 805E 34 06 pshs d */ | |
188 | 0x8E,0x80,0xA8, /* 8060 8E 80A8 ldx #tab1 */ | |
189 | 0x10,0x83,0x00,0x03, /* 8063 1083 0003 cmpd #$03 */ | |
190 | 0x24,0x01, /* 8067 24 01 bhs scof */ | |
191 | 0x3A, /* 8069 3A abx */ | |
192 | 0x8D,0x16, /* 806A 8D 16 scof bsr getbix */ | |
193 | 0xEB,0x03, /* 806C EB 03 addb 3,x */ | |
194 | 0x8D,0x2D, /* 806E 8D 2D bsr cook */ | |
195 | 0xDD,0x78, /* 8070 DD 78 std <offs+2 */ | |
196 | 0x35,0x10, /* 8072 35 10 puls x */ | |
197 | 0x33,0x5F, /* 8074 33 5F cpy2 leau -1,u */ | |
198 | 0xA6,0xC9,0x55,0X55, /* 8076 A6 C9 5555 offs lda $5555,u */ | |
199 | 0xA7,0xC4, /* 807A A7 C4 sta ,u */ | |
200 | 0x30,0x1F, /* 807C 30 1F leax -1,x */ | |
201 | 0x26,0xF4, /* 807E 26 F4 bne cpy2 */ | |
202 | 0x20,0xB1, /* 8080 20 B1 bra mloop */ | |
203 | 0xE6,0x84, /* 8082 E6 84 getbix ldb ,x */ | |
204 | 0x6F,0xE2, /* 8084 6F E2 getbits clr ,-s */ | |
205 | 0x6F,0xE2, /* 8086 6F E2 clr ,-s */ | |
206 | 0x86,0x55, /* 8088 86 55 bitbuf lda #$55 */ | |
207 | 0x20,0x09, /* 808A 20 09 bra get3 */ | |
208 | 0xA6,0xA2, /* 808C A6 A2 get1 lda ,-y */ | |
209 | 0x46, /* 808E 46 get2 rora */ | |
210 | 0x27,0xFB, /* 808F 27 FB beq get1 */ | |
211 | 0x69,0x61, /* 8091 69 61 rol 1,s */ | |
212 | 0x69,0xE4, /* 8093 69 E4 rol ,s */ | |
213 | 0x5A, /* 8095 5A get3 decb */ | |
214 | 0x2A,0xF6, /* 8096 2A F6 bpl get2 */ | |
215 | 0x97,0x89, /* 8098 97 89 sta <bitbuf+1 */ | |
216 | 0xEC,0xE1, /* 809A EC E1 ldd ,s++ */ | |
217 | 0x39, /* 809C 39 rts */ | |
218 | 0x8E,0x83,0xE7, /* 809D 8E 83E7 cook ldx #biba */ | |
219 | 0x3A, /* 80A0 3A abx */ | |
220 | 0x58, /* 80A1 58 aslb */ | |
221 | 0x3A, /* 80A2 3A abx */ | |
222 | 0x8D,0xDD, /* 80A3 8D DD bsr getbix */ | |
223 | 0xE3,0x01, /* 80A5 E3 01 addd 1,x */ | |
224 | 0x39, /* 80A7 39 rts */ | |
225 | 0x04,0x02,0x04, /* 80A8 04 02 04 tab1 fcb 4,2,4 */ | |
226 | 0x10,0x30,0x20 /* 80AB 10 30 20 fcb 16,48,32 */ | |
227 | /* 80AE incdat FILE.exo */ | |
228 | /* 83E7 biba rmb 156 */ | |
229 | }; | |
230 | ||
231 | /** | |
232 | * converts BIN file "infile" to EXO file "outfile" | |
233 | */ | |
234 | static void convert(char *infile, char *outfile, int hexaddr) { | |
235 | BIN *bin = read_bin(infile); | |
236 | struct membuf inbuf[1]; | |
237 | struct membuf outbuf[1]; | |
238 | struct crunch_info info[1]; | |
239 | static struct crunch_options options[1] = { CRUNCH_OPTIONS_DEFAULT }; | |
240 | char *name = infile; | |
241 | int len; | |
242 | int decomp_size; | |
243 | ||
244 | if(!bin) return; | |
245 | ||
246 | while(*name) ++name; | |
247 | while(name>infile) { | |
248 | if(*name=='/' || *name=='\\') {++name; break;} | |
249 | --name; | |
250 | } | |
251 | ||
252 | /*data_start = membuf_memlen(inbuf);*/ | |
253 | ||
254 | membuf_init(outbuf); | |
255 | membuf_init(inbuf); | |
256 | len = bin->max - bin->min; | |
257 | membuf_append(inbuf, bin->mem + bin->min, len); | |
258 | ||
259 | crunch_backwards(inbuf, outbuf, options, info); | |
260 | /*reverse_buffer(membuf_get(outbuf), membuf_memlen(outbuf));*/ | |
261 | ||
262 | if(0) { | |
263 | struct membuf tstbuf[1]; | |
264 | char *src, *tst; int i; | |
265 | ||
266 | membuf_init(tstbuf); | |
267 | decrunch_backwards(LOG_NORMAL, outbuf, tstbuf); | |
268 | src = membuf_get(inbuf); | |
269 | tst = membuf_get(tstbuf); | |
270 | for(i=0; i<membuf_memlen(inbuf); ++i) { | |
271 | if(src[i]!=tst[i]) printf("%d: %d!=%d", i, src[i]&255, tst[i]&255); | |
272 | } | |
273 | } | |
274 | decomp_size = 156 + sizeof(binary) + membuf_memlen(outbuf); | |
275 | if(hexaddr == HEXADDR_AUTO) { | |
276 | hexaddr = bin->min - decomp_size; | |
277 | if(!((0x4000<=hexaddr && hexaddr+decomp_size<=0x5F40) || | |
278 | (0x6100<=hexaddr && hexaddr+decomp_size<=0xE000))) | |
279 | hexaddr = 0x5f40 - decomp_size; | |
280 | while(hexaddr>=0x4000 && | |
281 | - | ((hexaddr+0x44)>>8) != ((hexaddr + 0x89)>>8)) --hexaddr; |
281 | + | ((hexaddr+0x44+OFFSET)>>8) != ((hexaddr+0x89+OFFSET)>>8)) --hexaddr; |
282 | } | |
283 | ||
284 | /* validation */ | |
285 | if(hexaddr != HEXADDR_NONE) { | |
286 | fprintf(stderr, "%s: debut decomp: $%04x ", name, hexaddr); | |
287 | - | if(((hexaddr+0x44)>>8) != ((hexaddr + 0x89)>>8)) { |
287 | + | if(((hexaddr+0x44+OFFSET)>>8) != ((hexaddr+0x89+OFFSET)>>8)) { |
288 | fprintf(stderr, "KO (PAGE-BOUNDARY CROSSING)\n"); | |
289 | hexaddr = HEXADDR_NONE; | |
290 | } else if((bin->min<=hexaddr && hexaddr<bin->max) || | |
291 | (bin->min<=hexaddr+decomp_size-1 && hexaddr+decomp_size-1<bin->max)) { | |
292 | fprintf(stderr, "KO (COLLISION)\n"); | |
293 | hexaddr = HEXADDR_NONE; | |
294 | } else if(hexaddr<0x4000 || hexaddr+decomp_size>0xE000) { | |
295 | fprintf(stderr, "KO (ROM)\n"); | |
296 | hexaddr = HEXADDR_NONE; | |
297 | } else if((0x4000<=hexaddr && hexaddr+decomp_size<=0x5F40) || | |
298 | (0x6100<=hexaddr && hexaddr+decomp_size<=0xE000)) { | |
299 | fprintf(stderr, "OK\n"); | |
300 | } else { | |
301 | fprintf(stderr, "KO (PAGE0 CROSSING)\n"); | |
302 | hexaddr = HEXADDR_NONE; | |
303 | } | |
304 | } | |
305 | ||
306 | if(hexaddr != HEXADDR_NONE) { | |
307 | int len = sizeof(binary) + membuf_memlen(outbuf); | |
308 | int biba = hexaddr + len; | |
309 | int idx = hexaddr+0x43+OFFSET; | |
310 | int offs = hexaddr+0x76+OFFSET; | |
311 | int bitbuf = hexaddr+0x88+OFFSET; | |
312 | int tab1 = hexaddr+0xA8+OFFSET; | |
313 | char *buf = membuf_get(outbuf); | |
314 | ||
315 | int i; | |
316 | for(i=0; i<sizeof(binary); ++i) bin->mem[hexaddr + i] = binary[i]; | |
317 | for(i=membuf_memlen(outbuf); --i>=0;) | |
318 | bin->mem[hexaddr + sizeof(binary)+i] = buf[i]; | |
319 | ||
320 | bin->mem[hexaddr + 0x01 + OFFSET] = (idx+1)>>8; | |
321 | bin->mem[hexaddr + 0x05 + OFFSET] = (biba)>>8; | |
322 | bin->mem[hexaddr + 0x06 + OFFSET] = (biba)&255; | |
323 | bin->mem[hexaddr + 0x0B + OFFSET] = (bitbuf+1)&255; | |
324 | bin->mem[hexaddr + 0x31 + OFFSET] = bin->max>>8; | |
325 | bin->mem[hexaddr + 0x32 + OFFSET] = bin->max&255; | |
326 | bin->mem[hexaddr + 0x3A + OFFSET] = (idx+1)&255; | |
327 | bin->mem[hexaddr + 0x3D + OFFSET] = (idx+1)&255; | |
328 | bin->mem[hexaddr + 0x49 + OFFSET] = (bin->exe - (hexaddr+0x47+4+OFFSET))>>8; | |
329 | bin->mem[hexaddr + 0x4A + OFFSET] = (bin->exe - (hexaddr+0x47+4+OFFSET))&255; | |
330 | bin->mem[hexaddr + 0x61 + OFFSET] = (tab1)>>8; | |
331 | bin->mem[hexaddr + 0x62 + OFFSET] = (tab1)&255; | |
332 | bin->mem[hexaddr + 0x71 + OFFSET] = (offs+2)&255; | |
333 | bin->mem[hexaddr + 0x99 + OFFSET] = (bitbuf+1)&255; | |
334 | bin->mem[hexaddr + 0x9E + OFFSET] = (biba)>>8; | |
335 | bin->mem[hexaddr + 0x9F + OFFSET] = (biba)&255; | |
336 | ||
337 | membuf_truncate(outbuf, 0); | |
338 | ||
339 | membuf_append_char(outbuf, 0x00); | |
340 | membuf_append_char(outbuf, len>>8); | |
341 | membuf_append_char(outbuf, len&255); | |
342 | membuf_append_char(outbuf, hexaddr>>8); | |
343 | membuf_append_char(outbuf, hexaddr&255); | |
344 | for(i=0; i<len; ++i) | |
345 | membuf_append_char(outbuf, bin->mem[hexaddr + i]); | |
346 | ||
347 | membuf_append_char(outbuf, (char)0xFF); | |
348 | membuf_append_char(outbuf, 0x00); | |
349 | membuf_append_char(outbuf, 0x00); | |
350 | membuf_append_char(outbuf, hexaddr>>8); | |
351 | membuf_append_char(outbuf, hexaddr&255); | |
352 | } | |
353 | ||
354 | ++num; | |
355 | total += membuf_memlen(outbuf); | |
356 | total2 += len; | |
357 | ||
358 | fprintf(stdout, "%s ($%04x): %d -> %d (%d%%) (avg #%d : %d -> %d (%d%%))\n", | |
359 | name, bin->exe, | |
360 | len, membuf_memlen(outbuf), (100*membuf_memlen(outbuf))/len, | |
361 | num, | |
362 | total2/num, total/num, (100*total)/total2); | |
363 | ||
364 | ||
365 | /* | |
366 | membuf_truncate(outbuf, 0); | |
367 | crunch_backwards(inbuf, outbuf, options, info); | |
368 | fprintf(stdout, "%s : <<< %d -> %d\n", name, img->length, membuf_memlen(outbuf)); | |
369 | */ | |
370 | ||
371 | /* | |
372 | LOG(LOG_NORMAL, (" Literal sequences are %sused and", | |
373 | info->literal_sequences_used ? "" : "not ")); | |
374 | LOG(LOG_NORMAL, (" the safety offset is %d.\n", | |
375 | info->needed_safety_offset)); | |
376 | */ | |
377 | ||
378 | write_file(outfile, outbuf); | |
379 | ||
380 | membuf_free(outbuf); | |
381 | membuf_free(inbuf); | |
382 | free(bin); | |
383 | } | |
384 | ||
385 | static BIN *read_bin(char *infile) { | |
386 | FILE *f = fopen(infile, "rb"); | |
387 | BIN *bin = NULL; | |
388 | if(f!=NULL) { | |
389 | int c; | |
390 | bin = alloc(sizeof(BIN)); | |
391 | bin->min = sizeof(bin->mem); | |
392 | bin->max = 0; | |
393 | while((c=get8(f))>=0) { | |
394 | int len = get16(f), adr; | |
395 | if(len<0) {c=len; break;} | |
396 | if(c==0xFF) { | |
397 | if((c = get16(f))<0) break; | |
398 | bin->exe = c; | |
399 | break; | |
400 | } else if(c==0x00) { | |
401 | if((c = get16(f))<0) break; | |
402 | adr = c; | |
403 | if(adr<0x6100) { | |
404 | fprintf(stderr, "Can't write below page 0 ($%04X)\n", adr); | |
405 | break; | |
406 | } | |
407 | if(adr<bin->min) bin->min = adr; | |
408 | while(len--) { | |
409 | if((c=get8(f))<0) break; | |
410 | if(adr>=0xE000) { | |
411 | fprintf(stderr, "Can't write in ROM space ($%04X)\n", adr); | |
412 | break; | |
413 | } | |
414 | bin->mem[adr++] = c; | |
415 | } | |
416 | if(adr>bin->max) bin->max = adr; | |
417 | } else { | |
418 | fprintf(stderr, "Skipping unknown chunk (len=%d)\n", len); | |
419 | while(len--) if((c=get8(f))<0) break; | |
420 | } | |
421 | } | |
422 | if(c<0) {perror(infile); free(bin); bin = NULL;} | |
423 | fclose(f); | |
424 | } else perror(infile); | |
425 | return bin; | |
426 | } | |
427 | ||
428 | static int get8(FILE *f) { | |
429 | int c = fgetc(f); | |
430 | if(c==EOF) return -1; | |
431 | return c; | |
432 | } | |
433 | ||
434 | static int get16(FILE *f) { | |
435 | int t = get8(f), r; | |
436 | if(t<0) return t; | |
437 | r = get8(f); | |
438 | if(r<0) return r; | |
439 | return (t<<8) | r; | |
440 | } | |
441 | ||
442 | static int endsWithIgnoreCase(char *s, char *end) { | |
443 | char *t = s, *u=end; | |
444 | while(*t) ++t; | |
445 | while(*u) ++u; | |
446 | while(--u>=end && --t>=s && tolower((int)*u) != tolower((int)*t)); | |
447 | return u<end; | |
448 | } |