tools: bmp2tiles clearer error message
[swan-dev] / tools / bmp2tiles / bmp2tiles.c
index 42a8805..b919fe9 100644 (file)
  * File              : bmp2tiles.c
  * Author            : Robin Krens <robin@robinkrens.nl>
  * Date              : 04.06.2022
- * Last Modified Date: 12.06.2022
+ * Last Modified Date: 15.06.2022
  * Last Modified By  : Robin Krens <robin@robinkrens.nl>
  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdint.h>
+#include <stdbool.h>
 #include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
 #include <SDL2/SDL_image.h>
 
 #define TILE_SZ                32
 #define FOURBBP_ROW    8
 #define FOURBPP_COL    8
 
-void bmp_info(SDL_Surface * img)
+typedef struct {
+       unsigned width;
+       unsigned height;
+       unsigned row_tiles;
+       unsigned col_tiles;
+       unsigned bpp;
+       unsigned char * data;
+       SDL_Colour * palette;
+       unsigned short outpal[8];
+} swan_gfx_t;
+
+int set_info(swan_gfx_t * gfx, SDL_Surface * img)
 {
-       SDL_PixelFormat * fmt;
-       fmt = img->format;
-       fprintf(stdout,"WIDTH: %d, HEIGHT: %d, BPP: %d\n", img->w, img->h, fmt->BitsPerPixel);
-}
+       gfx->width = img->w;
+       gfx->height = img->h;
+       gfx->bpp = img->format->BitsPerPixel;
+
+       if (gfx->bpp != 8) {
+               fprintf(stderr, "error: %d bpp format detected\n", img->format->BitsPerPixel);
+               return -1;
+       }
+
+       if (gfx->width % 8 | gfx->height % 8) {
+               fprintf(stderr, "error: image not multiple of 16x16 tiles\n \
+                               width: %d\theight: %d\n", gfx->width, gfx->height);
+               return -1;
+       }
 
+       gfx->row_tiles = gfx->height / 8;
+       gfx->col_tiles = gfx->width / 8;
 
