tools: bmp2tiles write seperate .gfx and .pal files
[swan-dev] / tools / bmp2tiles / bmp2tiles.c
1 /**
2  * File              : bmp2tiles.c
3  * Author            : Robin Krens <robin@robinkrens.nl>
4  * Date              : 04.06.2022
5  * Last Modified Date: 15.06.2022
6  * Last Modified By  : Robin Krens <robin@robinkrens.nl>
7  */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <stdbool.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <assert.h>
16 #include <getopt.h>
17 #include <SDL2/SDL_image.h>
18
19 #define TILE_SZ         32
20 #define FOURBBP_ROW     8
21 #define FOURBPP_COL     8
22
23 typedef struct {
24         unsigned width;
25         unsigned height;
26         unsigned row_tiles;
27         unsigned col_tiles;
28         unsigned bpp;
29         unsigned char * data;
30         SDL_Colour * palette;
31         unsigned short outpal[8];
32 } swan_gfx_t;
33
34 int set_info(swan_gfx_t * gfx, SDL_Surface * img)
35 {
36         gfx->width = img->w;
37         gfx->height = img->h;
38         gfx->bpp = img->format->BitsPerPixel;
39
40         if (gfx->bpp != 8) {
41                 fprintf(stderr, "error: %d bpp format detected\n", img->format->BitsPerPixel);
42                 return -1;
43         }
44
45         if (gfx->width % 8 | gfx->height % 8) {
46                 fprintf(stderr, "error: image not multiple of 16x16 tiles\n \
47                                 width: %d\theight: %d\n", gfx->width, gfx->height);
48                 return -1;
49         }
50
51         gfx->row_tiles = gfx->height / 8;
52         gfx->col_tiles = gfx->width / 8;
53
54         /* set userdata to iterate over
55          * pixels */
56         SDL_LockSurface(img);
57         img->userdata = img->pixels;
58         gfx->data = (unsigned char *) img->userdata;
59         gfx->palette = img->format->palette->colors;
60         SDL_UnlockSurface(img);
61         
62         return 0;
63 }
64
65 void generate_4bpp_tile(unsigned char *buf, swan_gfx_t * gfx,
66                 bool packed)
67 {
68         unsigned char * ptr = gfx->data;
69         for (int i = 0; i < TILE_SZ; i+=4) {
70                 for (int j = 0; j < 4; ++j) {
71                         if (packed) { /* packed format */
72                                 //printf("%d %d ", ptr[0], ptr[1]);
73                                 if (ptr[0] >= 8 || ptr[1] >= 8) 
74                                         fprintf(stdout, "warning: too many colors detected\n");
75                                 gfx->outpal[ptr[0]] = (gfx->palette[ptr[0]].r >> 4) << 8 |
76                                                       (gfx->palette[ptr[0]].g >> 4) << 4 |
77                                                       gfx->palette[ptr[0]].b >> 4;
78                                 buf[i+j] = ptr[0] << 4;
79                                 buf[i+j] |= ptr[1];
80                                 ptr += 2;
81                         } else { /* TODO: planar format */
82                                 for (int x = 0; x < 8; ++x) {
83                                         if (ptr[x] >= 8) 
84                                                 fprintf(stdout, "warning: too many colors detected\n");
85                                         buf[i + j] = ptr[x] << j; 
86                                 }
87                         }
88                 }
89                 if (!packed)
90                         ptr += 8;
91                 else {
92                         ptr += (8 * (gfx->col_tiles-1));
93                 }
94         }
95 }
96
97 FILE * openstream(const char * name, const char * prefix,
98                 unsigned tile_nr, bool append)
99 {
100         assert(strlen(name) < 200);
101         
102         FILE * stream;
103
104         unsigned buf_len = 256;
105         char buf[buf_len];
106         snprintf(buf, buf_len, "%s%d.%s", name, tile_nr, prefix);
107
108         //printf("%s\n", buf);
109         if (!append) { 
110                 stream = fopen(buf, "w");
111         } else {
112                 stream = fopen(buf, "a");
113         }
114
115         return stream;
116 }
117
118 int main(int argc, char *argv[])
119 {
120         SDL_Surface * rawbmp;
121         FILE * gfxstream;
122         FILE * palstream;
123         int opt;
124         char * infile;
125         char outfile[256];
126         bool bout = false;
127         const short fill = 0xFFFF;
128         bool seperate_gfx = false;
129         bool seperate_pal = false;
130
131         while ((opt = getopt(argc, argv, "po:sv")) != -1) {
132                 switch (opt) {
133                         case 'p':
134                                 seperate_pal = true;
135                                 printf("generate individual .pal for each tile");
136                                 break;
137                         case 'o':
138                                 printf("output file %s\n", optarg);
139                                 strcpy(outfile, optarg);
140                                 bout = true;
141                                 break;
142                         case 's':
143                                 seperate_gfx = true;
144                                 printf("generate .gfx for each tile\n");
145                                 break;
146                         case 'v':
147                                 printf("verbose");
148                                 break;
149                         default: /* '?' */
150                                 fprintf(stderr, "Usage: %s [-s] [-o output] file \n", argv[0]);
151                                 exit(EXIT_FAILURE);
152                }
153         }
154
155         if (optind >= argc) {
156                 fprintf(stderr, "Expected input .bmp file!\n");
157                 fprintf(stderr, "Usage: %s [-s] [-o output] [file]\n", argv[0]);
158                 exit(EXIT_FAILURE);
159         }
160
161         infile = argv[optind];
162
163         rawbmp = SDL_LoadBMP(infile);
164         if (!rawbmp) {
165                 fprintf(stderr, "can not load %s file\n", infile);
166                 exit(EXIT_FAILURE);
167         }
168
169         swan_gfx_t * gfx = calloc(1, sizeof(swan_gfx_t));
170         int ret = set_info(gfx, rawbmp);
171
172         if (ret != 0)
173                 goto cleanup;
174
175         unsigned char *tile_buf =  calloc(TILE_SZ, sizeof(unsigned char));
176
177         if (!bout) {
178                 sprintf(outfile, "out");
179         }
180         
181         fprintf(stdout, "generating %d tile(s) \
182                         in %d gfx file(s) \
183                         with %d pal file(s)\n",
184                         gfx->row_tiles * gfx->col_tiles,
185                         seperate_gfx ? gfx->row_tiles * gfx->col_tiles : 1,
186                         seperate_pal ? gfx->row_tiles * gfx->col_tiles : 1);
187
188         
189         for (int i = 0, cnt = 0; i < gfx->row_tiles; ++i) {
190                 for (int j = 0; j < gfx->col_tiles; ++j, ++cnt) {
191
192                         if (i == 0 && j == 0) {
193                                 gfxstream = openstream(outfile, "gfx", 0, false);
194                                 palstream = openstream(outfile, "pal", 0, false);
195                         } else {
196                                 if (seperate_gfx) {
197                                         gfxstream = openstream(outfile, "gfx", cnt, false);
198                                 }
199                                 if (seperate_pal) {
200                                         palstream = openstream(outfile, "pal", cnt, false);
201                                 }
202                         }
203
204                         generate_4bpp_tile(tile_buf, gfx, true);
205                         int ret = fwrite(tile_buf, sizeof(unsigned char), TILE_SZ, gfxstream);
206                         if (ret != TILE_SZ) {
207                                 fprintf(stderr, "failed to convert, only write %d instead of %d\n", ret, TILE_SZ);
208                                 fclose(gfxstream);
209                                 goto cleanup;
210                         }
211
212                         fwrite(gfx->outpal, sizeof(unsigned short), 8, palstream);
213                         for (int i = 0; i < 8; ++i) 
214                                 ret += fwrite(&fill, sizeof(unsigned short), 1, palstream);
215                         
216                         gfx->data += 8; /* next column */
217                         
218                         if (gfx->row_tiles == (i-1) && gfx->col_tiles == (j-1)) {
219                                 fclose(gfxstream);
220                                 fclose(palstream);
221                         } else {
222                                 if (seperate_gfx) 
223                                         fclose(gfxstream);
224                                 if (seperate_pal)
225                                         fclose(palstream);
226                         }
227                 }
228                 gfx->data += (gfx->col_tiles * 64) - (8 * gfx->col_tiles);
229         }
230         
231 cleanup:
232         free(gfx);
233         free(tile_buf);
234         SDL_FreeSurface(rawbmp);
235 }