- fixed typos in csprite's code generator for nasm
[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.global %s\n"
117         "%s:\n"
118         "\tmov 12(%%esp), %%eax\n"
119         "\tmov $%d, %%ecx\n"
120         "\tmul %%ecx\n"
121         "\tadd 8(%%esp), %%eax\n"
122         "\tadd 4(%%esp), %%eax\n"
123         "\tmov %%eax, %%edx\n"
124         "\tmov 16(%%esp), %%eax\n"
125         "\tjmp *tiletab(,%%eax,4)\n\n"
126         "tiletab:\n",
127
128         /* NASM template */
129         "\tglobal %s\n"
130         "%s:\n"
131         "\tmov eax, [esp + 12]\n"
132         "\tmov ecx, %d\n"
133         "\tmul ecx\n"
134         "\tadd eax, [esp + 8]\n"
135         "\tadd eax, [esp + 4]\n"
136         "\tmov edx, eax\n"
137         "\tmov eax, [esp + 16]\n"
138         "\tjmp [titletab + eax * 4]\n\n"
139         "titletab:\n"
140 };
141
142 int proc_sheet(const char *fname)
143 {
144         int i, j, num_xtiles, num_ytiles, xsz, ysz, tidx;
145         struct image img;
146
147         if(load_image(&img, fname) == -1) {
148                 fprintf(stderr, "failed to load image: %s\n", fname);
149                 return -1;
150         }
151         if(conv565) {
152                 struct image tmp;
153                 if(conv_image_rgb565(&tmp, &img) == -1) {
154                         fprintf(stderr, "failed to convert image to 16bpp 565: %s\n", fname);
155                         free(img.pixels);
156                         return -1;
157                 }
158                 free(img.pixels);
159                 img = tmp;
160         }
161
162         if(!fbpitch) fbpitch = fbwidth * img.bpp / 8;
163
164         if(rect.w <= 0) {
165                 rect.w = img.width;
166                 rect.h = img.height;
167         }
168
169         if(tile_xsz <= 0) {
170                 num_xtiles = num_ytiles = 1;
171                 xsz = rect.w;
172                 ysz = rect.h;
173         } else {
174                 num_xtiles = rect.w / tile_xsz;
175                 num_ytiles = rect.h / tile_ysz;
176                 xsz = tile_xsz;
177                 ysz = tile_ysz;
178         }
179
180         printf(prefixfmt[asyntax], name, name, fbpitch);
181         for(i=0; i<num_ytiles*num_xtiles; i++) {
182                 if(asyntax == AS_GNU) {
183                         printf("\t.long tile%d\n", i);
184                 } else {
185                         printf("\tdd tile%d\n", i);
186                 }
187         }
188         putchar('\n');
189
190         tidx = 0;
191         for(i=0; i<num_ytiles; i++) {
192                 for(j=0; j<num_xtiles; j++) {
193                         printf("tile%d:\n", tidx++);
194                         csprite(&img, rect.x + j * xsz, rect.y + i * ysz, xsz, ysz);
195                 }
196         }
197
198         free(img.pixels);
199         return 0;
200 }
201
202 enum {
203         CSOP_SKIP,
204         CSOP_FILL,
205         CSOP_COPY,
206         CSOP_ENDL
207 };
208
209 struct csop {
210         unsigned char op, val;
211         int len;
212 };
213
214 int csprite(struct image *img, int x, int y, int xsz, int ysz)
215 {
216         int i, j, numops, mode, new_mode, start, skip_acc, lenbytes, pixsz = img->bpp / 8;
217         unsigned char *pptr = img->pixels + y * img->scansz + x * pixsz;
218         struct csop *ops, *optr;
219
220         ops = optr = alloca((xsz + 1) * ysz * sizeof *ops);
221
222         for(i=0; i<ysz; i++) {
223                 mode = -1;
224                 start = -1;
225
226                 if(i > 0) {
227                         optr++->op = CSOP_ENDL;
228                 }
229
230                 for(j=0; j<xsz; j++) {
231                         if(memcmp(pptr, &ckey, pixsz) == 0) {
232                                 new_mode = CSOP_SKIP;
233                         } else {
234                                 new_mode = CSOP_COPY;
235                         }
236
237                         if(new_mode != mode) {
238                                 if(mode != -1) {
239                                         assert(start >= 0);
240                                         optr->op = mode;
241                                         optr->len = j - start;
242                                         optr++;
243                                 }
244                                 mode = new_mode;
245                                 start = j;
246                         }
247                         pptr += pixsz;
248                 }
249                 pptr += img->scansz - xsz * pixsz;
250
251                 if(mode != -1) {
252                         assert(start >= 0);
253                         optr->op = mode;
254                         optr->len = xsz - start;
255                         optr++;
256                 }
257         }
258         numops = optr - ops;
259
260         pptr = img->pixels + y * img->scansz + x * img->bpp / 8;
261         optr = ops;
262         skip_acc = 0;
263         /* edx points to dest */
264         for(i=0; i<numops; i++) {
265                 switch(optr->op) {
266                 case CSOP_SKIP:
267                         skip_acc += optr->len;
268                         pptr += optr->len * pixsz;
269                         break;
270
271                 case CSOP_ENDL:
272                         skip_acc += fbwidth - xsz;
273                         pptr += img->scansz - xsz * pixsz;
274                         break;
275
276                 case CSOP_COPY:
277                         if(skip_acc) {
278                                 if(asyntax == AS_GNU) {
279                                         printf("\tadd $%d, %%edx\n", skip_acc * pixsz);
280                                 } else {
281                                         printf("\tadd edx, %d\n", skip_acc * pixsz);
282                                 }
283                                 skip_acc = 0;
284                         }
285
286                         lenbytes = optr->len * pixsz;
287                         for(j=0; j<lenbytes / 4; j++) {
288                                 if(asyntax == AS_GNU) {
289                                         printf("\tmovl $0x%x, %d(%%edx)\n", *(uint32_t*)pptr, j * 4);
290                                 } else {
291                                         printf("\tmov dword [edx + %d], 0x%x\n", j * 4, *(uint32_t*)pptr);
292                                 }
293                                 pptr += 4;
294                         }
295                         j *= 4;
296                         switch(lenbytes % 4) {
297                         case 3:
298                                 if(asyntax == AS_GNU) {
299                                         printf("\tmovb $0x%x, %d(%%edx)\n", (unsigned int)*pptr++, j++);
300                                 } else {
301                                         printf("\tmov byte [edx + %d], 0x%x\n", j++, (unsigned int)*pptr++);
302                                 }
303                         case 2:
304                                 if(asyntax == AS_GNU) {
305                                         printf("\tmovw $0x%x, %d(%%edx)\n", (unsigned int)*(uint16_t*)pptr, j);
306                                 } else {
307                                         printf("\tmov word [edx + %d], 0x%x\n", j, (unsigned int)*(uint16_t*)pptr);
308                                 }
309                                 pptr += 2;
310                                 j += 2;
311                                 break;
312                         case 1:
313                                 if(asyntax == AS_GNU) {
314                                         printf("\tmovb $0x%x, %d(%%edx)\n", (unsigned int)*pptr++, j++);
315                                 } else {
316                                         printf("\tmov byte [edx + %d], 0x%x\n", j++, (unsigned int)*pptr++);
317                                 }
318                                 break;
319                         }
320
321                         skip_acc = optr->len;
322                         break;
323
324                 default:
325                         printf("\t%c invalid op: %d\n", asyntax == AS_GNU ? '#' : ';', optr->op);
326                 }
327                 optr++;
328         }
329         printf("\tret\n");
330
331         return 0;
332 }
333
334 void print_usage(const char *argv0)
335 {
336         printf("Usage: %s [options] <spritesheet>\n", argv0);
337         printf("Options:\n");
338         printf(" -s,-size <WxH>: tile size (default: whole image)\n");
339         printf(" -r,-rect <WxH+X+Y>: use rectangle of the input image (default: whole image)\n");
340         printf(" -coffset <offs>: colormap offset [0, 255] (default: 0)\n");
341         printf(" -fbpitch <pitch>: target framebuffer pitch (scanline size in bytes)\n");
342         printf(" -k,-key <color>: color-key for transparency (default: 0)\n");
343         printf(" -conv565: convert image to 16bpp 565 before processing\n");
344         printf(" -gas: output GNU assembler code (default)\n");
345         printf(" -nasm: output NASM-compatible code\n");
346         printf(" -h: print usage and exit\n");
347 }