| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <arpa/inet.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include <string> |
| |
| #include "makepng.h" |
| #include "unpackicon.h" |
| |
| #define arraysize(x) (sizeof(x) / sizeof(x[0])) |
| |
| namespace { |
| |
| struct IconType { |
| uint32_t magic; |
| const char* filename; |
| }; |
| |
| // clang-format off |
| const IconType icon_types[] = { |
| { 'icp4', "16x16" }, |
| { 'icp5', "32x32" }, |
| { 'icp6', "64x64" }, |
| { 'ic07', "128x128" }, |
| { 'ic08', "256x256" }, |
| { 'ic09', "512x512" }, |
| { 'ic10', "512x512@2x" }, // previously 1024x1024 |
| { 'ic11', "16x16@2x" }, |
| { 'ic12', "32x32@2x" }, |
| { 'ic13', "128x128@2x" }, |
| { 'ic14', "256x256@2x" }, |
| }; |
| // clang-format on |
| |
| struct IcnsHeader { |
| uint32_t magic; |
| uint32_t length; |
| }; |
| |
| struct IconHeader { |
| uint32_t magic; |
| uint32_t length; |
| }; |
| |
| bool IsPrintableASCIINoSlash(char c) { |
| return c >= ' ' && c <= '~' && c != '/'; |
| } |
| |
| std::string FourCCToASCII(uint32_t fourcc) { |
| const char chars[] = { |
| fourcc >> 24, |
| (fourcc >> 16) & 0xff, |
| (fourcc >> 8) & 0xff, |
| fourcc & 0xff, |
| }; |
| |
| bool use_ascii = true; |
| for (size_t i = 0; i < arraysize(chars); ++i) { |
| if (!IsPrintableASCIINoSlash(chars[i])) { |
| use_ascii = false; |
| break; |
| } |
| } |
| |
| char buf[11]; |
| if (use_ascii) { |
| snprintf(buf, sizeof(buf), "%c%c%c%c", chars[0], chars[1], chars[2], |
| chars[3]); |
| } else { |
| snprintf(buf, sizeof(buf), "0x%x", fourcc); |
| } |
| |
| return std::string(buf); |
| } |
| |
| } // namespace |
| |
| int main(int argc, char* argv[]) { |
| const char* me = argv[0]; |
| |
| if (argc != 3) { |
| fprintf(stderr, "usage: %s <icns> <iconset>\n", me); |
| return EXIT_FAILURE; |
| } |
| |
| const char* input_path = argv[1]; |
| int input_fd = open(input_path, O_RDONLY); |
| if (input_fd < 0) { |
| fprintf(stderr, "%s: open %s: %s\n", me, input_path, strerror(errno)); |
| return EXIT_FAILURE; |
| } |
| |
| const char* output_path = argv[2]; |
| if (mkdir(output_path, 0755) != 0 && errno != EEXIST) { |
| fprintf(stderr, "%s: mkdir %s: %s\n", me, output_path, strerror(errno)); |
| return EXIT_FAILURE; |
| } |
| |
| size_t total_read = 0; |
| IcnsHeader icns_header; |
| ssize_t nread = read(input_fd, &icns_header, sizeof(icns_header)); |
| if (nread < 0) { |
| fprintf(stderr, "%s: read: %s\n", me, strerror(errno)); |
| return EXIT_FAILURE; |
| } else if (nread != sizeof(icns_header)) { |
| fprintf(stderr, "%s: read: expected %zu, observed %zd\n", me, |
| sizeof(icns_header), nread); |
| return EXIT_FAILURE; |
| } |
| total_read += nread; |
| |
| const uint32_t icns_header_magic = ntohl(icns_header.magic); |
| if (icns_header_magic != 'icns') { |
| fprintf(stderr, "%s: icns file magic: expected 0x%x, observed 0x%x\n", me, |
| 'icns', icns_header_magic); |
| return EXIT_FAILURE; |
| } |
| |
| const uint32_t icns_header_length = ntohl(icns_header.length); |
| |
| struct ImageAndMask { |
| std::string image_path; |
| std::string mask_path; |
| std::string png_path; |
| }; |
| ImageAndMask images_and_masks[4]; |
| enum ImageAndMaskIndex { |
| IMAGE_16, |
| IMAGE_32, |
| IMAGE_48, |
| IMAGE_128, |
| IMAGE_NONE, |
| }; |
| const size_t kImageAndMaskDimensions[] = {16, 32, 48, 128}; |
| |
| while (total_read < icns_header_length) { |
| IconHeader icon_header; |
| nread = read(input_fd, &icon_header, sizeof(icon_header)); |
| if (nread < 0) { |
| fprintf(stderr, "%s: read: %s\n", me, strerror(errno)); |
| return EXIT_FAILURE; |
| } else if (nread != sizeof(icon_header)) { |
| fprintf(stderr, "%s: read: expected %zd, observed %zu\n", me, |
| sizeof(icon_header), nread); |
| return EXIT_FAILURE; |
| } |
| total_read += nread; |
| |
| size_t icon_length = ntohl(icon_header.length) - sizeof(icon_header); |
| char* icon_data = reinterpret_cast<char*>(malloc(icon_length)); |
| if (!icon_data) { |
| fprintf(stderr, "%s: malloc %zu: %s\n", me, icon_length, strerror(errno)); |
| return EXIT_FAILURE; |
| } |
| |
| size_t icon_data_read = 0; |
| while (icon_length - icon_data_read > 0) { |
| nread = read(input_fd, icon_data + icon_data_read, |
| icon_length - icon_data_read); |
| if (nread < 0) { |
| fprintf(stderr, "%s: read: %s\n", me, strerror(errno)); |
| return EXIT_FAILURE; |
| } else if (nread > icon_length - icon_data_read) { |
| fprintf(stderr, "%s: read: expected %zu, observed %zd\n", me, |
| icon_length - icon_data_read, nread); |
| return EXIT_FAILURE; |
| } |
| icon_data_read += nread; |
| } |
| total_read += icon_data_read; |
| |
| const uint32_t icon_header_magic = ntohl(icon_header.magic); |
| bool found = false; |
| std::string output_icon_path = output_path; |
| output_icon_path += '/'; |
| for (size_t icon_type_index = 0; icon_type_index < arraysize(icon_types); |
| ++icon_type_index) { |
| const IconType& icon_type = icon_types[icon_type_index]; |
| if (icon_header_magic == icon_type.magic) { |
| found = true; |
| output_icon_path += "icon_"; |
| output_icon_path += icon_type.filename; |
| output_icon_path += ".png"; |
| break; |
| } |
| } |
| |
| if (!found) { |
| output_icon_path += FourCCToASCII(icon_header_magic); |
| } |
| |
| const char* output_icon_path_c = output_icon_path.c_str(); |
| printf("%s\n", output_icon_path_c); |
| |
| int output_fd; |
| output_fd = open(output_icon_path_c, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
| if (output_fd < 0) { |
| fprintf(stderr, "%s: open %s: %s\n", me, output_icon_path_c, |
| strerror(errno)); |
| return EXIT_FAILURE; |
| } |
| |
| size_t icon_data_written = 0; |
| while (icon_length - icon_data_written > 0) { |
| ssize_t nwritten = write(output_fd, icon_data + icon_data_written, |
| icon_length - icon_data_written); |
| if (nwritten < 0) { |
| fprintf(stderr, "%s: write: %s\n", me, strerror(errno)); |
| return EXIT_FAILURE; |
| } else if (nwritten > icon_length - icon_data_written) { |
| fprintf(stderr, "%s: write: expected %zu, observed %zd\n", me, |
| icon_length - icon_data_written, nwritten); |
| return EXIT_FAILURE; |
| } |
| icon_data_written += nwritten; |
| } |
| |
| if (close(output_fd) < 0) { |
| fprintf(stderr, "%s: close %s: %s\n", me, output_icon_path_c, |
| strerror(errno)); |
| return EXIT_FAILURE; |
| } |
| |
| free(icon_data); |
| |
| ImageAndMaskIndex index = IMAGE_NONE; |
| switch (icon_header_magic) { |
| case 'is32': |
| index = IMAGE_16; |
| break; |
| case 'il32': |
| index = IMAGE_32; |
| break; |
| case 'ih32': |
| index = IMAGE_48; |
| break; |
| case 'it32': |
| index = IMAGE_128; |
| break; |
| case 's8mk': |
| images_and_masks[IMAGE_16].mask_path = output_icon_path; |
| break; |
| case 'l8mk': |
| images_and_masks[IMAGE_32].mask_path = output_icon_path; |
| break; |
| case 'h8mk': |
| images_and_masks[IMAGE_48].mask_path = output_icon_path; |
| break; |
| case 't8mk': |
| images_and_masks[IMAGE_128].mask_path = output_icon_path; |
| break; |
| } |
| |
| if (index != IMAGE_NONE) { |
| images_and_masks[index].image_path = output_icon_path; |
| images_and_masks[index].png_path = output_icon_path + ".png"; |
| } |
| |
| if ((icon_header_magic == 'is32' && icon_length != 16 * 16 * 3) || |
| (icon_header_magic == 'il32' && icon_length != 32 * 32 * 3) || |
| (icon_header_magic == 'ih32' && icon_length != 48 * 48 * 3) || |
| (icon_header_magic == 'it32' && icon_length != 128 * 128 * 3)) { |
| const std::string output_icon_unpacked_path = |
| output_icon_path + ".unpacked"; |
| const char* output_icon_unpacked_path_c = |
| output_icon_unpacked_path.c_str(); |
| printf("%s\n", output_icon_unpacked_path_c); |
| |
| // is32 and il32 definitely don’t use skip. it32 definitely does. I’m not |
| // sure about ih32, but I think it doesn’t use skip. |
| const bool skip = icon_header_magic == 'it32'; |
| |
| if (!UnpackIcon(output_icon_path_c, output_icon_unpacked_path_c, skip)) { |
| return EXIT_FAILURE; |
| } |
| |
| assert(index != IMAGE_NONE); |
| images_and_masks[index].image_path = output_icon_unpacked_path; |
| } |
| } |
| |
| for (size_t index = 0; index < arraysize(images_and_masks); ++index) { |
| const ImageAndMask& image_and_mask = images_and_masks[index]; |
| if (!image_and_mask.image_path.empty() && |
| !image_and_mask.mask_path.empty()) { |
| printf("%s\n", image_and_mask.png_path.c_str()); |
| if (!EncodePNG(image_and_mask.image_path.c_str(), |
| image_and_mask.mask_path.c_str(), |
| image_and_mask.png_path.c_str(), |
| kImageAndMaskDimensions[index])) { |
| return EXIT_FAILURE; |
| } |
| } |
| } |
| |
| if (total_read != icns_header_length) { |
| fprintf(stderr, "%s: icns file length: expected %d, observed %zu\n", me, |
| icns_header_length, total_read); |
| return EXIT_FAILURE; |
| } |
| |
| if (close(input_fd) < 0) { |
| fprintf(stderr, "%s: close %s: %s\n", me, input_path, strerror(errno)); |
| return EXIT_FAILURE; |
| } |
| |
| return EXIT_SUCCESS; |
| } |