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