abc8576f34ccf5377b09f2fdacc20b720de4c619
[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: 12.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 <getopt.h>
16 #include <SDL2/SDL_image.h>
17
18 #define TILE_SZ         32
19 #define FOURBBP_ROW     8
20 #define FOURBPP_COL     8
21
22 typedef struct {
23         unsigned width;
24         unsigned height;
25         unsigned row_tiles;
26         unsigned col_tiles;
27         unsigned bpp;
28         unsigned char * data;
29         SDL_Colour * palette;
30         unsigned short outpal[8];
31 } swan_gfx_t;
32
33 int set_info(swan_gfx_t * gfx, SDL_Surface * img)
34 {
35         gfx->width = img->w;
36         gfx->height = img->h;
37         gfx->bpp = img->format->BitsPerPixel;
38
39         if (gfx->bpp != 8) {
40                 fprintf(stderr, "error: %d bpp format detected\n", img->format->BitsPerPixel);
41                 return -1;
42         }
43
44         if (gfx->width % 8 | gfx->height % 8) {
45                 fprintf(stderr, "error: image not multiple of 16x16 tiles\n \
46                                 width: %d\theight: %d\n", gfx->width, gfx->height);
47                 return -1;
48         }
49
50         gfx->row_tiles = gfx->height / 8;
51         gfx->col_tiles = gfx->width / 8;
52
53         /* set userdata to iterate over
54          * pixels */
55         SDL_LockSurface(img);
56         img->userdata = img->pixels;
57         gfx->data = (unsigned char *) img->userdata;
58         gfx->palette = img->format->palette->colors;
59         SDL_UnlockSurface(img);
60         
61         return 0;
62 }
63
64 void generate_4bpp_tile(unsigned char *buf, swan_gfx_t * gfx,
65                 bool packed)
66 {
67         unsigned char * ptr = gfx->data;
68         for (int i = 0; i < TILE_SZ; i+=4) {
69                 for (int j = 0; j < 4; ++j) {
70                         if (packed) { /* packed format */
71                                 if (ptr[0] >= 8 || ptr[1] >= 8) 
72                                         fprintf(stdout, "warning: too many colors detected\n");
73                                 gfx->outpal[ptr[0]] = (gfx->palette[ptr[0]].r >> 4) << 8 |
74                                                       (gfx->palette[ptr[0]].g >> 4) << 4 |
75                                                       gfx->palette[ptr[0]].b >> 4;
76                                 buf[i+j] = ptr[0] << 4;
77                                 buf[i+j] |= ptr[1];
78                                 ptr += 2;
79                         } else { /* planar format */
80                                 for (int x = 0; x < 8; ++x) {
81                                         if (ptr[x] >= 8) 
82                                                 fprintf(stdout, "warning: too many colors detected\n");
83                                         buf[i + j] = ptr[x] << j; 
84                                 }
85                         }
86                 }
87                 if (!packed)
88                         ptr += 8;
89         }
90 }
91
92 int main(int argc, char *argv[])
93 {
94         SDL_Surface * rawbmp;
95         FILE * gfxstream;
96         FILE * palstream;
97         int opt;
98         char * infile;
99         char outfile[256];
100         bool bout = false;
101
102         while ((opt = getopt(argc, argv, "po:sv:")) != -1) {
103                 switch (opt) {
104                         case 'p':
105                                 printf("include palette");
106                                 break;
107                         case 'o':
108                                 printf("output file %s\n", optarg);
109                                 strcpy(outfile, optarg);
110                                 bout = true;
111                                 break;
112                         case 's':
113                                 printf("generate gfx per 16x16 tile \n");
114                                 break;
115                         case 'v':
116                                 break;
117                         default: /* '?' */
118                                 fprintf(stderr, "Usage: %s [-s] [-o output] file \n", argv[0]);
119                                 exit(EXIT_FAILURE);
120                }
121         }
122
123         if (optind >= argc) {
124                 fprintf(stderr, "Expected infile\n");
125                 fprintf(stderr, "Usage: %s [-s] [-o output] [file]\n", argv[0]);
126                 exit(EXIT_FAILURE);
127         }
128
129         infile = argv[optind];
130
131         rawbmp = SDL_LoadBMP(infile);
132         if (!rawbmp) {
133                 fprintf(stderr, "can not load %s file\n", infile);
134                 exit(EXIT_FAILURE);
135         }
136
137         swan_gfx_t * gfx = malloc(sizeof(swan_gfx_t));
138         int ret = set_info(gfx, rawbmp);
139
140         if (ret != 0)
141                 goto cleanup;
142
143         unsigned char *tile_buf = malloc(sizeof(unsigned char) * TILE_SZ);
144         
145         if (!bout) 
146                 gfxstream = fopen("out.gfx", "w");
147         else { 
148                 printf("opening %s\n", outfile);
149                 gfxstream = fopen(outfile, "w");
150         }
151         
152         if (!gfxstream) {
153                 fprintf(stderr, "can not open file for writing!\n");
154                 goto close;
155         }
156
157         unsigned nr_tiles = 1;
158
159         for (int i = 0; i < nr_tiles; ++i) {
160                 generate_4bpp_tile(tile_buf, gfx, true);
161                 int ret = fwrite(tile_buf, sizeof(unsigned char), TILE_SZ, gfxstream);
162                 if (ret != TILE_SZ) {
163                         fprintf(stderr, "failed to convert, only write %d instead of %d\n", ret, TILE_SZ);
164                         goto cleanup;
165                 }
166         }
167         
168         palstream = fopen("out.pal", "w");
169         if (!palstream) {
170                 fprintf(stderr, "can not open file for writing!\n");
171                 fclose(palstream);
172                 goto close;
173         }
174
175         fwrite(gfx->outpal, sizeof(unsigned short), 8, palstream);
176         const short fill = 0xFFFF;
177         for (int i = 0; i < 8; ++i) 
178                 fwrite(&fill, sizeof(unsigned short), 1, palstream);
179
180         fclose(palstream);
181
182 close:
183         fclose(gfxstream);
184 cleanup:
185         free(gfx);
186         SDL_FreeSurface(rawbmp);
187 }