diff --git a/bin/main.c b/bin/main.c index fcae7c9..3f51a20 100644 --- a/bin/main.c +++ b/bin/main.c @@ -40,7 +40,7 @@ static void usage(int argc, char **argv, const char *message, ...) { " %1$s split file.sav rn gn bn input.png\n" " %1$s merge file.sav rn gn bn output.png\n" " %1$s import-tile file.sav 1..30 printer.tile\n" - " %1$s export-tile file.sav 1..30 output.png\n" + " %1$s export-tile file.sav 1..30 output.tile\n" " %1$s convert-tile printer.tile output.png\n", argv[0]); @@ -587,6 +587,87 @@ error_sram_open: return 1; } +static int export_tile(int argc, char **argv) { + cammy_sram *sram; + cammy_image *dest, *src; + int photo; + + if (argc < 2) { + usage(argc, argv, "No camera SRAM file provided"); + } else if (argc < 3) { + usage(argc, argv, "No picture number provided"); + } else if (argc < 4) { + usage(argc, argv, "No Game Boy screen tile data file provided"); + } + + photo = atoi(argv[3]); + + if (photo < 1 || photo > CAMMY_SRAM_PHOTO_COUNT) { + usage(argc, argv, "Invalid photo number"); + } + + if ((sram = cammy_sram_open(argv[2])) == NULL) { + fprintf(stderr, "%s: %s: %s: %s\n", + argv[0], "cammy_sram_open()", argv[2], strerror(errno)); + + goto error_sram_open; + } + + if ((src = malloc(sizeof(*src))) == NULL) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], "malloc()", strerror(errno)); + + goto error_malloc_src; + } + + src->format = CAMMY_IMAGE_TILE; + src->size = CAMMY_PHOTO_SIZE; + src->width = CAMMY_PHOTO_WIDTH; + src->height = CAMMY_PHOTO_HEIGHT; + src->tiles = (cammy_tile *)&sram->data->photos[photo-1].tiles; + + if ((dest = cammy_image_new(CAMMY_IMAGE_TILE, + CAMMY_SCREEN_WIDTH, + CAMMY_SCREEN_HEIGHT)) == NULL) { + fprintf(stderr, "%s: %s: %s\n", + argv[0], "cammy_image_new()", strerror(errno)); + + goto error_image_new_dest; + } + + memset(dest->buf, '\0', dest->size); + + cammy_image_copy(dest, src, + 16, 16, 0, 0, CAMMY_PHOTO_WIDTH, CAMMY_PHOTO_HEIGHT); + + if (cammy_image_save_tile(dest, argv[4]) < 0) { + fprintf(stderr, "%s: %s: %s: %s\n", + argv[0], "cammy_image_save_tile()", argv[4], strerror(errno)); + + goto error_image_save_tile_dest; + } + + free(dest); + + cammy_image_close(src); + + cammy_sram_close(sram); + + return 0; + +error_image_save_tile_dest: + cammy_image_destroy(dest); + +error_image_new_dest: + free(src); + +error_malloc_src: + cammy_sram_close(sram); + +error_sram_open: + return errno || 1; +} + static int convert_tile(int argc, char **argv) { int fd; void *in, *out; @@ -664,6 +745,7 @@ static struct { { "split", split }, { "merge", merge }, { "import-tile", import_tile }, + { "export-tile", export_tile }, { "convert-tile", convert_tile }, { NULL, NULL } }; diff --git a/include/cammy/image.h b/include/cammy/image.h index b5a5ae8..860f04f 100644 --- a/include/cammy/image.h +++ b/include/cammy/image.h @@ -21,7 +21,8 @@ typedef enum { typedef struct _cammy_image { cammy_image_format format; - size_t width, + size_t size, + width, height; union { @@ -30,12 +31,20 @@ typedef struct _cammy_image { }; } cammy_image; +cammy_image *cammy_image_new(cammy_image_format format, + size_t width, + size_t height); + cammy_image *cammy_image_open_tile(const char *filename, size_t width, size_t height); +int cammy_image_save_tile(cammy_image *image, const char *filename); + void cammy_image_close(cammy_image *image); +void cammy_image_destroy(cammy_image *image); + int cammy_image_save(cammy_image *image, const char *filename); void cammy_image_dither(uint8_t *dest, diff --git a/include/cammy/photo.h b/include/cammy/photo.h index d46cb33..f08b581 100644 --- a/include/cammy/photo.h +++ b/include/cammy/photo.h @@ -8,6 +8,9 @@ #define CAMMY_PHOTO_TILES_WIDTH 16 #define CAMMY_PHOTO_TILES_HEIGHT 14 +#define CAMMY_PHOTO_SIZE \ + (CAMMY_TILE_SIZE * CAMMY_PHOTO_TILES_WIDTH * CAMMY_PHOTO_TILES_HEIGHT) + #define CAMMY_PHOTO_WIDTH \ (CAMMY_TILE_WIDTH * CAMMY_PHOTO_TILES_WIDTH) diff --git a/src/image.c b/src/image.c index c30f114..16f625b 100644 --- a/src/image.c +++ b/src/image.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -140,28 +141,81 @@ static inline void buf_write(uint8_t *buf, buf[depth*stride*y + depth*x + 2] = b; } -cammy_image *cammy_image_open_tile(const char *filename, - size_t width, - size_t height) { +cammy_image *cammy_image_new(cammy_image_format format, + size_t width, + size_t height) { cammy_image *image; - int fd; + size_t size; - size_t size = (width * height) >> 2, - off; + switch (format) { + case CAMMY_IMAGE_TILE: { + size_t tiles_width = width >> 3, + tiles_height = height >> 3; - if ((fd = open(filename, O_RDONLY)) < 0) { - goto error_open; + if (width & 7) tiles_width++; + if (height & 7) tiles_height++; + + size = CAMMY_TILE_SIZE * tiles_width * tiles_height; + + break; + } + + case CAMMY_IMAGE_RGB: { + size = 3 * width * height; + break; + } + + case CAMMY_IMAGE_RGBA: { + size = 4 * width * height; + break; + } + + default: { + errno = EINVAL; + goto error_invalid_format; + } } if ((image = malloc(sizeof(*image))) == NULL) { goto error_malloc_image; } - if ((image->tiles = malloc(size)) == NULL) { - goto error_malloc_tiles; + if ((image->buf = malloc(size)) == NULL) { + goto error_malloc_buf; } - for (off=0; offformat = format; + image->size = size; + image->width = width; + image->height = height; + + return image; + +error_malloc_buf: + free(image); + +error_malloc_image: +error_invalid_format: + return NULL; +} + +cammy_image *cammy_image_open_tile(const char *filename, + size_t width, + size_t height) { + cammy_image *image; + int fd; + + size_t off; + + if ((fd = open(filename, O_RDONLY)) < 0) { + goto error_open; + } + + if ((image = cammy_image_new(CAMMY_IMAGE_TILE, width, height)) == NULL) { + goto error_image_new; + } + + for (off=0; offsize;) { ssize_t len; if ((len = read(fd, &image->buf[off], CAMMY_IMAGE_CHUNK_SIZE)) < 0) { @@ -173,19 +227,12 @@ cammy_image *cammy_image_open_tile(const char *filename, close(fd); - image->format = CAMMY_IMAGE_TILE; - image->width = width; - image->height = height; - return image; error_read: - free(image->tiles); + cammy_image_destroy(image); -error_malloc_tiles: - free(image); - -error_malloc_image: +error_image_new: close(fd); error_open: @@ -193,10 +240,48 @@ error_open: } void cammy_image_close(cammy_image *image) { + free(image); +} + +void cammy_image_destroy(cammy_image *image) { free(image->buf); free(image); } +int cammy_image_save_tile(cammy_image *image, const char *filename) { + size_t off, + rest = image->size; + + int fd; + + if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0) { + goto error_open; + } + + for (off=0; offsize; off+=CAMMY_IMAGE_CHUNK_SIZE) { + size_t chunk = rest > CAMMY_IMAGE_CHUNK_SIZE? + CAMMY_IMAGE_CHUNK_SIZE: rest; + + ssize_t len; + + if ((len = write(fd, image->buf + off, chunk)) < 0) { + goto error_write; + } + + rest -= len; + } + + close(fd); + + return 0; + +error_write: + close(fd); + +error_open: + return -1; +} + void cammy_image_dither(uint8_t *dest, uint8_t *src, size_t width,