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