-void generate_4bpp_tile_planar(unsigned char *buf, void * userdata, int sz)
+       /* set userdata to iterate over
+        * pixels */
+       SDL_LockSurface(img);
+       img->userdata = img->pixels;
+       gfx->data = (unsigned char *) img->userdata;
+       gfx->palette = img->format->palette->colors;
+       SDL_UnlockSurface(img);
+       
+       return 0;
+}
+
+void generate_4bpp_tile(unsigned char *buf, swan_gfx_t * gfx,
+               bool packed)
 {
-       unsigned char * ptr = (unsigned char *) userdata;
-       for (int i = 0; i < 32; i+=4) {
-               /* fprintf(stdout, "userdata: pos: %d - %d\n", i, *ptr++); */
+       unsigned char * ptr = gfx->data;
+       for (int i = 0; i < TILE_SZ; i+=4) {
                for (int j = 0; j < 4; ++j) {
-                       for (int x = 0; x < 8; ++x) {
-                               buf[i + j] = ptr[x] << j; 
+                       if (packed) { /* packed format */
+                               //printf("%d %d ", ptr[0], ptr[1]);
+                               if (ptr[0] >= 8 || ptr[1] >= 8) 
+                                       fprintf(stdout, "warning: too many colors detected\n");
+                               gfx->outpal[ptr[0]] = (gfx->palette[ptr[0]].r >> 4) << 8 |
+                                                     (gfx->palette[ptr[0]].g >> 4) << 4 |
+                                                     gfx->palette[ptr[0]].b >> 4;
+                               buf[i+j] = ptr[0] << 4;
+                               buf[i+j] |= ptr[1];
+                               ptr += 2;
+                       } else { /* TODO: planar format */
+                               for (int x = 0; x < 8; ++x) {
+                                       if (ptr[x] >= 8) 
+                                               fprintf(stdout, "warning: too many colors detected\n");
+                                       buf[i + j] = ptr[x] << j; 
+                               }
                        }
-                       /* printf("%d: %x\n", i+j, buf[i+j]); */
                }
-               ptr += 8;
+               if (!packed)
+                       ptr += 8;
+               else {
+                       ptr += (8 * (gfx->col_tiles-1));
+               }
        }
 }
 
-void generate_4bpp_tile_packed(unsigned char *buf, void * userdata, int sz)
+FILE * openstream(const char * name, const char * prefix,
+               unsigned tile_nr, bool append)
 {
-       unsigned char * ptr = (unsigned char *) userdata;
-       for (int i = 0; i < 32; i+=4) {
-               for (int j = 0; j < 4; ++j) {
-                       buf[i+j] = ptr[0] << 4;
-                       buf[i+j] |= ptr[1];
-                       ptr += 2;
-                       printf("%d: %x\n", i+j, buf[i+j]);
-               }
+       assert(strlen(name) < 200);
+       
+       FILE * stream;
+
+       unsigned buf_len = 256;
+       char buf[buf_len];
+       snprintf(buf, buf_len, "%s/%s%d.%s", prefix, name, tile_nr, prefix);
+
+       //printf("%s\n", buf);
+       if (!append) { 
+               stream = fopen(buf, "w");
+       } else {
+               stream = fopen(buf, "a");
        }
+
+       return stream;
 }
 
-int main(void)
+int create_dir(const char *name)
 {
-       SDL_Surface * rawbmp;
-       SDL_PixelFormat * fmt;
-       char filename[] = "test.bmp";
-       rawbmp = SDL_LoadBMP(filename);
+       struct stat statbuf;
+
+       if (stat(name, &statbuf) == -1) 
+               return mkdir(name, 0755);
        
+       if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
+               return -1;
+
+       return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+       SDL_Surface * rawbmp;
+       FILE * gfxstream;
+       FILE * palstream;
+       int opt;
+       char * infile;
+       char outfile[256];
+       bool bout = false;
+       const short fill = 0xFFFF;
+       bool seperate_gfx = false;
+       bool seperate_pal = false;
+
+       while ((opt = getopt(argc, argv, "po:sv")) != -1) {
+               switch (opt) {
+                       case 'p':
+                               seperate_pal = true;
+                               printf("generate individual .pal for each tile");
+                               break;
+                       case 'o':
+                               printf("output file %s\n", optarg);
+                               strcpy(outfile, optarg);
+                               bout = true;
+                               break;
+                       case 's':
+                               seperate_gfx = true;
+                               printf("generate .gfx for each tile\n");
+                               break;
+                       case 'v':
+                               printf("verbose");
+                               break;
+                       default: /* '?' */
+                               fprintf(stderr, "Usage: %s [-s] [-o output] file \n", argv[0]);
+                               exit(EXIT_FAILURE);
+               }
+        }
+
+       if (optind >= argc) {
+               fprintf(stderr, "Expected input .bmp file!\n");
+               fprintf(stderr, "Usage: %s [-s] [-o output] [file]\n", argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       infile = argv[optind];
+
+       rawbmp = SDL_LoadBMP(infile);
        if (!rawbmp) {
-               fprintf(stderr, "can not load .bmp file\n");
+               fprintf(stderr, "can not load %s file\n", infile);
                exit(EXIT_FAILURE);
        }
 
-       bmp_info(rawbmp);
+       swan_gfx_t * gfx = calloc(1, sizeof(swan_gfx_t));
+       int ret = set_info(gfx, rawbmp);
 
-       SDL_LockSurface(rawbmp);
-       rawbmp->userdata = rawbmp->pixels;
-       SDL_UnlockSurface(rawbmp);
+       if (ret != 0)
+               goto cleanup;
 
-       unsigned char *tile_buf = malloc(sizeof(unsigned char) * TILE_SZ);
+       unsigned char *tile_buf =  calloc(TILE_SZ, sizeof(unsigned char));
 
-       generate_4bpp_tile_packed(tile_buf, rawbmp->userdata, 64);
+       if (!bout) {
+               sprintf(outfile, "out");
+       }
        
-       /* if (rawbmp->format->BitsPerPixel != 24) {
-               fprintf(stderr, "format %d not supported\n", rawbmp->format->BitsPerPixel);
-               exit(EXIT_FAILURE);
-       } */
+       /* check for existing gfx/ and pal/ directories */
+       ret = create_dir("gfx/");
+       ret += create_dir("pal/");
+
+       if (ret) {
+               fprintf(stderr, "error: can not create output gfx/ or pal/ location\n");
+               goto cleanup;
+       }
 
+       fprintf(stdout, "generating %d tile(s) \
+                       in %d gfx file(s) \
+                       with %d pal file(s)\n",
+                       gfx->row_tiles * gfx->col_tiles,
+                       seperate_gfx ? gfx->row_tiles * gfx->col_tiles : 1,
+                       seperate_pal ? gfx->row_tiles * gfx->col_tiles : 1);
 
-       SDL_FreeSurface(rawbmp);
+       for (int i = 0, cnt = 0; i < gfx->row_tiles; ++i) {
+               for (int j = 0; j < gfx->col_tiles; ++j, ++cnt) {
+
+                       if (i == 0 && j == 0) {
+                               gfxstream = openstream(outfile, "gfx", 0, false);
+                               palstream = openstream(outfile, "pal", 0, false);
+                       } else {
+                               if (seperate_gfx) {
+                                       gfxstream = openstream(outfile, "gfx", cnt, false);
+                               }
+                               if (seperate_pal) {
+                                       palstream = openstream(outfile, "pal", cnt, false);
+                               }
+                       }
+
+                       generate_4bpp_tile(tile_buf, gfx, true);
+                       int ret = fwrite(tile_buf, sizeof(unsigned char), TILE_SZ, gfxstream);
+                       if (ret != TILE_SZ) {
+                               fprintf(stderr, "failed to convert, only write %d instead of %d\n", ret, TILE_SZ);
+                               fclose(gfxstream);
+                               goto cleanup;
+                       }
 
+                       fwrite(gfx->outpal, sizeof(unsigned short), 8, palstream);
+                       for (int i = 0; i < 8; ++i) 
+                               ret += fwrite(&fill, sizeof(unsigned short), 1, palstream);
+                       
+                       gfx->data += 8; /* next column */
+                       
+                       if (gfx->row_tiles == (i-1) && gfx->col_tiles == (j-1)) {
+                               fclose(gfxstream);
+                               fclose(palstream);
+                       } else {
+                               if (seperate_gfx) 
+                                       fclose(gfxstream);
+                               if (seperate_pal)
+                                       fclose(palstream);
+                       }
+               }
+               gfx->data += (gfx->col_tiles * 64) - (8 * gfx->col_tiles);
+       }
+       
+cleanup:
+       free(gfx);
+       free(tile_buf);
+       SDL_FreeSurface(rawbmp);
 }