View difference between Paste ID: pLjHRC9H and QVxBYF6X
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
}