/* SPDX-License-Identifier: GPL-2.0-or-later */ /* SPDX-FileCopyrightText: 2020 Laurent Pinchart */ #include #include #include #include #include #include #include #include #include #include #include #include "crc.c" struct image_rect { int left; int top; unsigned int width; unsigned int height; }; struct image_size { unsigned int width; unsigned int height; }; struct options { const char *filename; struct image_rect crop; struct image_size size; }; /* ----------------------------------------------------------------------------- * Miscellaneous helpers */ static bool rect_is_empty(const struct image_rect *rect) { return !rect->width || !rect->height; } static bool rect_is_null(const struct image_rect *rect) { return !rect->left && !rect->top && !rect->width && !rect->height; } static bool size_is_null(const struct image_size *size) { return !size->width && !size->height; } static int readall(int fd, void *buf, size_t count) { int ret; do { ret = read(fd, buf, count); if (ret == -1) return -errno; if (ret == 0) return -ENODATA; count -= ret; buf += ret; } while (count); return 0; } /* ----------------------------------------------------------------------------- * Usage and argument parsing */ static void usage(const char *argv0) { printf("Usage: %s [options] \n\n", argv0); printf("Calculate the R-Car DISCOM CRC for the image stored in \n\n"); printf("Supported options:\n"); printf("-c, --crop (X,Y)/WxH Crop the input image (needs --size)\n"); printf("-s, --size WxH Input image size\n"); } static struct option opts[] = { {"crop", 1, 0, 'c'}, {"size", 1, 0, 's'}, {0, 0, 0, 0} }; static int parse_args(struct options *options, int argc, char *argv[]) { int ret; int c; if (argc < 2) { usage(argv[0]); return 1; } memset(options, 0, sizeof(*options)); opterr = 0; while ((c = getopt_long(argc, argv, "c:s:", opts, NULL)) != -1) { int count; switch (c) { case 'c': ret = sscanf(optarg, "(%d,%d)/%ux%u%n", &options->crop.left, &options->crop.top, &options->crop.width, &options->crop.height, &count); if (ret != 4 || count != (int)strlen(optarg)) { printf("Invalid crop value '%s'\n", optarg); return 1; } break; case 's': ret = sscanf(optarg, "%ux%u%n", &options->size.width, &options->size.height, &count); if (ret != 2 || count != (int)strlen(optarg)) { printf("Invalid size value '%s'\n", optarg); return 1; } break; default: printf("Invalid option -%c\n", c); printf("Run %s -h for help.\n", argv[0]); return 1; } } if (optind != argc - 1) { usage(argv[0]); return 1; } options->filename = argv[optind]; return 0; } /* ----------------------------------------------------------------------------- * Main */ int main(int argc, char *argv[]) { struct options options; void *image = NULL; uint32_t crc; off_t offset; off_t size; int fd = -1; int ret; /* Parse and validate options. */ ret = parse_args(&options, argc, argv); if (ret) return ret; if (!rect_is_null(&options.crop)) { if (size_is_null(&options.size)) { printf("--crop requires --size\n"); goto error; } if (rect_is_empty(&options.crop)) { printf("Crop rectangle is empty\n"); goto error; } if (options.crop.left < 0 || options.crop.top < 0 || options.crop.left + options.crop.width > options.size.width || options.crop.top + options.crop.height > options.size.height) { printf("Crop rectangle out of image bounds\n"); goto error; } } /* Open the file and determine its size. */ fd = open(options.filename, O_RDONLY); if (fd == -1) { printf("Failed to open '%s': %s\n", options.filename, strerror(errno)); goto error; } size = lseek(fd, 0, SEEK_END); if (size == -1) { printf("Failed to determine file size: %s\n", strerror(errno)); goto error; } if (!size_is_null(&options.size) && options.size.width * options.size.height * 4 != size) { printf("Image size %ux%u doesn't match file size %jd\n", options.size.width, options.size.height, (intmax_t)size); goto error; } /* Read the image data. */ if (!rect_is_null(&options.crop)) size = options.crop.width * options.crop.height * 4; image = malloc(size); if (!image) { printf("Unable to allocate memory for image data\n"); goto error; } offset = (options.crop.top * options.size.width + options.crop.left) * 4; lseek(fd, offset, SEEK_SET); if (!options.crop.width || options.crop.width == options.size.width) { /* * When the crop rectangle width spans the whole image, read it * in one go. */ ret = readall(fd, image, size); if (ret < 0) { printf("Unable to read image: %s\n", strerror(errno)); goto error; } } else { /* Otherwise, read line by line. */ void *line = image; unsigned int y; offset = (options.size.width - options.crop.width) * 4; for (y = 0; y < options.crop.height; ++y) { ret = readall(fd, line, options.crop.width * 4); if (ret < 0) { printf("Unable to read line %u: %s\n", y, strerror(errno)); goto error; } lseek(fd, offset, SEEK_CUR); line += options.crop.width * 4; } } close(fd); fd = -1; /* * Compute the CRC. The generate CRC code XORs the initial value with * the final XOR value, so we need to pass 0 to get the desired * 0xffffffff initial value. */ crc = calculate_crc(image, size, 0); free(image); image = NULL; printf("0x%08x\n", crc); return 0; error: free(image); if (fd != -1) close(fd); return 1; }