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