tools: bmp2tiles multiple tiles one palette
[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                                 //printf("%d %d ", ptr[0], ptr[1]);
72                                 if (ptr[0] >= 8 || ptr[1] >= 8) 
73                                         fprintf(stdout, "warning: too many colors detected\n");
74                                 gfx->outpal[ptr[0]] = (gfx->palette[ptr[0]].r >> 4) << 8 |
75                                                       (gfx->palette[ptr[0]].g >> 4) << 4 |
76                                                       gfx->palette[ptr[0]].b >> 4;
77                                 buf[i+j] = ptr[0] << 4;
78                                 buf[i+j] |= ptr[1];
79                                 ptr += 2;
80                         } else { /* TODO: planar format */
81                                 for (int x = 0; x < 8; ++x) {
82                                         if (ptr[x] >= 8) 
83                                                 fprintf(stdout, "warning: too many colors detected\n");
84                                         buf[i + j] = ptr[x] << j; 
85                                 }
86                         }
87                 }
88                 if (!packed)
89                         ptr += 8;
90                 else {
91                         ptr += (8 * (gfx->row_tiles-1));
92                 }
93         }
94 }
95
96 int main(int argc, char *argv[])
97 {
98         SDL_Surface * rawbmp;
99         FILE * gfxstream;
100         FILE * palstream;
101         int opt;
102         char * infile;
103         char outfile[256];
104         bool bout = false;
105
106         while ((opt = getopt(argc, argv, "po:sv:")) != -1) {
107                 switch (opt) {
108                         case 'p':
109                                 printf("include palette");
110                                 break;
111                         case 'o':
112                                 printf("output file %s\n", optarg);
113                                 strcpy(outfile, optarg);
114                                 bout = true;
115                                 break;
116                         case 's':
117                                 printf("generate gfx per 16x16 tile \n");
118                                 break;
119                         case 'v':
120                                 break;
121                         default: /* '?' */
122                                 fprintf(stderr, "Usage: %s [-s] [-o output] file \n", argv[0]);
123                                 exit(EXIT_FAILURE);
124                }
125         }
126
127         if (optind >= argc) {
128                 fprintf(stderr, "Expected infile\n");
129                 fprintf(stderr, "Usage: %s [-s] [-o output] [file]\n", argv[0]);
130                 exit(EXIT_FAILURE);
131         }
132
133         infile = argv[optind];
134
135         rawbmp = SDL_LoadBMP(infile);
136         if (!rawbmp) {
137                 fprintf(stderr, "can not load %s file\n", infile);
138                 exit(EXIT_FAILURE);
139         }
140
141         swan_gfx_t * gfx = malloc(sizeof(swan_gfx_t));
142         int ret = set_info(gfx, rawbmp);
143
144         if (ret != 0)
145                 goto cleanup;
146
147         unsigned char *tile_buf = malloc(sizeof(unsigned char) * TILE_SZ);
148         
149         if (!bout) 
150                 gfxstream = fopen("out.gfx", "w");
151         else { 
152                 printf("opening %s\n", outfile);
153                 gfxstream = fopen(outfile, "w");
154         }
155         
156         if (!gfxstream) {
157                 fprintf(stderr, "can not open file for writing!\n");
158                 goto close;
159         }
160
161         for (int i = 0; i < gfx->row_tiles; ++i) {
162                 for (int j = 0; j < gfx->col_tiles; ++j) {
163                         //printf("row[%d]col[%d]\n",i,j);
164                         generate_4bpp_tile(tile_buf, gfx, true);
165                         int ret = fwrite(tile_buf, sizeof(unsigned char), TILE_SZ, gfxstream);
166                         if (ret != TILE_SZ) {
167                                 fprintf(stderr, "failed to convert, only write %d instead of %d\n", ret, TILE_SZ);
168                                 goto cleanup;
169                         }
170                         gfx->data += 8; /* next column */
171                         //printf("\n");
172                 }
173                 gfx->data += (gfx->col_tiles * 64) - 16;
174         }
175         
176         palstream = fopen("out.pal", "w");
177         if (!palstream) {
178                 fprintf(stderr, "can not open file for writing!\n");
179                 fclose(palstream);
180                 goto close;
181         }
182
183         fwrite(gfx->outpal, sizeof(unsigned short), 8, palstream);
184         const short fill = 0xFFFF;
185         for (int i = 0; i < 8; ++i) 
186                 fwrite(&fill, sizeof(unsigned short), 1, palstream);
187
188         fclose(palstream);
189
190 close:
191         fclose(gfxstream);
192 cleanup:
193         free(gfx);
194         SDL_FreeSurface(rawbmp);
195 }