SNES first test
[snes_test1] / img2snes / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <errno.h>
6 #include "image.h"
7 #include "dynarr.h"
8
9 void print_usage(const char *argv0);
10 int proc_image(const char *fname);
11 void wrtiles(FILE *fp, const char *name, struct image *timg, int ntiles);
12 void wrtilemap(FILE *fp, const char *name, int *tmap, int xtiles, int ytiles);
13 void wrpalette(FILE *fp, const char *name, struct cmapent *cmap, int ncol);
14
15 int tile_xsz = 8, tile_ysz = 8;
16 const char *output_filename;
17
18 int main(int argc, char **argv)
19 {
20         int i;
21
22         for(i=1; i<argc; i++) {
23                 if(argv[i][0] == '-') {
24                         if(argv[i][2] == 0) {
25                                 switch(argv[i][1]) {
26                                 case 'o':
27                                         output_filename = argv[++i];
28                                         break;
29
30                                 case 't':
31                                         if(sscanf(argv[++i], "%dx%d", &tile_xsz, &tile_ysz) != 2) {
32                                                 fprintf(stderr, "-t must be followed by the tile size (WxH)\n");
33                                                 return 1;
34                                         }
35                                         break;
36
37                                 case 'h':
38                                         print_usage(argv[0]);
39                                         return 0;
40
41                                 default:
42                                         fprintf(stderr, "invalid option: %s\n", argv[i]);
43                                         print_usage(argv[0]);
44                                         return 1;
45                                 }
46                         } else {
47                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
48                                 print_usage(argv[0]);
49                                 return 1;
50                         }
51
52                 } else {
53                         if(proc_image(argv[i]) == -1) {
54                                 return 1;
55                         }
56                 }
57         }
58
59         return 0;
60 }
61
62 void print_usage(const char *argv0)
63 {
64         printf("Usage: %s [options] <img1> [<img2> ... <imgN>]\n", argv0);
65         printf("Options:\n");
66         printf(" -o <file>: output file\n");
67         printf(" -t WxH: tile size (default 8x8)\n");
68         printf(" -h: print usage and exit\n\n");
69 }
70
71 int find_tile(struct image *tile, struct image *tiles)
72 {
73         int i, count;
74         count = dynarr_size(tiles);
75         for(i=0; i<count; i++) {
76                 if(cmp_image(tile, tiles + i) == 0) {
77                         return i;
78                 }
79         }
80         return -1;
81 }
82
83 int proc_image(const char *fname)
84 {
85         int i, j, k, idx, xtiles, ytiles, ntiles, result = -1;
86         FILE *fp;
87         struct image img, tile;
88         struct image *tiles = 0;
89         unsigned char *tiles_pixels, *tptr;
90         unsigned char *sptr;
91         int *tilemap = 0, *mapptr;
92         char *basename, *suffix, *outfile;
93
94         if(load_image(&img, fname) == -1) {
95                 fprintf(stderr, "failed to load image: %s\n", fname);
96                 return -1;
97         }
98         basename = alloca(strlen(fname) + 1);
99         strcpy(basename, fname);
100         if((suffix = strrchr(basename, '/'))) {
101                 basename = suffix + 1;
102         }
103         if((suffix = strchr(basename, '.'))) {
104                 *suffix = 0;
105         }
106
107         xtiles = img.width / tile_xsz;
108         ytiles = img.height / tile_ysz;
109         if(img.width % tile_xsz != 0 || img.height % tile_ysz != 0) {
110                 fprintf(stderr, "image size (%dx%d) not evenly divisible into %dx%d tiles\n",
111                                 img.width, img.height, tile_xsz, tile_ysz);
112                 goto err;
113         }
114
115         if(!(tilemap = malloc(xtiles * ytiles * sizeof *tilemap))) {
116                 fprintf(stderr, "failed to allocate %dx%d tilemap\n", xtiles, ytiles);
117                 goto err;
118         }
119         mapptr = tilemap;
120
121         if(!(tiles = dynarr_alloc(0, sizeof *tiles))) {
122                 fprintf(stderr, "failed to allocate tile array\n");
123                 goto err;
124         }
125         /* alloc a contiguous buffer for the full tileset pixels, to make it easier to write it
126          * out as a single image in the end
127          */
128         if(!(tiles_pixels = dynarr_alloc(0, tile_xsz * tile_ysz * img.bpp / 8))) {
129                 fprintf(stderr, "failed to allocate tile pixel buffer\n");
130                 goto err;
131         }
132
133         tile = img;
134         tile.width = tile_xsz;
135         tile.height = tile_ysz;
136         tile.scansz = tile_xsz * tile.bpp / 8;
137
138         sptr = img.pixels;
139         for(i=0; i<ytiles; i++) {
140                 for(j=0; j<xtiles; j++) {
141                         tile.pixels = sptr;
142
143                         if((idx = find_tile(&tile, tiles)) == -1) {
144                                 /* we don't have a duplicate of this tile */
145                                 idx = dynarr_size(tiles);
146
147                                 if(!(tiles = dynarr_push(tiles, 0))) {
148                                         goto err;
149                                 }
150                                 if(!(tptr = dynarr_push(tiles_pixels, 0))) {
151                                         goto err;
152                                 }
153
154                                 tiles[idx] = tile;
155                                 tiles[idx].scansz = tiles[idx].pitch = tile.scansz;
156
157                                 /* did the array get relocated? */
158                                 if(tptr != tiles_pixels) {
159                                         tiles_pixels = tptr;
160                                         /* make each tile's pixels pointer point to the right place in the large pixelbuffer */
161                                         for(k=0; k<idx+1; k++) {
162                                                 tiles[k].pixels = tptr;
163                                                 tptr += tile_ysz * tiles[idx].pitch;
164                                         }
165                                 } else {
166                                         /* otherwise just set the new one */
167                                         tiles[idx].pixels = tiles_pixels + idx * tile_ysz * tiles[idx].pitch;
168                                 }
169
170                                 blit(&tile, 0, 0, tile_xsz, tile_ysz, tiles + idx, 0, 0);
171                         }
172
173                         *mapptr++ = idx;
174
175                         sptr += tile.scansz;
176                 }
177                 sptr += (tile_ysz - 1) * tile.pitch;
178         }
179
180         ntiles = dynarr_size(tiles);
181         fprintf(stderr, "%s (%dx%d) -> %d tiles, %d unique\n", fname, img.width, img.height,
182                         xtiles * ytiles, ntiles);
183
184         assert(ntiles > 0);
185
186         /* make a big image out of the tiles and write it out */
187         tile = tiles[0];
188         tile.height = ntiles * tile_ysz;
189
190         if(output_filename) {
191                 outfile = (char*)output_filename;
192         } else {
193                 outfile = alloca(strlen(basename) + 5);
194                 sprintf(outfile, "%s.asm", basename);
195         }
196
197         if(!(fp = fopen(outfile, "w"))) {
198                 fprintf(stderr, "failed to open output file: %s: %s\n", outfile, strerror(errno));
199                 goto err;
200         }
201         wrtiles(fp, basename, &tile, ntiles);
202         wrtilemap(fp, basename, tilemap, xtiles, ytiles);
203         wrpalette(fp, basename, tile.cmap, tile.cmap_ncolors);
204         fclose(fp);
205
206         result = 0;
207
208 err:
209         dynarr_free(tiles_pixels);
210         dynarr_free(tiles);
211         free(tilemap);
212         free(img.pixels);
213         return result;
214 }
215
216
217 unsigned int packbitplane4bpp(unsigned char *pix, int bit)
218 {
219         int i;
220         unsigned int val = 0;
221
222         for(i=0; i<4; i++) {
223                 val = (val << 1) | ((*pix >> (bit + 4)) & 1);
224                 val = (val << 1) | ((*pix >> bit) & 1);
225                 pix++;
226         }
227
228         return val;
229 }
230
231 void wrtile4bpp(FILE *fp, unsigned char *tpix, struct image *timg)
232 {
233         int i, x, y, curx = 0;
234         unsigned char *ptr;
235         unsigned int val;
236
237         /* for each pair of bitplanes */
238         for(i=0; i<timg->bpp/2; i++) {
239                 ptr = tpix;
240                 for(y=0; y<tile_ysz; y++) {
241                         for(x=0; x<tile_xsz/8; x++) {
242                                 val = packbitplane4bpp(ptr, i * 2);
243                                 val |= packbitplane4bpp(ptr, i * 2 + 1) << 8;
244
245                                 if(curx == 0) {
246                                         curx = 7 + fprintf(fp, "\t.word $%x", (unsigned int)val);
247                                 } else {
248                                         curx += fprintf(fp, ", $%x", (unsigned int)val);
249                                 }
250                                 if(curx >= 75) {
251                                         fputc('\n', fp);
252                                         curx = 0;
253                                 }
254                         }
255                         ptr += tile_xsz * timg->bpp / 8;
256                 }
257         }
258
259         if(curx != 0) {
260                 fputc('\n', fp);
261         }
262 }
263
264 void wrtiles(FILE *fp, const char *name, struct image *timg, int ntiles)
265 {
266         int i, j;
267         unsigned char *ptr = timg->pixels;
268
269         fprintf(fp, "\n\t%s_num_tiles = %d\n", name, ntiles);
270         fprintf(fp, "\t%s_tiles_width = %d\n", name, timg->width);
271         fprintf(fp, "\t%s_tiles_height = %d\n", name, timg->height);
272         fprintf(fp, "\t%s_tiles_bpp = %d\n", name, timg->bpp);
273         fprintf(fp, "%s_tiles:\n", name);
274
275         for(i=0; i<ntiles; i++) {
276                 fprintf(fp, "\t; tile %d:", i);
277                 for(j=0; j<timg->scansz * tile_ysz; j++) {
278                         fprintf(fp, " %x", ptr[j]);
279                 }
280                 fputc('\n', fp);
281
282                 switch(timg->bpp) {
283                 case 4:
284                         wrtile4bpp(fp, ptr, timg);
285                         break;
286
287                 default:
288                         fprintf(stderr, "unsupported bpp: %d\n", timg->bpp);
289                         return;
290                 }
291                 ptr += timg->scansz * tile_ysz;
292                 fputc('\n', fp);
293         }
294
295         fputc('\n', fp);
296 }
297
298 void wrtilemap(FILE *fp, const char *name, int *tmap, int xtiles, int ytiles)
299 {
300         int i, sz, curx = 0;
301
302         fprintf(fp, "\n\t%s_tilemap_cols = %d\n", name, xtiles);
303         fprintf(fp, "\t%s_tilemap_rows = %d\n", name, ytiles);
304         fprintf(fp, "%s_tilemap:\n", name);
305
306         sz = xtiles * ytiles;
307         for(i=0; i<sz; i++) {
308                 if(curx == 0) {
309                         curx = 7 + fprintf(fp, "\t.word %u", (unsigned int)tmap[i]);
310                 } else {
311                         curx += fprintf(fp, ", %u", (unsigned int)tmap[i]);
312                 }
313                 if(curx >= 75) {
314                         fprintf(fp, "\n");
315                         curx = 0;
316                 }
317         }
318
319         fprintf(fp, "\n\n");
320 }
321
322 void wrpalette(FILE *fp, const char *name, struct cmapent *cmap, int ncol)
323 {
324         int i;
325
326         fprintf(fp, "\n\t%s_cmap_colors = %d\n", name, ncol);
327         fprintf(fp, "%s_cmap:\n", name);
328         for(i=0; i<ncol; i++) {
329                 unsigned int col = (cmap[i].r * 31 / 255) | ((cmap[i].g * 31 / 255) << 5) |
330                         ((cmap[i].b * 31 / 255) << 10);
331                 fprintf(fp, "\t.word $%04x\n", col);
332         }
333         fprintf(fp, "\n\n");
334 }