fixed compiled sprites
[dosdemo] / tools / csprite / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdint.h>
5 #include <assert.h>
6 #include <alloca.h>
7 #include "image.h"
8
9 struct rect {
10         int x, y, w, h;
11 };
12
13 enum { AS_GNU, AS_NASM };
14
15 int csprite(struct image *img, int x, int y, int xsz, int ysz);
16 int proc_sheet(const char *fname);
17 void print_usage(const char *argv0);
18
19 int tile_xsz, tile_ysz;
20 struct rect rect;
21 int cmap_offs;
22 int ckey;
23 int fbpitch, fbwidth = 320;
24 const char *name = "sprite";
25 int asyntax = AS_GNU;
26 int conv565;
27 int padding;
28 const char *wrop = "mov";
29
30 int main(int argc, char **argv)
31 {
32         int i;
33         char *endp;
34
35         for(i=1; i<argc; i++) {
36                 if(argv[i][0] == '-') {
37                         if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "-size") == 0) {
38                                 if(sscanf(argv[++i], "%dx%d", &tile_xsz, &tile_ysz) != 2 ||
39                                                 tile_xsz <= 0 || tile_ysz <= 0) {
40                                         fprintf(stderr, "%s must be followed by WIDTHxHEIGHT\n", argv[i - 1]);
41                                         return 1;
42                                 }
43
44                         } else if(strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "-rect") == 0) {
45                                 rect.x = rect.y = 0;
46                                 if(sscanf(argv[++i], "%dx%d+%d+%d", &rect.w, &rect.h, &rect.x, &rect.y) < 2 || rect.w <= 0 || rect.h <= 0) {
47                                         fprintf(stderr, "%s must be followed by WIDTHxHEIGHT+X+Y\n", argv[i - 1]);
48                                         return 1;
49                                 }
50
51                         } else if(strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "-pad") == 0) {
52                                 padding = strtol(argv[++i], &endp, 10);
53                                 if(endp == argv[i] || padding < 0) {
54                                         fprintf(stderr, "%s must be followed by a positive number\n", argv[i - 1]);
55                                         return 1;
56                                 }
57
58                         } else if(strcmp(argv[i], "-coffset") == 0) {
59                                 cmap_offs = strtol(argv[++i], &endp, 10);
60                                 if(endp == argv[i] || cmap_offs < 0 || cmap_offs >= 256) {
61                                         fprintf(stderr, "-coffset must be followed by a valid colormap offset\n");
62                                         return 1;
63                                 }
64
65                         } else if(strcmp(argv[i], "-fbwidth") == 0) {
66                                 fbwidth = atoi(argv[++i]);
67                                 if(fbwidth <= 0) {
68                                         fprintf(stderr, "-fbwidth must be followed by a positive number\n");
69                                         return 1;
70                                 }
71
72                         } else if(strcmp(argv[i], "-fbpitch") == 0) {
73                                 fbpitch = atoi(argv[++i]);
74                                 if(fbpitch <= 0) {
75                                         fprintf(stderr, "-fbpitch must be followed by a positive number\n");
76                                         return 1;
77                                 }
78
79                         } else if(strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "-name") == 0) {
80                                 name = argv[++i];
81                                 if(!name) {
82                                         fprintf(stderr, "%s must be followed by a name\n", argv[i - 1]);
83                                         return 1;
84                                 }
85
86                         } else if(strcmp(argv[i], "-k") == 0 || strcmp(argv[i], "-key") == 0) {
87                                 ckey = strtol(argv[++i], &endp, 10);
88                                 if(endp == argv[i] || ckey < 0) {
89                                         fprintf(stderr, "%s must be followed by a valid color key\n", argv[i - 1]);
90                                         return 1;
91                                 }
92
93                         } else if(strcmp(argv[i], "-conv565") == 0) {
94                                 conv565 = 1;
95
96                         } else if(strcmp(argv[i], "-x") == 0 || strcmp(argv[i], "-xor") == 0) {
97                                 wrop = "xor";
98
99                         } else if(strcmp(argv[i], "-gas") == 0) {
100                                 asyntax = AS_GNU;
101
102                         } else if(strcmp(argv[i], "-nasm") == 0) {
103                                 asyntax = AS_NASM;
104
105                         } else if(strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-help") == 0) {
106                                 print_usage(argv[0]);
107                                 return 0;
108
109                         } else {
110                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
111                                 print_usage(argv[0]);
112                                 return 1;
113                         }
114
115                 } else {
116                         if(proc_sheet(argv[i]) == -1) {
117                                 return 1;
118                         }
119                 }
120         }
121
122         return 0;
123 }
124
125 /* prototype of generated function is (void *fb, int x, int y, int idx) */
126 const char *prefixfmt[] = {
127         /* GNU assembler template */
128         "\t.text\n"
129         "\t.global %s\n"
130         "\t.global _%s\n"
131         "%s:\n"
132         "_%s:\n"
133         "\tmov 12(%%esp), %%eax\n"
134         "\tmov $%d, %%ecx\n"
135         "\tmul %%ecx\n"
136         "\tadd 8(%%esp), %%eax\n"
137         "\tadd 4(%%esp), %%eax\n"
138         "\tmov %%eax, %%edx\n"
139         "\tmov 16(%%esp), %%eax\n"
140         "\tjmp *tiletab(,%%eax,4)\n\n"
141         "tiletab:\n",
142
143         /* NASM template */
144         /* TODO hardcoding the 16bpp changes for now, generalize later
145          *      and while we're at it, let's get rid of the mul too ...
146          */
147         "\tsection .text USE32\n"
148         "\tglobal %s\n"
149         "\tglobal _%s\n"
150         "\tglobal %s_\n"
151         "%s:\n"
152         "_%s:\n"
153         "\tmov eax, [esp + 12]\n"
154         ";\tmov ecx, %d\n"
155         ";\tmul ecx\n"
156         "\tmov ecx, eax\n"
157         "\tshl eax, 9\n"
158         "\tshl ecx, 7\n"
159         "\tadd eax, ecx\n"
160         "\tadd eax, [esp + 8]\n"
161         "\tadd eax, [esp + 8]\n"
162         "\tadd eax, [esp + 4]\n"
163         "\tmov edx, eax\n"
164         "\tmov eax, [esp + 16]\n"
165         "\tjmp [titletab + eax * 4]\n\n"
166         "%s_:\n"
167         "\tpush eax\n"
168         "\tmov eax, ebx\n"
169         "\tshl eax, 9\n"
170         "\tshl ebx, 7\n"
171         "\tadd eax, ebx\n"
172         "\tshl edx, 1\n"
173         "\tadd eax, edx\n"
174         "\tpop edx\n"
175         "\tadd edx, eax\n"
176         "\tjmp [titletab + ecx * 4]\n\n"
177         "titletab:\n"
178 };
179
180 int proc_sheet(const char *fname)
181 {
182         int i, j, x, y, num_xtiles, num_ytiles, xsz, ysz, tidx;
183         struct image img;
184
185         if(load_image(&img, fname) == -1) {
186                 fprintf(stderr, "failed to load image: %s\n", fname);
187                 return -1;
188         }
189         if(conv565) {
190                 struct image tmp;
191                 if(conv_image_rgb565(&tmp, &img) == -1) {
192                         fprintf(stderr, "failed to convert image to 16bpp 565: %s\n", fname);
193                         free(img.pixels);
194                         return -1;
195                 }
196                 free(img.pixels);
197                 img = tmp;
198         }
199
200         if(!fbpitch) fbpitch = fbwidth * img.bpp / 8;
201
202         if(rect.w <= 0) {
203                 rect.w = img.width;
204                 rect.h = img.height;
205         }
206
207         if(tile_xsz <= 0) {
208                 num_xtiles = num_ytiles = 1;
209                 xsz = rect.w - padding;
210                 ysz = rect.h - padding;
211         } else {
212                 if(padding) {
213                         num_xtiles = num_ytiles = 0;
214                         i = 0;
215                         while(i < rect.w) {
216                                 num_xtiles++;
217                                 i += tile_xsz + padding;
218                         }
219                         i = 0;
220                         while(i < rect.h) {
221                                 num_ytiles++;
222                                 i += tile_ysz + padding;
223                         }
224                 } else {
225                         num_xtiles = rect.w / tile_xsz;
226                         num_ytiles = rect.h / tile_ysz;
227                 }
228                 xsz = tile_xsz;
229                 ysz = tile_ysz;
230         }
231
232         printf(prefixfmt[asyntax], name, name, name, name, name, fbpitch, name);
233         for(i=0; i<num_ytiles*num_xtiles; i++) {
234                 if(asyntax == AS_GNU) {
235                         printf("\t.long tile%d\n", i);
236                 } else {
237                         printf("\tdd tile%d\n", i);
238                 }
239         }
240         putchar('\n');
241
242         tidx = 0;
243         y = rect.y;
244         for(i=0; i<num_ytiles; i++) {
245                 x = rect.x;
246                 for(j=0; j<num_xtiles; j++) {
247                         printf("tile%d:\n", tidx++);
248                         csprite(&img, x, y, xsz, ysz);
249                         x += xsz + padding;
250                 }
251                 y += ysz + padding;
252         }
253
254         free(img.pixels);
255         return 0;
256 }
257
258 enum {
259         CSOP_SKIP,
260         CSOP_FILL,
261         CSOP_COPY,
262         CSOP_ENDL
263 };
264
265 struct csop {
266         unsigned char op, val;
267         int len;
268 };
269
270 int csprite(struct image *img, int x, int y, int xsz, int ysz)
271 {
272         int i, j, numops, mode, new_mode, start, skip_acc, lenbytes, pixsz = img->bpp / 8;
273         unsigned char *pptr = img->pixels + y * img->scansz + x * pixsz;
274         struct csop *ops, *optr;
275
276         ops = optr = alloca((xsz + 1) * ysz * sizeof *ops);
277
278         for(i=0; i<ysz; i++) {
279                 mode = -1;
280                 start = -1;
281
282                 if(i > 0) {
283                         optr++->op = CSOP_ENDL;
284                 }
285
286                 for(j=0; j<xsz; j++) {
287                         if(memcmp(pptr, &ckey, pixsz) == 0) {
288                                 new_mode = CSOP_SKIP;
289                         } else {
290                                 new_mode = CSOP_COPY;
291                         }
292
293                         if(new_mode != mode) {
294                                 if(mode != -1) {
295                                         assert(start >= 0);
296                                         optr->op = mode;
297                                         optr->len = j - start;
298                                         optr++;
299                                 }
300                                 mode = new_mode;
301                                 start = j;
302                         }
303                         pptr += pixsz;
304                 }
305                 pptr += img->scansz - xsz * pixsz;
306
307                 if(mode != -1) {
308                         assert(start >= 0);
309                         optr->op = mode;
310                         optr->len = xsz - start;
311                         optr++;
312                 }
313         }
314         numops = optr - ops;
315
316         pptr = img->pixels + y * img->scansz + x * img->bpp / 8;
317         optr = ops;
318         skip_acc = 0;
319         /* edx points to dest */
320         for(i=0; i<numops; i++) {
321                 switch(optr->op) {
322                 case CSOP_SKIP:
323                         skip_acc += optr->len;
324                         pptr += optr->len * pixsz;
325                         break;
326
327                 case CSOP_ENDL:
328                         skip_acc += fbwidth - xsz;
329                         pptr += img->scansz - xsz * pixsz;
330                         break;
331
332                 case CSOP_COPY:
333                         if(skip_acc) {
334                                 if(asyntax == AS_GNU) {
335                                         printf("\tadd $%d, %%edx\n", skip_acc * pixsz);
336                                 } else {
337                                         printf("\tadd edx, %d\n", skip_acc * pixsz);
338                                 }
339                                 skip_acc = 0;
340                         }
341
342                         lenbytes = optr->len * pixsz;
343                         for(j=0; j<lenbytes / 4; j++) {
344                                 if(asyntax == AS_GNU) {
345                                         printf("\t%sl $0x%x, %d(%%edx)\n", wrop, *(uint32_t*)pptr, j * 4);
346                                 } else {
347                                         printf("\t%s dword [edx + %d], 0x%x\n", wrop, j * 4, *(uint32_t*)pptr);
348                                 }
349                                 pptr += 4;
350                         }
351                         j *= 4;
352                         switch(lenbytes % 4) {
353                         case 3:
354                                 if(asyntax == AS_GNU) {
355                                         printf("\t%sb $0x%x, %d(%%edx)\n", wrop, (unsigned int)*pptr++, j++);
356                                 } else {
357                                         printf("\t%s byte [edx + %d], 0x%x\n", wrop, j++, (unsigned int)*pptr++);
358                                 }
359                         case 2:
360                                 if(asyntax == AS_GNU) {
361                                         printf("\t%sw $0x%x, %d(%%edx)\n", wrop, (unsigned int)*(uint16_t*)pptr, j);
362                                 } else {
363                                         printf("\t%s word [edx + %d], 0x%x\n", wrop, j, (unsigned int)*(uint16_t*)pptr);
364                                 }
365                                 pptr += 2;
366                                 j += 2;
367                                 break;
368                         case 1:
369                                 if(asyntax == AS_GNU) {
370                                         printf("\t%sb $0x%x, %d(%%edx)\n", wrop, (unsigned int)*pptr++, j++);
371                                 } else {
372                                         printf("\t%s byte [edx + %d], 0x%x\n", wrop, j++, (unsigned int)*pptr++);
373                                 }
374                                 break;
375                         }
376
377                         skip_acc = optr->len;
378                         break;
379
380                 default:
381                         printf("\t%c invalid op: %d\n", asyntax == AS_GNU ? '#' : ';', optr->op);
382                 }
383                 optr++;
384         }
385         printf("\tret\n");
386
387         return 0;
388 }
389
390 void print_usage(const char *argv0)
391 {
392         printf("Usage: %s [options] <spritesheet>\n", argv0);
393         printf("Options:\n");
394         printf(" -s,-size <WxH>: tile size (default: whole image)\n");
395         printf(" -r,-rect <WxH+X+Y>: use rectangle of the input image (default: whole image)\n");
396         printf(" -p,-pad <N>: how many pixels to skip between tiles in source image (default: 0)\n");
397         printf(" -coffset <offs>: colormap offset [0, 255] (default: 0)\n");
398         printf(" -fbpitch <pitch>: target framebuffer pitch (scanline size in bytes)\n");
399         printf(" -k,-key <color>: color-key for transparency (default: 0)\n");
400         printf(" -conv565: convert image to 16bpp 565 before processing\n");
401         printf(" -x,-xor: use XOR for writing pixels instead of MOV\n");
402         printf(" -gas: output GNU assembler code (default)\n");
403         printf(" -nasm: output NASM-compatible code\n");
404         printf(" -h: print usage and exit\n");
405 }