#include #include #include #include #include #include #include #include #include #include "pnglite.h" #include #include #include #include #include static cammy_tile_palette palette = { .colors = { { 0, 0, 0 }, { 85, 85, 85 }, { 171, 171, 171 }, { 255, 255, 255 } } }; static void usage(int argc, char **argv, const char *message, ...) { if (message) { va_list args; va_start(args, message); vfprintf(stderr, message, args); va_end(args); fprintf(stderr, "\n"); } fprintf(stderr, "usage: %1$s import file.sav 1..30 input.png\n" " %1$s export file.sav 1..30 output.png\n" " %1$s dither input.png output.png\n" " %1$s split-rgb file.sav rn gn bn input.png\n" " %1$s merge-rgb 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.tile\n" " %1$s convert-tile printer.tile output.png\n" " %1$s slice x-pad y-pad input.png\n", argv[0]); exit(1); } static int save_buf_to_png_file(const char *file, void *buf, size_t width, size_t height, int depth, int format) { png_t *png; png_init(malloc, free); if ((png = malloc(sizeof(*png))) == NULL) { goto error_malloc_png; } if (png_open_file_write(png, file) < 0) { goto error_png_open_file_write; } if (png_set_data(png, width, height, depth, format, buf) < 0) { goto error_png_set_data; } png_close_file(png); free(png); return 0; error_png_set_data: png_close_file(png); error_png_open_file_write: free(png); error_malloc_png: return -1; } static uint8_t *read_png_file(const char *file, size_t *width, size_t *height, int *depth) { png_t *png; uint8_t *buf; png_init(malloc, free); if ((png = malloc(sizeof(*png))) == NULL) { goto error_malloc_png; } if (png_open_file_read(png, file) < 0) { goto error_png_open_file_read; } *width = png->width; *height = png->height; *depth = png->bpp; if ((buf = malloc(*width * *height * *depth)) == NULL) { goto error_malloc_buf; } if (png_get_data(png, buf) < 0) { goto error_png_get_data; } png_close_file(png); free(png); return buf; error_png_get_data: free(buf); error_malloc_buf: png_close_file(png); error_png_open_file_read: free(png); error_malloc_png: return NULL; } static int import(int argc, char **argv) { cammy_sram *sram; png_t *png; int photo = 0; uint8_t *buf; int error; if (argc < 3) { usage(argc, argv, "No save file provided"); } else if (argc < 4) { usage(argc, argv, "No photo number provided"); } else if (argc < 5) { usage(argc, argv, "No photo 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; } png_init(malloc, free); if ((png = malloc(sizeof(*png))) == NULL) { goto error_malloc_png; } if ((error = png_open_file_read(png, argv[4])) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "png_open_file_read()", argv[4], png_error_string(error)); goto error_png_open_file_read; } if (png->width != CAMMY_PHOTO_WIDTH || png->height != CAMMY_PHOTO_HEIGHT) { fprintf(stderr, "%s: %s: %s\n", argv[0], argv[4], "Invalid image dimensions"); goto error_invalid_dimensions; } if ((buf = malloc(png->width * png->height * png->bpp)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "malloc()", strerror(errno)); goto error_malloc_buf; } if ((error = png_get_data(png, buf)) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "png_get_data()", argv[4], png_error_string(error)); goto error_png_get_data; } cammy_photo_import(&sram->data->photos[photo-1], buf, (int)png->bpp); free(buf); png_close_file(png); free(png); cammy_sram_close(sram); return 0; error_png_get_data: free(buf); error_malloc_buf: error_invalid_dimensions: png_close_file(png); error_png_open_file_read: free(png); error_malloc_png: cammy_sram_close(sram); error_sram_open: return 1; } static int export(int argc, char **argv) { cammy_sram *sram; int photo = 0; uint8_t *buf; if (argc < 3) { usage(argc, argv, "No save file provided"); } else if (argc < 4) { usage(argc, argv, "No photo number provided"); } else if (argc < 5) { usage(argc, argv, "No output filename 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 ((buf = malloc(CAMMY_PHOTO_WIDTH * CAMMY_PHOTO_HEIGHT * 3)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "malloc()", strerror(errno)); goto error_malloc_buf; } cammy_photo_export(&sram->data->photos[photo-1], buf, 3, &palette); if (save_buf_to_png_file(argv[4], buf, CAMMY_PHOTO_WIDTH, CAMMY_PHOTO_HEIGHT, 8, PNG_TRUECOLOR) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "save_buf_to_png_file()", argv[4], strerror(errno)); goto error_save_buf_to_png_file; } free(buf); cammy_sram_close(sram); return 0; error_save_buf_to_png_file: free(buf); error_malloc_buf: cammy_sram_close(sram); error_sram_open: return 1; } static int dither(int argc, char **argv) { uint8_t *bufin, *bufout; size_t width, height; int depth; if (argc < 3) { usage(argc, argv, "No PNG input file provided"); } else if (argc < 4) { usage(argc, argv, "No PNG output filename provided"); } if ((bufin = read_png_file(argv[2], &width, &height, &depth)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "read_png_file()", strerror(errno)); goto error_read_png_file; } if ((bufout = malloc(width * height * 3)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "malloc()", strerror(errno)); goto error_malloc_bufout; } cammy_image_dither(bufout, bufin, width, height, depth, &palette); if (save_buf_to_png_file(argv[3], bufout, width, height, 8, PNG_TRUECOLOR) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "save_buf_to_png_file()", argv[3], strerror(errno)); goto error_save_buf_to_png_file; } free(bufout); free(bufin); return 0; error_save_buf_to_png_file: free(bufout); error_malloc_bufout: free(bufin); error_read_png_file: return 1; } static void validate_photo_number(int argc, char **argv, int num) { if (num < 1 || num > CAMMY_SRAM_PHOTO_COUNT) { usage(argc, argv, "Invalid photo number"); } } static int split_rgb(int argc, char **argv) { cammy_sram *sram; png_t *png; int photo_r = 0, photo_g = 0, photo_b = 0; uint8_t *buf; int error; if (argc < 3) { usage(argc, argv, "No save file provided"); } else if (argc < 4) { usage(argc, argv, "No red photo number provided"); } else if (argc < 5) { usage(argc, argv, "No green photo number provided"); } else if (argc < 6) { usage(argc, argv, "No blue photo number provided"); } else if (argc < 7) { usage(argc, argv, "No photo provided"); } validate_photo_number(argc, argv, photo_r = atoi(argv[3])); validate_photo_number(argc, argv, photo_g = atoi(argv[4])); validate_photo_number(argc, argv, photo_b = atoi(argv[5])); 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; } png_init(malloc, free); if ((png = malloc(sizeof(*png))) == NULL) { goto error_malloc_png; } if ((error = png_open_file_read(png, argv[6])) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "png_open_file_read()", argv[6], png_error_string(error)); goto error_png_open_file_read; } if (png->width != CAMMY_PHOTO_WIDTH || png->height != CAMMY_PHOTO_HEIGHT) { fprintf(stderr, "%s: %s: %s\n", argv[0], argv[6], "Invalid image dimensions"); goto error_invalid_dimensions; } if ((buf = malloc(png->width * png->height * png->bpp)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "malloc()", strerror(errno)); goto error_malloc; } if ((error = png_get_data(png, buf)) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "png_get_data()", argv[4], png_error_string(error)); goto error_png_get_data; } cammy_photo_import_rgb(&sram->data->photos[photo_r-1], &sram->data->photos[photo_g-1], &sram->data->photos[photo_b-1], buf, (int)png->bpp); free(buf); png_close_file(png); free(png); cammy_sram_close(sram); return 0; error_png_get_data: free(buf); error_malloc: error_invalid_dimensions: png_close_file(png); error_png_open_file_read: free(png); error_malloc_png: cammy_sram_close(sram); error_sram_open: return 1; } static int merge_rgb(int argc, char **argv) { cammy_sram *sram; png_t *png; int photo_r = 0, photo_g = 0, photo_b = 0; uint8_t *buf; int error; if (argc < 3) { usage(argc, argv, "No save file provided"); } else if (argc < 4) { usage(argc, argv, "No red photo number provided"); } else if (argc < 5) { usage(argc, argv, "No green photo number provided"); } else if (argc < 6) { usage(argc, argv, "No blue photo number provided"); } else if (argc < 7) { usage(argc, argv, "No photo provided"); } validate_photo_number(argc, argv, photo_r = atoi(argv[3])); validate_photo_number(argc, argv, photo_g = atoi(argv[4])); validate_photo_number(argc, argv, photo_b = atoi(argv[5])); 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; } png_init(malloc, free); if ((png = malloc(sizeof(*png))) == NULL) { goto error_malloc_png; } if ((error = png_open_file_write(png, argv[6])) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "png_open_file_write()", argv[6], png_error_string(error)); goto error_png_open_file_write; } if ((buf = malloc(CAMMY_PHOTO_WIDTH * CAMMY_PHOTO_HEIGHT * 3)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "malloc()", strerror(errno)); goto error_malloc_buf; } cammy_photo_merge(&sram->data->photos[photo_r-1], &sram->data->photos[photo_g-1], &sram->data->photos[photo_b-1], buf, 3); if ((error = png_set_data(png, CAMMY_PHOTO_WIDTH, CAMMY_PHOTO_HEIGHT, 8, PNG_TRUECOLOR, buf)) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "png_set_data()", argv[6], png_error_string(error)); goto error_png_set_data; } png_close_file(png); free(buf); free(png); cammy_sram_close(sram); return 0; error_png_set_data: free(buf); error_malloc_buf: png_close_file(png); error_png_open_file_write: free(png); error_malloc_png: cammy_sram_close(sram); error_sram_open: return 1; } static int import_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 = cammy_image_open_tile(argv[4], CAMMY_SCREEN_WIDTH, CAMMY_SCREEN_HEIGHT)) == NULL) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "cammy_image_open_tile()", argv[4], strerror(errno)); goto error_image_open_tile; } if ((dest = malloc(sizeof(*dest))) == NULL) { goto error_malloc_dest; } dest->format = CAMMY_IMAGE_TILE; dest->tiles = (cammy_tile *)&sram->data->photos[photo-1].tiles; dest->width = CAMMY_PHOTO_WIDTH; dest->height = CAMMY_PHOTO_HEIGHT; cammy_image_copy(dest, src, 0, 0, 16, 16, CAMMY_PHOTO_WIDTH, CAMMY_PHOTO_HEIGHT); free(dest); cammy_image_close(src); cammy_sram_close(sram); return 0; error_malloc_dest: cammy_image_close(src); error_image_open_tile: cammy_sram_close(sram); 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; if (argc < 3) { usage(argc, argv, "No input Game Boy screen tile data file provided"); } else if (argc < 4) { usage(argc, argv, "No output PNG filename provided"); } if ((fd = open(argv[2], O_RDONLY)) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "open()", argv[2], strerror(errno)); goto error_open; } if ((in = malloc(CAMMY_SCREEN_SIZE)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "malloc()", strerror(errno)); goto error_malloc_in; } if (read(fd, in, CAMMY_SCREEN_SIZE) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "read()", argv[2], strerror(errno)); goto error_read; } if ((out = malloc(CAMMY_SCREEN_WIDTH * CAMMY_SCREEN_HEIGHT * 3)) == NULL) { fprintf(stderr, "%s: %s: %s\n", argv[0], "malloc()", strerror(errno)); goto error_malloc_out; } cammy_image_copy_from_tile(out, in, CAMMY_SCREEN_WIDTH, CAMMY_SCREEN_HEIGHT, 3, &palette); if (save_buf_to_png_file(argv[3], out, CAMMY_SCREEN_WIDTH, CAMMY_SCREEN_HEIGHT, 8, PNG_TRUECOLOR) < 0) { fprintf(stderr, "%s: %s: %s: %s\n", argv[0], "save_buf_to_png_file()", argv[3], strerror(errno)); goto error_save_buf_to_png_file; } return 0; error_save_buf_to_png_file: free(out); error_malloc_out: error_read: free(in); error_malloc_in: close(fd); error_open: return errno; } static int slice(int argc, char **argv) { uint8_t *buf; size_t width, height, x_pad, y_pad; int depth; if (argc < 3) { usage(argc, argv, "No X padding provided"); } else if (argc < 4) { usage(argc, argv, "No Y padding provided"); } else if (argc < 5) { usage(argc, argv, "No input PNG image provided"); } x_pad = atoi(argv[2]); y_pad = atoi(argv[3]); if ((buf = read_png_file(argv[4], &width, &height, &depth)) == NULL) { goto error_read_png_file; } if (cammy_image_slice(buf, width, height, x_pad, y_pad, depth) < 0) { goto error_image_slice; } free(buf); return 0; error_image_slice: free(buf); error_read_png_file: return 1; } static struct { char *name; int (*fun)(int, char **); } commands[] = { { "import", import }, { "export", export }, { "dither", dither }, { "split-rgb", split_rgb }, { "merge-rgb", merge_rgb }, { "import-tile", import_tile }, { "export-tile", export_tile }, { "convert-tile", convert_tile }, { "slice", slice }, { NULL, NULL } }; int main(int argc, char **argv) { int i; if (argc < 2) { usage(argc, argv, "No command specified"); } for (i=0; commands[i].name; i++) { if (strcmp(argv[1], commands[i].name) == 0) { return commands[i].fun(argc, argv); } } usage(argc, argv, "Unknown command '%s'", argv[1]); return 0; }