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