├── LICENSE ├── Makefile ├── ibootim.c ├── ibootim.h ├── lzss.c ├── lzss.h └── main.c /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Pupyshev Nikita 2 | All rights reserved 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted providing that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 21 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 22 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | gcc ibootim.c lzss.c main.c -lpng -o ibootim 3 | -------------------------------------------------------------------------------- /ibootim.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2015 Pupyshev Nikita 3 | * All rights reserved 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted providing that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include "ibootim.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include "lzss.h" 36 | 37 | #define IBOOTIM_HEADER_SIZE sizeof(struct ibootim_header) 38 | 39 | typedef struct { 40 | uint8_t brightness; 41 | uint8_t alpha; 42 | } ibootim_grayscale_pixel; 43 | 44 | typedef struct { 45 | uint8_t blue; 46 | uint8_t green; 47 | uint8_t red; 48 | uint8_t alpha; 49 | } ibootim_argb_pixel; 50 | 51 | typedef struct { 52 | uint8_t red; 53 | uint8_t green; 54 | uint8_t blue; 55 | uint8_t alpha; 56 | } pixel_rgba_8; 57 | 58 | typedef struct { 59 | uint8_t brightness; 60 | uint8_t alpha; 61 | } pixel_grayscale_8; 62 | 63 | typedef struct { 64 | uint16_t red; 65 | uint16_t green; 66 | uint16_t blue; 67 | uint16_t alpha; 68 | } pixel_rgba_16; 69 | 70 | typedef struct { 71 | uint16_t brightness; 72 | uint16_t alpha; 73 | } pixel_grayscale_16; 74 | 75 | const char *ibootim_signature = "iBootIm"; 76 | 77 | struct ibootim_header { 78 | char signature[8]; 79 | uint32_t adler; 80 | uint32_t compressionType; 81 | uint32_t colorSpace; 82 | uint16_t width; 83 | uint16_t height; 84 | int16_t offsetX; 85 | int16_t offsetY; 86 | uint32_t compressedSize; 87 | uint32_t reserved[8]; 88 | }; 89 | 90 | typedef union { 91 | ibootim_argb_pixel *argb; 92 | ibootim_grayscale_pixel *grayscale; 93 | void *pointer; 94 | } ibootim_pixel_buffer_t; 95 | 96 | typedef struct ibootim { 97 | uint16_t width; 98 | uint16_t height; 99 | int16_t offsetX; 100 | int16_t offsetY; 101 | ibootim_compression_type_t compressionType; 102 | ibootim_color_space_t colorSpace; 103 | ibootim_pixel_buffer_t pixels; 104 | } ibootim; 105 | 106 | static unsigned _adler32(unsigned adler, const unsigned char* data, unsigned len) { 107 | unsigned s1 = adler & 0xffff; 108 | unsigned s2 = (adler >> 16) & 0xffff; 109 | 110 | while(len > 0) { 111 | /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ 112 | unsigned amount = len > 5550 ? 5550 : len; 113 | len -= amount; 114 | while(amount > 0) { 115 | s1 += (*data++); 116 | s2 += s1; 117 | --amount; 118 | } 119 | s1 %= 65521; 120 | s2 %= 65521; 121 | } 122 | 123 | return (s2 << 16) | s1; 124 | } 125 | 126 | static void *_ibootim_pixel_ptr_at(ibootim *image, uint16_t x, uint16_t y); 127 | static void _ibootim_set_pixel_with_params(ibootim *image, void *png_pixel, uint16_t x, uint16_t y, int bit_depth, int has_alpha); 128 | static inline void *_ibootim_get_row(ibootim *image, unsigned int row); 129 | 130 | static unsigned int _ibootim_pixel_size_for_color_space(ibootim_color_space_t colorSpace) { 131 | switch (colorSpace) { 132 | case ibootim_color_space_argb: 133 | return sizeof(ibootim_argb_pixel); 134 | case ibootim_color_space_grayscale: 135 | return sizeof(ibootim_grayscale_pixel); 136 | default: 137 | return 0; //invalid color space value 138 | } 139 | } 140 | 141 | static int _ibootim_sanity_check_header(struct ibootim_header *imageHeader, const char **errDst) { 142 | ibootim_color_space_t colorSpace; 143 | ibootim_compression_type_t compressionType; 144 | 145 | //check if header has proper magic 146 | if (memcmp(imageHeader->signature, ibootim_signature, 8) != 0) { 147 | if (errDst != NULL) *errDst = "header does not have \"iBootIm\" signature"; 148 | return -1; 149 | } 150 | //check if compression type field has valid value 151 | compressionType = imageHeader->compressionType; 152 | if (compressionType != ibootim_compression_type_lzss) { 153 | if (errDst != NULL) *errDst = "invalid compression type field value"; 154 | return -1; 155 | } 156 | //check if color space field has known value 157 | colorSpace = imageHeader->colorSpace; 158 | if (colorSpace != ibootim_color_space_argb && 159 | colorSpace != ibootim_color_space_grayscale) { 160 | if (errDst != NULL) *errDst = "invalid color space field value"; 161 | return -1; 162 | } 163 | 164 | return 0; 165 | } 166 | 167 | static unsigned int _ibootim_get_pixel_buffer_size(ibootim *image) { 168 | unsigned int width = image->width, height = image->height; 169 | unsigned int pixelSize = _ibootim_pixel_size_for_color_space(image->colorSpace); 170 | return width * height * pixelSize; 171 | } 172 | 173 | int ibootim_load(const char *path, ibootim **handle) { 174 | return ibootim_load_at_index(path, handle, 0); 175 | } 176 | 177 | int ibootim_load_at_index(const char *path, ibootim **handle, unsigned int targetIndex) { 178 | int rc; 179 | const char *errorDesc; 180 | ssize_t items; 181 | FILE *inputFile; 182 | struct ibootim_header header; 183 | unsigned int width, height; 184 | unsigned int pixelsCount, pixelSize, compressedSize; 185 | ssize_t expectedUncompressedSize, actualUncompressedSize; 186 | 187 | if (targetIndex == UINT_MAX) { 188 | printf("[-] INTERNAL ERROR: iBootIm image index is equal to UINT_MAX."); 189 | return EINVAL; 190 | } 191 | 192 | inputFile = fopen(path, "r"); 193 | if (!inputFile) { 194 | printf("[-] Failed to open '%s': %s, aborting.\n", path, strerror(errno)); 195 | return ENOENT; 196 | } 197 | 198 | for (unsigned int i = 0; i <= targetIndex; i++) { 199 | //This complex structure was introduced to handle both errors in one place 200 | items = fread(&header, sizeof(header), 1, inputFile); 201 | if (items == 1) { 202 | //sanity check the header 203 | rc = _ibootim_sanity_check_header(&header, &errorDesc); 204 | if (rc != 0) { 205 | printf("[-] Invalid iBootIm image header: %s.\n", errorDesc); 206 | fclose(inputFile); 207 | return EFTYPE; 208 | } 209 | 210 | //We don't need to skip the compressed data of the image we are going 211 | //to load. 212 | if (i != targetIndex) { 213 | compressedSize = header.compressedSize; 214 | rc = fseek(inputFile, compressedSize, SEEK_CUR); 215 | } else { 216 | rc = 0; 217 | } 218 | } else { 219 | rc = -1; 220 | } 221 | 222 | //Check if an error occurred during file I/O and handle report it to the 223 | //user if so. 224 | if (rc != 0) { 225 | if (feof(inputFile)) { 226 | printf("[-] iBootIm file is either truncated or image index is out of bounds.\n"); 227 | rc = EFTYPE; 228 | } else { 229 | printf("[-] An I/O error occurred while reading iBootIm header at index %u (offset %lu): %s.\n", i, ftell(inputFile), strerror(ferror(inputFile))); 230 | rc = EIO; 231 | } 232 | fclose(inputFile); 233 | return rc; 234 | } 235 | } 236 | 237 | //No integer overflow should occur, since header.width and header.height 238 | //are uint16_t, all the following variables are unsigned int. 239 | width = header.width; 240 | height = header.height; 241 | pixelsCount = width * height; 242 | pixelSize = _ibootim_pixel_size_for_color_space(header.colorSpace); 243 | //Finally we get to the compressed and uncompressed sizes, not verifying 244 | //them yet. 245 | compressedSize = header.compressedSize; 246 | expectedUncompressedSize = pixelsCount * pixelSize; 247 | 248 | //Read compressed image data. 249 | void *compressedData = malloc(compressedSize); 250 | if (!compressedData) { 251 | fclose(inputFile); 252 | printf("[-] Can not allocate memory for compressed image data, aborting.\n"); 253 | return ENOMEM; 254 | } 255 | items = fread(compressedData, 1, compressedSize, inputFile); 256 | //nothing else will be read here, so close the file 257 | fclose(inputFile); 258 | if (items != compressedSize) { 259 | //Determine what kind of error has occurred. 260 | if (feof(inputFile)) { 261 | printf("[-] iBootIm image data is truncated.\n"); 262 | rc = EFTYPE; 263 | } else { 264 | printf("[-] An I/O error occurred while reading iBootIm image data: %s.\n", strerror(ferror(inputFile))); 265 | rc = EIO; 266 | } 267 | //clean up and return error code 268 | free(compressedData); 269 | return rc; 270 | } 271 | 272 | unsigned headerAdler = _adler32(1, 273 | (void *)&header.compressionType, 274 | sizeof(header) - offsetof(struct ibootim_header, compressionType)); 275 | unsigned imageAdler = _adler32(headerAdler, compressedData, compressedSize); 276 | if (header.adler != imageAdler) { 277 | printf("[!] Checksum in the header is not valid (0x%08x != 0x%08x).\n", imageAdler, header.adler); 278 | } 279 | 280 | //decompress pixel data 281 | void *pixelData = malloc(expectedUncompressedSize); 282 | actualUncompressedSize = lzss_decompress(pixelData, 283 | (unsigned int)expectedUncompressedSize, 284 | compressedData, 285 | compressedSize); 286 | free(compressedData); 287 | if (actualUncompressedSize <= 0) { 288 | free(pixelData); 289 | printf("[-] An error occurred during decompression of pixel data, aborting.\n"); 290 | return EFTYPE; 291 | } else if (actualUncompressedSize != expectedUncompressedSize) { 292 | printf("[!] Actual length of uncompressed pixel data is less than expected."); 293 | memset(&pixelData[actualUncompressedSize], 0, expectedUncompressedSize - actualUncompressedSize); 294 | } 295 | 296 | //finally allocate memory for ibootim structure and fill it 297 | ibootim *image = malloc(sizeof(ibootim)); 298 | if (!image) { 299 | free(pixelData); 300 | printf("[-] Memory allocation error, aborting.\n"); 301 | return ENOMEM; 302 | } 303 | image->width = width; 304 | image->height = height; 305 | image->offsetX = header.offsetX; 306 | image->offsetY = header.offsetY; 307 | image->compressionType = header.compressionType; 308 | image->colorSpace = header.colorSpace; 309 | image->pixels.pointer = pixelData; 310 | 311 | //write handle and return the image gracefully 312 | *handle = image; 313 | return 0; 314 | }; 315 | 316 | int ibootim_convert_to_colorspace(ibootim *image, ibootim_color_space_t targetColorSpace) { 317 | int rc; 318 | ibootim_color_space_t sourceColorSpace; 319 | ibootim_argb_pixel *argbPixelPtr; 320 | ibootim_grayscale_pixel *grayscalePixelPtr; 321 | size_t pixelsCount, bufferSize; 322 | void *pixelBuffer; 323 | 324 | sourceColorSpace = image->colorSpace; 325 | if (sourceColorSpace == targetColorSpace) return 0; // nothing to do here 326 | pixelsCount = image->width * image->height; 327 | 328 | if (targetColorSpace == ibootim_color_space_grayscale) { 329 | if (sourceColorSpace == ibootim_color_space_argb) { 330 | argbPixelPtr = image->pixels.argb; 331 | grayscalePixelPtr = image->pixels.grayscale; 332 | 333 | puts("[*] Converting image from argb to grayscale color space..."); 334 | 335 | uint8_t brightness, alpha; 336 | for (unsigned int i = 0; i < pixelsCount; i++) { 337 | //first get the values, since structures are in the same buffer 338 | alpha = argbPixelPtr->alpha; 339 | brightness = ((unsigned int)argbPixelPtr->red + 340 | (unsigned int)argbPixelPtr->green + 341 | (unsigned int)argbPixelPtr->blue) / 3; 342 | //then write them down 343 | grayscalePixelPtr->brightness = brightness; 344 | grayscalePixelPtr->alpha = alpha; 345 | //finally advance the pointers 346 | argbPixelPtr++; 347 | grayscalePixelPtr++; 348 | } 349 | 350 | //Try to reallocate the buffer and leave it as is if we get an error for 351 | //some weird reason. 352 | bufferSize = pixelsCount * sizeof(ibootim_grayscale_pixel); 353 | pixelBuffer = realloc(image->pixels.pointer, bufferSize); 354 | if (pixelBuffer) image->pixels.pointer = pixelBuffer; 355 | else { 356 | puts("[!] Reducing image pixel buffer size failed, leaving as-is."); 357 | } 358 | 359 | //Set colorSpace field after it's all done and return success. 360 | image->colorSpace = ibootim_color_space_grayscale; 361 | rc = 0; 362 | } else { 363 | printf("[-] INTERNAL ERROR: Invalid source color space value 0x%08x.\n", sourceColorSpace); 364 | rc = EFAULT; 365 | } 366 | } else if (targetColorSpace == ibootim_color_space_argb) { 367 | if (sourceColorSpace == ibootim_color_space_grayscale) { 368 | //Try to increase the buffer size and abort if that is not possible. 369 | bufferSize = pixelsCount * sizeof(ibootim_grayscale_pixel); 370 | pixelBuffer = realloc(image->pixels.pointer, bufferSize); 371 | if (pixelBuffer) image->pixels.pointer = pixelBuffer; 372 | else { 373 | puts("[-] Increasing image pixel buffer size failed, aborting."); 374 | return ENOMEM; 375 | } 376 | 377 | //Assign pointers after the buffer has been reallocated. 378 | argbPixelPtr = &image->pixels.argb[pixelsCount]; 379 | grayscalePixelPtr = &image->pixels.grayscale[pixelsCount]; 380 | 381 | puts("[*] Converting image from grayscale to argb color space..."); 382 | 383 | //Convert pixels from the last one to the first one to not overwrite 384 | //unconverted grayscale pixels with converted argb pixels. 385 | uint8_t brightness, alpha; 386 | for (size_t i = pixelsCount; i > 0; i--) { 387 | //First reduce the pointers. 388 | argbPixelPtr--; 389 | grayscalePixelPtr--; 390 | //Then read alpha and brightness before the may be overwritten. 391 | alpha = grayscalePixelPtr->alpha; 392 | brightness = grayscalePixelPtr->brightness; 393 | //Finally write them down to the argb pixel. 394 | argbPixelPtr->red = argbPixelPtr->green = argbPixelPtr->blue = brightness; 395 | argbPixelPtr->alpha = alpha; 396 | } 397 | 398 | //Set colorSpace field after it's all done and return success. 399 | image->colorSpace = ibootim_color_space_argb; 400 | rc = 0; 401 | } else { 402 | printf("[-] INTERNAL ERROR: Invalid source color space value 0x%08x.\n", sourceColorSpace); 403 | rc = EFAULT; 404 | } 405 | } else { 406 | printf("[-] INTERNAL ERROR: Invalid target color space value 0x%08x.\n", targetColorSpace); 407 | rc = EFAULT; 408 | } 409 | 410 | return rc; 411 | } 412 | 413 | int ibootim_write(ibootim *image, const char *path) { 414 | int rc; 415 | FILE *outputFile; 416 | struct ibootim_header header; 417 | unsigned int uncompressedSize = _ibootim_get_pixel_buffer_size(image); 418 | unsigned int estimatedMaxCompSize = uncompressedSize; 419 | ssize_t actualCompSize; 420 | 421 | outputFile = fopen(path, "w"); 422 | if (!outputFile) { 423 | printf("[-] Failed to open '%s' for writing: %s\n", path, strerror(errno)); 424 | return ENOENT; 425 | } 426 | ftruncate(fileno(outputFile), 0); 427 | 428 | memcpy(header.signature, ibootim_signature, 8); 429 | header.width = image->width; 430 | header.height = image->height; 431 | header.offsetX = image->offsetX; 432 | header.offsetY = image->offsetY; 433 | header.colorSpace = image->colorSpace; 434 | header.compressionType = image->compressionType; 435 | memset(header.reserved, 0, sizeof(header.reserved)); 436 | 437 | void *compressedDataBuf = malloc(estimatedMaxCompSize); 438 | if (!compressedDataBuf) { 439 | printf("[-] Memory allocation failed\n"); 440 | return ENOMEM; 441 | } 442 | 443 | //do this while buffer is not large enough 444 | while ((actualCompSize = lzss_compress(compressedDataBuf, 445 | estimatedMaxCompSize, 446 | image->pixels.pointer, 447 | uncompressedSize)) <= 0) { 448 | if (lzss_errno != LZSS_NOMEM) { 449 | printf("[-] An error occurred while compressing pixel data, aborting.\n"); 450 | free(compressedDataBuf); 451 | return EFAULT; 452 | } else { 453 | printf("[!] Compressed pixel data is longer than expected, enlarging buffer.\n"); 454 | estimatedMaxCompSize += 0x100; 455 | free(compressedDataBuf); 456 | compressedDataBuf = malloc(estimatedMaxCompSize); 457 | if (!compressedDataBuf) { 458 | printf("[-] Larger buffer allocation failed, aborting.\n"); 459 | return ENOMEM; 460 | } 461 | } 462 | } 463 | 464 | //complete the header and write it along with data 465 | header.compressedSize = (uint32_t)actualCompSize; 466 | unsigned headerAdler = _adler32(1, (void *)&header.compressionType, sizeof(header) - offsetof(struct ibootim_header, compressionType)); 467 | unsigned imageAdler = _adler32(headerAdler, compressedDataBuf, (unsigned)actualCompSize); 468 | header.adler = imageAdler; 469 | rc = (int)fwrite(&header, sizeof(header), 1, outputFile); 470 | if (rc != 1) { 471 | printf("[-] Failed to write iBootIm header, aborting.\n"); 472 | free(compressedDataBuf); 473 | return EIO; 474 | } 475 | rc = (int)fwrite(compressedDataBuf, (uint32_t)actualCompSize, 1, outputFile); 476 | free(compressedDataBuf); 477 | if (rc != 1) { 478 | printf("[-] Failed to write compressed pixel data, aborting.\n"); 479 | return EIO; 480 | } 481 | 482 | printf("[+] iBootIm image was written to '%s'.\n", path); 483 | return 0; 484 | } 485 | 486 | int ibootim_load_png(const char *path, ibootim **handle) { 487 | FILE *f = fopen(path, "rb"); 488 | if (!f) { 489 | puts("Failed to open file."); 490 | return -1; 491 | } 492 | 493 | //check if file has PNG signature 494 | char fileSignature[8]; 495 | fread(fileSignature, 1, 8, f); 496 | if (png_sig_cmp((png_const_bytep)fileSignature, 0, 8) != 0) { 497 | fclose(f); 498 | puts("Not a png."); 499 | return -1; 500 | } 501 | 502 | ibootim *image = malloc(sizeof(ibootim)); 503 | if (!image) { 504 | fclose(f); 505 | puts("malloc"); 506 | return -1; 507 | } 508 | memset(image, 0, sizeof(ibootim)); 509 | 510 | png_structp read_struct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 511 | if (!read_struct) { 512 | fclose(f); 513 | puts("Failed to make read struct."); 514 | return -1; 515 | } 516 | 517 | png_infop info_struct = png_create_info_struct(read_struct); 518 | if (!info_struct) { 519 | png_destroy_read_struct(&read_struct, NULL, NULL); 520 | fclose(f); 521 | puts("Failed to make info struct."); 522 | return -1; 523 | } 524 | 525 | //setup error handling 526 | if (setjmp(png_jmpbuf(read_struct))) 527 | { 528 | png_destroy_read_struct(&read_struct, &info_struct, NULL); 529 | fclose(f); 530 | puts("libpng error"); 531 | return -1; 532 | } 533 | 534 | //init i/o 535 | png_init_io(read_struct, f); 536 | png_set_sig_bytes(read_struct, 8); 537 | 538 | //read info 539 | png_read_info(read_struct, info_struct); 540 | 541 | //read common info 542 | uint32_t width = png_get_image_width(read_struct, info_struct); 543 | uint32_t height = png_get_image_height(read_struct, info_struct); 544 | uint8_t color_type = png_get_color_type(read_struct, info_struct); 545 | uint8_t bit_depth = png_get_bit_depth(read_struct, info_struct); 546 | 547 | //convert 16-bit colors to 8-bit 548 | if (bit_depth == 16) { 549 | png_set_strip_16(read_struct); 550 | } 551 | 552 | //add alpha if not present 553 | if ((color_type & PNG_COLOR_MASK_ALPHA) == 0) { 554 | png_set_add_alpha(read_struct, 0, PNG_FILLER_AFTER); 555 | } 556 | png_set_invert_alpha(read_struct); 557 | 558 | //if we have RGB colors, convert them to bgr 559 | if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGBA)) { 560 | png_set_bgr(read_struct); 561 | } else if (color_type == PNG_COLOR_TYPE_GA) { 562 | //png_set_swap_alpha(read_struct); 563 | } 564 | 565 | //update structures after setting properties 566 | png_read_update_info(read_struct, info_struct); 567 | 568 | //fill in ibootim image properties 569 | image->width = width; 570 | image->height = height; 571 | image->offsetX = 0; 572 | image->offsetY = 0; 573 | image->compressionType = ibootim_compression_type_lzss; 574 | unsigned int pixelSize; 575 | if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGBA)) { 576 | image->colorSpace = ibootim_color_space_argb; 577 | pixelSize = 4; 578 | } else {//if ((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_GA)) { 579 | image->colorSpace = ibootim_color_space_grayscale; 580 | pixelSize = 2; 581 | } 582 | image->pixels.pointer = malloc(width * height * pixelSize); 583 | 584 | //allocate and fill in array for rows 585 | void **rows = (void **)malloc(height * sizeof(void *)); 586 | if (!rows) { 587 | printf("Failed to alloc rows\n"); 588 | return -1; 589 | } 590 | for (uint16_t y = 0; y < height; y++) 591 | rows[y] = _ibootim_get_row(image, y); 592 | 593 | //read color data 594 | png_read_image(read_struct, (png_bytepp)rows); 595 | 596 | free(rows); 597 | png_read_end(read_struct, NULL); 598 | png_destroy_read_struct(&read_struct, &info_struct, NULL); 599 | 600 | *handle = image; 601 | return 0; 602 | 603 | /*if (path && handle) { 604 | FILE *f = fopen(path, "rb"); 605 | if (f) { 606 | char png_sig[8]; 607 | fread(png_sig, 1, 8, f); 608 | if (png_sig_cmp((png_const_bytep)png_sig, 0, 8) == 0) { 609 | png_structp read_struct = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 610 | if (read_struct) { 611 | png_info *info_struct = png_create_info_struct(read_struct); 612 | if (info_struct) { 613 | ibootim *image = malloc(sizeof(ibootim)); 614 | if (image) { 615 | image->compressionType = ibootim_compression_type_lzss; 616 | image->offsetX = 0; 617 | image->offsetY = 0; 618 | image->pixels.pointer = NULL; 619 | 620 | if (setjmp(png_jmpbuf(read_struct))) { 621 | ret = EFAULT; 622 | 623 | if (image) { 624 | if (image->pixels.pointer) free(image->pixels.pointer); 625 | free(image); 626 | } 627 | 628 | goto end; 629 | } 630 | 631 | png_init_io(read_struct, f); 632 | png_set_sig_bytes(read_struct, 8); 633 | 634 | png_read_info(read_struct, info_struct); 635 | 636 | uint32_t width = png_get_image_width(read_struct, info_struct); 637 | uint32_t height = png_get_image_height(read_struct, info_struct); 638 | image->width = width; 639 | image->height = height; 640 | 641 | uint8_t color_type = png_get_color_type(read_struct, info_struct); 642 | uint8_t bit_depth = png_get_bit_depth(read_struct, info_struct); 643 | int has_alpha = (color_type & PNG_COLOR_MASK_ALPHA) >> 2; 644 | color_type &= ~PNG_COLOR_MASK_ALPHA; 645 | image->colorSpace = color_type == PNG_COLOR_TYPE_GRAY ? ibootim_color_space_grayscale : ibootim_color_space_argb; 646 | 647 | if (((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_RGB)) && (bit_depth == 8)) { 648 | if (color_type == PNG_COLOR_TYPE_RGB) image->colorSpace = ibootim_color_space_argb; 649 | else image->colorSpace = ibootim_color_space_grayscale; 650 | 651 | int pixel_size = ibootim_get_pixel_size(image); 652 | 653 | void *contents = malloc(width * height * pixel_size); 654 | if (contents) { 655 | image->pixels.pointer = contents; 656 | 657 | if (!has_alpha) { 658 | png_set_add_alpha(read_struct, 0, PNG_FILLER_AFTER); 659 | } 660 | png_set_interlace_handling(read_struct); 661 | png_set_invert_alpha(read_struct); 662 | png_set_bgr(read_struct); 663 | 664 | png_read_update_info(read_struct, info_struct); 665 | 666 | void **rows = malloc(height * sizeof(void *)); 667 | if (rows) 668 | for (uint16_t y = 0; y < height; y++) 669 | rows[y] = _ibootim_get_row(image, y); 670 | 671 | png_read_image(read_struct, (png_bytepp)rows); 672 | 673 | free(rows); 674 | ret = 0; 675 | *handle = image; 676 | } 677 | } 678 | 679 | end: 680 | png_destroy_info_struct(read_struct, &info_struct); 681 | } else ret = ENOMEM; 682 | } else ret = ENOMEM; 683 | png_destroy_read_struct(&read_struct, &info_struct, NULL); 684 | } 685 | } else ret = EFTYPE; 686 | fclose(f); 687 | } 688 | } 689 | 690 | return ret;*/ 691 | } 692 | 693 | int ibootim_write_png(ibootim *image, const char *path) { 694 | int ret = -1; 695 | 696 | png_structp write_struct = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 697 | if (!write_struct) return -1; 698 | 699 | png_info *info_struct = png_create_info_struct(write_struct); 700 | if (!info_struct) goto error; 701 | 702 | if (setjmp(png_jmpbuf(write_struct))) goto error; 703 | 704 | png_init_io(write_struct, fopen(path, "wb")); 705 | //png_set_sig_bytes(write_struct, 8); 706 | 707 | uint32_t color_space = image->colorSpace; 708 | int color_type, transforms = PNG_TRANSFORM_INVERT_ALPHA; 709 | if (color_space == ibootim_color_space_argb) { 710 | color_type = PNG_COLOR_TYPE_RGBA; 711 | transforms |= PNG_TRANSFORM_BGR; 712 | } else { 713 | color_type = PNG_COLOR_TYPE_GA; 714 | transforms |= PNG_TRANSFORM_IDENTITY; 715 | } 716 | 717 | png_set_IHDR(write_struct, 718 | info_struct, 719 | image->width, 720 | image->height, 721 | 8, 722 | color_type, 723 | PNG_INTERLACE_NONE, 724 | PNG_COMPRESSION_TYPE_DEFAULT, 725 | PNG_FILTER_TYPE_DEFAULT); 726 | //png_set_invert_alpha(write_struct); 727 | //png_set_bgr(write_struct); 728 | 729 | void **rows = png_malloc(write_struct, image->height * sizeof(void *)); 730 | 731 | for (uint16_t y = 0; y < image->height; y++) 732 | rows[y] = _ibootim_get_row(image, y); 733 | 734 | png_set_rows(write_struct, info_struct, (png_bytepp)rows); 735 | png_write_png(write_struct, info_struct, transforms, NULL); 736 | 737 | png_free(write_struct, rows); 738 | ret = 0; 739 | 740 | error: 741 | png_destroy_write_struct(&write_struct, &info_struct); 742 | 743 | return ret; 744 | } 745 | 746 | void ibootim_close(ibootim *image) { 747 | if (image) { 748 | if (image->pixels.pointer) free(image->pixels.pointer); 749 | free(image); 750 | } 751 | } 752 | 753 | bool file_is_ibootim(const char *path) { 754 | struct ibootim_header header; 755 | int result = 0; 756 | int fd = open(path, O_RDONLY); 757 | if (fd) { 758 | if (read(fd, &header, sizeof(struct ibootim_header)) == sizeof(struct ibootim_header)) { 759 | result = _ibootim_sanity_check_header(&header, NULL) == 0; 760 | } 761 | close(fd); 762 | } 763 | return result; 764 | } 765 | 766 | /* Properties */ 767 | 768 | uint16_t ibootim_get_width(ibootim *image) { 769 | return image->width; 770 | } 771 | 772 | uint16_t ibootim_get_height(ibootim *image) { 773 | return image->height; 774 | } 775 | 776 | int16_t ibootim_get_x_offset(ibootim *image) { 777 | return image->offsetX; 778 | } 779 | 780 | int16_t ibootim_get_y_offset(ibootim *image) { 781 | return image->offsetY; 782 | } 783 | 784 | void ibootim_set_x_offset(ibootim *image, int16_t offset) { 785 | image->offsetX = offset; 786 | } 787 | 788 | void ibootim_set_y_offset(ibootim *image, int16_t offset) { 789 | image->offsetY = offset; 790 | } 791 | 792 | ibootim_color_space_t ibootim_get_color_space(ibootim *image) { 793 | return image->colorSpace; 794 | } 795 | 796 | ibootim_compression_type_t ibootim_get_compression_type(ibootim *image) { 797 | return image->compressionType; 798 | } 799 | 800 | unsigned int ibootim_get_pixel_size(ibootim *image) { 801 | if (image->colorSpace == ibootim_color_space_argb) return 4; 802 | else if (image->colorSpace == ibootim_color_space_grayscale) return 2; 803 | else return 0; 804 | } 805 | 806 | /* Private functions */ 807 | 808 | static void _ibootim_set_pixel_with_params(ibootim *image, void *png_pixel, uint16_t x, uint16_t y, int bit_depth, int has_alpha) { 809 | int png_pixel_size = bit_depth / 8; 810 | 811 | if (image->colorSpace == ibootim_color_space_argb) { 812 | png_pixel_size *= 3 + (has_alpha != 0); 813 | 814 | if (bit_depth == 8) { 815 | pixel_rgba_8 *png_rgba_pixel = png_pixel; 816 | ibootim_argb_pixel *pixel = _ibootim_pixel_ptr_at(image, x, y); 817 | 818 | pixel->red = png_rgba_pixel->red; 819 | pixel->green = png_rgba_pixel->green; 820 | pixel->blue = png_rgba_pixel->blue; 821 | if (has_alpha) pixel->alpha = ~png_rgba_pixel->alpha; 822 | else pixel->alpha = 0xff; 823 | } 824 | else if (bit_depth == 16) { 825 | pixel_rgba_16 *png_rgba_pixel = png_pixel; 826 | ibootim_argb_pixel *pixel = _ibootim_pixel_ptr_at(image, x, y); 827 | 828 | pixel->red = png_rgba_pixel->red / 0x100; 829 | pixel->green = png_rgba_pixel->green / 0x100; 830 | pixel->blue = png_rgba_pixel->blue / 0x100; 831 | if (has_alpha) pixel->alpha = png_rgba_pixel->alpha / 0x100; 832 | else pixel->alpha = 0xff; 833 | } 834 | } else { 835 | if (has_alpha) png_pixel_size *= 2; 836 | 837 | if (bit_depth == 8) { 838 | pixel_grayscale_8 *png_rgba_pixel = png_pixel; 839 | ibootim_grayscale_pixel *pixel = _ibootim_pixel_ptr_at(image, x, y); 840 | 841 | pixel->brightness = png_rgba_pixel->brightness; 842 | if (has_alpha) pixel->alpha = png_rgba_pixel->alpha; 843 | else pixel->alpha = 0xff; 844 | } 845 | else if (bit_depth == 16) { 846 | pixel_grayscale_16 *png_rgba_pixel = png_pixel; 847 | ibootim_grayscale_pixel *pixel = _ibootim_pixel_ptr_at(image, x, y); 848 | 849 | pixel->brightness = png_rgba_pixel->brightness / 0x100; 850 | if (has_alpha) pixel->alpha = png_rgba_pixel->alpha / 0x100; 851 | else pixel->alpha = 0xff; 852 | } 853 | } 854 | } 855 | 856 | int ibootim_count_images_in_file(const char *path, int *error) { 857 | FILE *file; 858 | int ret = -1; 859 | struct ibootim_header header; 860 | 861 | file = fopen(path, "r"); 862 | if (!file) { 863 | if (error) *error = ENOENT; 864 | return -1; 865 | } 866 | 867 | unsigned int count = 0; 868 | while (fread(&header, sizeof(header), 1, file) == 1) { 869 | if (_ibootim_sanity_check_header(&header, NULL) != 0) break; 870 | if (fseek(file, header.compressedSize, SEEK_CUR) == -1) break; 871 | count++; 872 | } 873 | ret = count; 874 | if (error) *error = 0; 875 | 876 | fclose(file); 877 | return ret; 878 | } 879 | 880 | static inline void *_ibootim_pixel_ptr_at(ibootim *image, uint16_t x, uint16_t y) { 881 | return (void *)image->pixels.pointer + (y * image->width + x) * ibootim_get_pixel_size(image); 882 | } 883 | 884 | static inline void *_ibootim_get_row(ibootim *image, unsigned int row) { 885 | int offset = image->width * ibootim_get_pixel_size(image); 886 | return image->pixels.pointer + offset * row; 887 | } 888 | -------------------------------------------------------------------------------- /ibootim.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2015 Pupyshev Nikita 3 | * All rights reserved 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted providing that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef __ibootim_h__ 28 | #define __ibootim_h__ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | typedef struct ibootim ibootim; 35 | 36 | typedef enum { 37 | ibootim_color_space_grayscale = 0x67726579, // 'grey' 38 | ibootim_color_space_argb = 0x61726762 // 'argb' 39 | } ibootim_color_space_t; 40 | 41 | typedef enum { 42 | ibootim_compression_type_lzss = 0x6C7A7373, // 'lzss' 43 | } ibootim_compression_type_t; 44 | 45 | extern const char *ibootim_signature; 46 | 47 | /*! 48 | @function file_is_ibootim 49 | @abstract Checks if file is an iBoot Embedded Image file. 50 | @param path Path to the file. 51 | @result 0 if file is not an iBoot Embedded Image and 1 if it is. 52 | */ 53 | 54 | extern bool file_is_ibootim(const char *path); 55 | 56 | /*! 57 | @function ibootim_load 58 | @abstract Loads an iBoot Embedded Image file. 59 | @discussion Loads and uncompresses LZSS compressed iBoot Embedded Image file located at path 'path' and returns an ibootim image handle that represents an iBoot Embedded Image and must be closed with ibootim_close() function. This is equivalent to ibootim_load_at_index(path, handle, 0). 60 | @param path Path to the iBoot Embedded Image file. 61 | @param handle A pointer where the handle is written on success. 62 | @result UNIX error code or 0 on success. 63 | */ 64 | 65 | extern int ibootim_load(const char *path, ibootim **handle); 66 | 67 | /*! 68 | @function ibootim_load_at_index 69 | @abstract Loads an iBoot Embedded Image file at given index. 70 | @discussion Loads and uncompresses LZSS compressed iBoot Embedded Image file located at path 'path' and returns an ibootim image handle that represents an iBoot Embedded Image and must be closed with ibootim_close() function. This is used for concatenated images. 71 | @param path Path to the iBoot Embedded Image file. 72 | @param handle A pointer where the handle is written on success. 73 | @param index Index of the image in the file. 74 | @result UNIX error code or 0 on success. 75 | */ 76 | 77 | extern int ibootim_load_at_index(const char *path, ibootim **handle, unsigned int index); 78 | 79 | /*! 80 | @function ibootim_load 81 | @abstract Loads a PNG file and converts it into iBoot Embedded Image. 82 | @discussion Loads a PNG file located at path 'path', converts it into an iBoot Embedded Image and returns an ibootim image handle that represents an iBoot Embedded Image and must be closed with ibootim_close() function. 83 | @param path Path to the PNG file. 84 | @param handle A pointer where the handle is written on success. 85 | @result UNIX error code or 0 on success. 86 | */ 87 | 88 | extern int ibootim_load_png(const char *path, ibootim **handle); 89 | 90 | /*! 91 | @function ibootim_get_pixel_size 92 | @abstract Returns the size of one pixel of the image in bytes. 93 | @param image The image. 94 | @result Size of one pixel of the image. 95 | */ 96 | 97 | extern unsigned int ibootim_get_pixel_size(ibootim *image); 98 | 99 | /*! 100 | @function ibootim_write 101 | @abstract Converts the iBoot Embedded Image to a PNG image. 102 | @discussion Converts the iBoot Embedded Image to a PNG image and writes the PNG image to the specified path 103 | @param image The image. 104 | @param path The path where the PNG image will be written. 105 | @result 0 on success or an error code on error. 106 | */ 107 | 108 | extern int ibootim_write(ibootim *image, const char *path); 109 | 110 | /*! 111 | @function ibootim_write_png 112 | @abstract Writes iBoot Embedded Image to a file. 113 | @discussion Compresses and writes iBoot Embedded Image 'image' to a file at path 'path'. 114 | @param image The image. 115 | @param path The path where to write the image. 116 | @result 0 on success or -1 on error. 117 | */ 118 | 119 | extern int ibootim_write_png(ibootim *image, const char *path); 120 | 121 | /*! 122 | @function ibootim_get_expected_size 123 | @abstract Calculates expected raw content length for the image. 124 | @param image The image handle. 125 | @param path Where to write PNG file. 126 | @result Expected content length. 127 | */ 128 | 129 | extern unsigned int ibootim_get_expected_content_size(ibootim *image); 130 | 131 | /*! 132 | @function ibootim_close 133 | @abstract Destroys ibootim image handle. 134 | @param image The image handle. 135 | */ 136 | 137 | extern void ibootim_close(ibootim *image); 138 | 139 | /* Properties */ 140 | 141 | extern uint16_t ibootim_get_width(ibootim *image); 142 | extern uint16_t ibootim_get_height(ibootim *image); 143 | extern int16_t ibootim_get_x_offset(ibootim *image); 144 | extern void ibootim_set_x_offset(ibootim *image, int16_t offset); 145 | extern int16_t ibootim_get_y_offset(ibootim *image); 146 | extern void ibootim_set_y_offset(ibootim *image, int16_t offset); 147 | extern ibootim_color_space_t ibootim_get_color_space(ibootim *image); 148 | extern ibootim_compression_type_t ibootim_get_compression_type(ibootim *image); 149 | 150 | extern int ibootim_convert_to_colorspace(ibootim *image, ibootim_color_space_t targetColorSpace); 151 | extern int ibootim_count_images_in_file(const char *path, int *error); 152 | 153 | #endif /* defined(__ibootim__ibootim__) */ 154 | -------------------------------------------------------------------------------- /lzss.c: -------------------------------------------------------------------------------- 1 | #include "lzss.h" 2 | #include 3 | #include 4 | #include 5 | 6 | lzss_error_t lzss_errno = LZSS_OK; 7 | 8 | const char *lzss_strerror(lzss_error_t error) { 9 | switch (error) { 10 | case LZSS_NOMEM: 11 | return "Memory allocation failure"; 12 | 13 | case LZSS_NODATA: 14 | return "Provided data is too short"; 15 | 16 | case LZSS_INVARG: 17 | return "Invalid argument"; 18 | 19 | case LZSS_OK: 20 | return "No error"; 21 | 22 | default: 23 | return NULL; 24 | } 25 | } 26 | 27 | /************************************************************** 28 | LZSS.C -- A Data Compression Program 29 | (tab = 4 spaces) 30 | *************************************************************** 31 | 4/6/1989 Haruhiko Okumura 32 | Use, distribute, and modify this program freely. 33 | Please send me your improved versions. 34 | PC-VAN SCIENCE 35 | NIFTY-Serve PAF01022 36 | CompuServe 74050,1022 37 | **************************************************************/ 38 | 39 | #define N 4096 /* size of ring buffer - must be power of 2 */ 40 | #define F 18 /* upper limit for match_length */ 41 | #define THRESHOLD 2 /* encode string into position and length if match_length is greater than this */ 42 | #define NIL N /* index for root of binary search trees */ 43 | 44 | ssize_t lzss_decompress(uint8_t *dst, unsigned int dstlen, uint8_t *src, unsigned int srclen) 45 | { 46 | if (dst && src && dstlen && srclen) { 47 | /* ring buffer of size N, with extra F-1 bytes to aid string comparison */ 48 | uint8_t text_buf[N + F - 1]; 49 | uint8_t *dststart = dst; 50 | uint8_t *srcend = src + srclen; 51 | uint8_t *dstend = dst + dstlen; 52 | int i, j, k, r, c; 53 | unsigned int flags; 54 | 55 | srcend = src + srclen; 56 | dst = dststart; 57 | for (i = 0; i < N - F; i++) 58 | text_buf[i] = ' '; 59 | r = N - F; 60 | flags = 0; 61 | 62 | while (1) { 63 | if (((flags >>= 1) & 0x100) == 0) { 64 | if (src < srcend) c = *src++; else break; 65 | flags = c | 0xFF00; /* uses higher byte cleverly */ 66 | } /* to count eight */ 67 | if (flags & 1) { 68 | if (src < srcend) c = *src++; else break; 69 | if (dst < dstend) 70 | *dst++ = c; 71 | else { 72 | lzss_errno = LZSS_NOMEM; 73 | return -1; 74 | } 75 | text_buf[r++] = c; 76 | r &= (N - 1); 77 | } else { 78 | if (src < srcend) i = *src++; else break; 79 | if (src < srcend) j = *src++; else break; 80 | i |= ((j & 0xF0) << 4); 81 | j = (j & 0x0F) + THRESHOLD; 82 | for (k = 0; k <= j; k++) { 83 | c = text_buf[(i + k) & (N - 1)]; 84 | if (dst <= dstend) 85 | *dst++ = c; 86 | else { 87 | lzss_errno = LZSS_NOMEM; 88 | return -1; 89 | } 90 | text_buf[r++] = c; 91 | r &= (N - 1); 92 | } 93 | } 94 | } 95 | 96 | lzss_errno = LZSS_OK; 97 | return (ssize_t)dst - (ssize_t)dststart; 98 | } else { 99 | lzss_errno = LZSS_INVARG; 100 | return -1; 101 | } 102 | } 103 | 104 | struct encode_state { 105 | /* left & right children & parent. These constitute binary search trees. */ 106 | int lchild[N + 1], rchild[N + 257], parent[N + 1]; 107 | 108 | /* ring buffer of size N, with extra F-1 bytes to aid string comparison */ 109 | uint8_t text_buf[N + F - 1]; 110 | 111 | /* match_length of longest match. 112 | * These are set by the insert_node() procedure. 113 | */ 114 | int match_position, match_length; 115 | }; 116 | 117 | /* initialize state, mostly the trees 118 | * 119 | * For i = 0 to N - 1, rchild[i] and lchild[i] will be the right and left 120 | * children of node i. These nodes need not be initialized. Also, parent[i] 121 | * is the parent of node i. These are initialized to NIL (= N), which stands 122 | * for 'not used.' For i = 0 to 255, rchild[N + i + 1] is the root of the 123 | * tree for strings that begin with character i. These are initialized to NIL. 124 | * Note there are 256 trees. */ 125 | static void init_state(struct encode_state *sp) { 126 | int i; 127 | 128 | memset(sp, 0, sizeof(*sp)); 129 | 130 | for (i = 0; i < N - F; i++) 131 | sp->text_buf[i] = ' '; 132 | for (i = N + 1; i <= N + 256; i++) 133 | sp->rchild[i] = NIL; 134 | for (i = 0; i < N; i++) 135 | sp->parent[i] = NIL; 136 | } 137 | 138 | /* Inserts string of length F, text_buf[r..r+F-1], into one of the trees 139 | * (text_buf[r]'th tree) and returns the longest-match position and length 140 | * via the global variables match_position and match_length. 141 | * If match_length = F, then removes the old node in favor of the new one, 142 | * because the old one will be deleted sooner. Note r plays double role, 143 | * as tree node and position in buffer. 144 | */ 145 | static void insert_node(struct encode_state *sp, int r) { 146 | int i, p, cmp; 147 | uint8_t *key; 148 | 149 | cmp = 1; 150 | key = &sp->text_buf[r]; 151 | p = N + 1 + key[0]; 152 | sp->rchild[r] = sp->lchild[r] = NIL; 153 | sp->match_length = 0; 154 | for ( ; ; ) { 155 | if (cmp >= 0) { 156 | if (sp->rchild[p] != NIL) 157 | p = sp->rchild[p]; 158 | else { 159 | sp->rchild[p] = r; 160 | sp->parent[r] = p; 161 | return; 162 | } 163 | } else { 164 | if (sp->lchild[p] != NIL) 165 | p = sp->lchild[p]; 166 | else { 167 | sp->lchild[p] = r; 168 | sp->parent[r] = p; 169 | return; 170 | } 171 | } 172 | for (i = 1; i < F; i++) { 173 | if ((cmp = key[i] - sp->text_buf[p + i]) != 0) 174 | break; 175 | } 176 | if (i > sp->match_length) { 177 | sp->match_position = p; 178 | if ((sp->match_length = i) >= F) 179 | break; 180 | } 181 | } 182 | sp->parent[r] = sp->parent[p]; 183 | sp->lchild[r] = sp->lchild[p]; 184 | sp->rchild[r] = sp->rchild[p]; 185 | sp->parent[sp->lchild[p]] = r; 186 | sp->parent[sp->rchild[p]] = r; 187 | if (sp->rchild[sp->parent[p]] == p) 188 | sp->rchild[sp->parent[p]] = r; 189 | else 190 | sp->lchild[sp->parent[p]] = r; 191 | sp->parent[p] = NIL; /* remove p */ 192 | } 193 | 194 | /* deletes node p from tree */ 195 | static void delete_node(struct encode_state *sp, int p) { 196 | int q; 197 | 198 | if (sp->parent[p] == NIL) 199 | return; /* not in tree */ 200 | if (sp->rchild[p] == NIL) 201 | q = sp->lchild[p]; 202 | else if (sp->lchild[p] == NIL) 203 | q = sp->rchild[p]; 204 | else { 205 | q = sp->lchild[p]; 206 | if (sp->rchild[q] != NIL) { 207 | do { 208 | q = sp->rchild[q]; 209 | } while (sp->rchild[q] != NIL); 210 | sp->rchild[sp->parent[q]] = sp->lchild[q]; 211 | sp->parent[sp->lchild[q]] = sp->parent[q]; 212 | sp->lchild[q] = sp->lchild[p]; 213 | sp->parent[sp->lchild[p]] = q; 214 | } 215 | sp->rchild[q] = sp->rchild[p]; 216 | sp->parent[sp->rchild[p]] = q; 217 | } 218 | sp->parent[q] = sp->parent[p]; 219 | if (sp->rchild[sp->parent[p]] == p) 220 | sp->rchild[sp->parent[p]] = q; 221 | else 222 | sp->lchild[sp->parent[p]] = q; 223 | sp->parent[p] = NIL; 224 | } 225 | 226 | ssize_t lzss_compress(uint8_t *dst, unsigned int dstlen, uint8_t *src, unsigned int srclen) { 227 | if (dst && src && dstlen && srclen) { 228 | /* Encoding state, mostly tree but some current match stuff */ 229 | struct encode_state *sp; 230 | 231 | int i, c, len, r, s, last_match_length, code_buf_ptr; 232 | uint8_t code_buf[17], mask; 233 | uint8_t *srcend = src + srclen; 234 | uint8_t *dstend = dst + dstlen; 235 | uint8_t *dststart = dst; 236 | 237 | /* initialize trees */ 238 | sp = (struct encode_state *) malloc(sizeof(*sp)); 239 | init_state(sp); 240 | 241 | /* code_buf[1..16] saves eight units of code, and code_buf[0] works 242 | * as eight flags, "1" representing that the unit is an unencoded 243 | * letter (1 byte), "" a position-and-length pair (2 bytes). 244 | * Thus, eight units require at most 16 bytes of code. 245 | */ 246 | code_buf[0] = 0; 247 | code_buf_ptr = mask = 1; 248 | 249 | /* Clear the buffer with any character that will appear often. */ 250 | s = 0; r = N - F; 251 | 252 | /* Read F bytes into the last F bytes of the buffer */ 253 | for (len = 0; len < F && src < srcend; len++) 254 | sp->text_buf[r + len] = *src++; 255 | if (!len) { 256 | free(sp); 257 | lzss_errno = LZSS_NODATA; 258 | return -1; 259 | } 260 | /* 261 | * Insert the F strings, each of which begins with one or more 262 | * 'space' characters. Note the order in which these strings are 263 | * inserted. This way, degenerate trees will be less likely to occur. 264 | */ 265 | for (i = 1; i <= F; i++) 266 | insert_node(sp, r - i); 267 | 268 | /* Finally, insert the whole string just read. 269 | * The global variables match_length and match_position are set. 270 | */ 271 | insert_node(sp, r); 272 | do { 273 | /* match_length may be spuriously long near the end of text. */ 274 | if (sp->match_length > len) 275 | sp->match_length = len; 276 | if (sp->match_length <= THRESHOLD) { 277 | sp->match_length = 1; /* Not long enough match. Send one byte. */ 278 | code_buf[0] |= mask; /* 'send one byte' flag */ 279 | code_buf[code_buf_ptr++] = sp->text_buf[r]; /* Send uncoded. */ 280 | } else { 281 | /* Send position and length pair. Note match_length > THRESHOLD. */ 282 | code_buf[code_buf_ptr++] = (uint8_t) sp->match_position; 283 | code_buf[code_buf_ptr++] = (uint8_t) 284 | ( ((sp->match_position >> 4) & 0xF0) 285 | | (sp->match_length - (THRESHOLD + 1)) ); 286 | } 287 | if ((mask <<= 1) == 0) { /* Shift mask left one bit. */ 288 | /* Send at most 8 units of code together */ 289 | for (i = 0; i < code_buf_ptr; i++) 290 | if (dst < dstend) 291 | *dst++ = code_buf[i]; 292 | else { 293 | free(sp); 294 | lzss_errno = LZSS_NOMEM; 295 | return -1; 296 | } 297 | code_buf[0] = 0; 298 | code_buf_ptr = mask = 1; 299 | } 300 | last_match_length = sp->match_length; 301 | for (i = 0; i < last_match_length && src < srcend; i++) { 302 | delete_node(sp, s); /* Delete old strings and */ 303 | c = *src++; 304 | sp->text_buf[s] = c; /* read new bytes */ 305 | 306 | /* If the position is near the end of buffer, extend the buffer 307 | * to make string comparison easier. 308 | */ 309 | if (s < F - 1) 310 | sp->text_buf[s + N] = c; 311 | 312 | /* Since this is a ring buffer, increment the position modulo N. */ 313 | s = (s + 1) & (N - 1); 314 | r = (r + 1) & (N - 1); 315 | 316 | /* Register the string in text_buf[r..r+F-1] */ 317 | insert_node(sp, r); 318 | } 319 | while (i++ < last_match_length) { 320 | delete_node(sp, s); 321 | 322 | /* After the end of text, no need to read, */ 323 | s = (s + 1) & (N - 1); 324 | r = (r + 1) & (N - 1); 325 | /* but buffer may not be empty. */ 326 | if (--len) 327 | insert_node(sp, r); 328 | } 329 | } while (len > 0); /* until length of string to be processed is zero */ 330 | 331 | if (code_buf_ptr > 1) { /* Send remaining code. */ 332 | for (i = 0; i < code_buf_ptr; i++) 333 | if (dst < dstend) 334 | *dst++ = code_buf[i]; 335 | else { 336 | free(sp); 337 | lzss_errno = LZSS_NOMEM; 338 | return -1; 339 | } 340 | } 341 | 342 | free(sp); 343 | lzss_errno = LZSS_OK; 344 | return (ssize_t)dst - (ssize_t)dststart; 345 | } else { 346 | lzss_errno = LZSS_INVARG; 347 | return -1; 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /lzss.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2015 Pupyshev Nikita 3 | * All rights reserved 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted providing that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #ifndef __ibootim__lzss__ 28 | #define __ibootim__lzss__ 29 | 30 | #include 31 | #include 32 | 33 | typedef enum { 34 | LZSS_OK, 35 | LZSS_NOMEM, 36 | LZSS_NODATA, 37 | LZSS_INVARG 38 | } lzss_error_t; 39 | 40 | /*! 41 | @function lzss_compress 42 | @abstract Compresses data using LZSS compression algorithm 43 | @param src Data to compress 44 | @param dst Buffer for the compressed data 45 | @param srclen Length of data to compress 46 | @param dstlen Length of the destination buffer 47 | @result Size of compressed data or -1 on failure. 48 | */ 49 | 50 | extern ssize_t lzss_compress(uint8_t *dst, unsigned int dstlen, uint8_t *src, unsigned int srclen); 51 | 52 | /*! 53 | @function lzss_decompress 54 | @abstract Decompresses LZSS compressed data 55 | @param src LZSS compressed data buffer 56 | @param dst Buffer for the decompressed data 57 | @param srclen Length of LZSS compressed data 58 | @param dstlen Length of the destination buffer 59 | @result Size of decompressed data or -1 on failure. 60 | */ 61 | 62 | extern ssize_t lzss_decompress(uint8_t *dst, unsigned int dstlen, uint8_t *src, unsigned int srclen); 63 | 64 | 65 | extern lzss_error_t lzss_errno; 66 | extern const char *lzss_strerror(lzss_error_t error); 67 | 68 | #endif /* defined(__ibootim__lzss__) */ 69 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2015 Pupyshev Nikita 3 | * All rights reserved 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted providing that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 18 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 22 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 23 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include "ibootim.h" 40 | #include "lzss.h" 41 | 42 | const char *license = "\nCopyright 2015 Pupyshev Nikita\n\ 43 | All rights reserved\n\ 44 | \n\ 45 | Redistribution and use in source and binary forms, with or without\n\ 46 | modification, are permitted providing that the following conditions\n\ 47 | are met:\n\ 48 | 1. Redistributions of source code must retain the above copyright\n\ 49 | notice, this list of conditions and the following disclaimer.\n\ 50 | 2. Redistributions in binary form must reproduce the above copyright\n\ 51 | notice, this list of conditions and the following disclaimer in the\n\ 52 | documentation and/or other materials provided with the distribution.\n\ 53 | \n\ 54 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n\ 55 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n\ 56 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n\ 57 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n\ 58 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n\ 59 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n\ 60 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n\ 61 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n\ 62 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n\ 63 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n\ 64 | POSSIBILITY OF SUCH DAMAGE.\n"; 65 | 66 | const char *warning_fmt_len_less_m = "WARNING: Content length of image %u is shorter than expected.\n"; 67 | const char *warning_fmt_len_less_o = "WARNING: Content length is shorter than expected.\n"; 68 | const char *warning_fmt_len_more_m = "WARNING: Content length of image %u is longer than expected.\n"; 69 | const char *warning_fmt_len_more_o = "WARNING: Content length is longer than expected.\n"; 70 | const char *error_fmt_create_png_m = "ERROR: Failed to create a PNG file for image %u.\n"; 71 | const char *error_fmt_create_png_o = "ERROR: Failed to create a PNG file.\n"; 72 | const char *error_fmt_image_corrupt_m = "ERROR: Image %u is corrupt.\n"; 73 | const char *error_fmt_image_corrupt_o = "ERROR: iBoot Embedded Image is corrupt.\n"; 74 | 75 | const char *warning_fmt_len_less; 76 | const char *warning_fmt_len_more; 77 | const char *error_fmt_create_png; 78 | const char *error_fmt_image_corrupt; 79 | 80 | void show_license() { 81 | puts(license); 82 | } 83 | 84 | void show_usage() { 85 | puts("Usage: ibootim [-x -y ] [-l -W -H ] [-c | -g] \n" 86 | " or ibootim license\n" 87 | "\n" 88 | " Description:\n" 89 | " Converts PNGs to iBoot Embedded Images and vice versa.\n" 90 | "\n" 91 | " Parameters:\n" 92 | " infile - PNG or iBoot Embedded Image file.\n" 93 | " outfile - path where converted image will be written.\n" 94 | " x/y offset - offsets that will be written into iBoot Embedded\n" 95 | " Image file. Hex numbers are allowed. If input file\n" 96 | " is an iBoot Embedded Image, offsets are ignored.\n" 97 | "\n" 98 | " Non-zero offsets are outputted if the input file is an iBoot\n" 99 | " Embedded Image.\n" 100 | "\n" 101 | " You can safely ignore the content length warning."); 102 | } 103 | 104 | bool file_is_png(const char *file) { 105 | bool result = false; 106 | if (file) { 107 | FILE *f = fopen(file, "rb"); 108 | if (f) { 109 | char magic[8]; 110 | fread(magic, 8, 1, f); 111 | result = png_sig_cmp((png_bytep)magic, 0, 8) == 0; 112 | fclose(f); 113 | } 114 | } 115 | return result; 116 | } 117 | 118 | bool is_decimal_digit(char c) { 119 | return ((c >= '0') && (c <= '9')); 120 | } 121 | 122 | bool is_hex_digit(char c) { 123 | if (is_decimal_digit(c)) return true; 124 | if ((c >= 'a') && (c <= 'f')) return true; 125 | if ((c >= 'A') && (c <= 'F')) return true; 126 | return false; 127 | } 128 | 129 | bool string_is_number(const char *str) { 130 | bool hex = false; 131 | char c; 132 | unsigned long i = 0; 133 | unsigned long length = strlen(str); 134 | 135 | if (length == 0) return false; 136 | 137 | if (str[0] == '-') i = 1; 138 | else if (memcmp("0x", str, 2) && (length > 2)) { 139 | hex = true; 140 | i = 2; 141 | } 142 | 143 | for (; i < length; i++) { 144 | c = str[i]; 145 | if (hex) { 146 | if (!is_hex_digit(c)) return false; 147 | } else { 148 | if (!is_decimal_digit(c)) return false; 149 | } 150 | } 151 | 152 | return true; 153 | } 154 | 155 | void path_add_index(char *dst, const char *path, unsigned int index) { 156 | const char *extension = strrchr(path, '.'); 157 | if (!extension) extension = ""; 158 | 159 | size_t raw_path_length = strlen(path) - strlen(extension); 160 | char *raw_path = alloca(raw_path_length + 1); 161 | strncpy(raw_path, path, raw_path_length); 162 | 163 | if (extension) sprintf(dst, "%s_%u%s", raw_path, index, extension); 164 | } 165 | 166 | int main(int argc, char * argv[]) { 167 | int16_t x_offset = 0, y_offset = 0; 168 | uint16_t width = 0, height = 0; 169 | const char *input_path, *output_path; 170 | const char *ibootim_path, *png_path; 171 | bool force_grayscale = false; 172 | bool force_argb = false; 173 | struct stat st; 174 | int rc; 175 | 176 | if (argc < 3) { 177 | show_usage(); 178 | return 1; 179 | } 180 | 181 | int opt; 182 | while ((opt = getopt(argc - 2, argv, "x:y:W:H:gch")) > 0) { 183 | switch (opt) { 184 | case 'x': 185 | x_offset = atoi(optarg); 186 | break; 187 | case 'y': 188 | y_offset = atoi(optarg); 189 | break; 190 | case 'W': 191 | width = atoi(optarg); 192 | break; 193 | case 'H': 194 | height = atoi(optarg); 195 | break; 196 | case 'c': 197 | force_argb = true; 198 | break; 199 | case 'g': 200 | force_grayscale = true; 201 | break; 202 | case 'h': 203 | show_usage(); 204 | break; 205 | default: 206 | printf("Unknown option '%c'.\n\n", opt); 207 | show_usage(); 208 | return 1; 209 | } 210 | } 211 | 212 | if (force_argb && force_grayscale) { 213 | puts("-c and -g options can not be specified together.\n"); 214 | show_usage(); 215 | return 1; 216 | } 217 | 218 | input_path = argv[argc - 2]; 219 | output_path = argv[argc - 1]; 220 | 221 | if (stat(input_path, &st) != 0) { 222 | printf("Path '%s' does not exist.\n", input_path); 223 | return 1; 224 | } else if (!S_ISREG(st.st_mode)) { 225 | printf("'%s' is not a regular file.\n", input_path); 226 | return 1; 227 | } 228 | 229 | if (stat(output_path, &st) != 0) { 230 | if (!S_ISREG(st.st_mode)) { 231 | printf("'%s' already exists and is not a regular file.\n", output_path); 232 | return 1; 233 | } 234 | } 235 | 236 | if (file_is_png(input_path)) { 237 | ibootim_path = output_path; 238 | png_path = input_path; 239 | 240 | ibootim *image = NULL; 241 | 242 | rc = ibootim_load_png(png_path, &image); 243 | if (rc != 0) { 244 | printf("ERROR: Failed to load PNG file '%s'.\n", png_path); 245 | return 1; 246 | } 247 | 248 | printf("width=%u, height=%u\n", ibootim_get_width(image), ibootim_get_height(image)); 249 | 250 | ibootim_set_x_offset(image, x_offset); 251 | ibootim_set_y_offset(image, y_offset); 252 | 253 | if (force_argb) { 254 | rc = ibootim_convert_to_colorspace(image, ibootim_color_space_argb); 255 | if (rc != 0) { 256 | puts("[-] Failed to convert image to the requested color space."); 257 | ibootim_close(image); 258 | return 1; 259 | } 260 | } else if (force_grayscale) { 261 | rc = ibootim_convert_to_colorspace(image, ibootim_color_space_grayscale); 262 | if (rc != 0) { 263 | puts("[-] Failed to convert image to the requested color space."); 264 | ibootim_close(image); 265 | return 1; 266 | } 267 | } 268 | 269 | rc = ibootim_write(image, ibootim_path); 270 | ibootim_close(image); 271 | if (rc != 0) { 272 | switch (rc) { 273 | case ENOMEM: 274 | puts("ERROR: Not enough memory."); 275 | break; 276 | case ENOENT: 277 | printf("ERROR: Failed to open '%s' for writing: %s.\n", ibootim_path, strerror(errno)); 278 | break; 279 | case EFAULT: 280 | printf("ERROR: Compression error: %s (code %i).\n", lzss_strerror(lzss_errno), lzss_errno); 281 | break; 282 | default: 283 | printf("ERROR: Unknown error (code %i).\n", rc); 284 | break; 285 | } 286 | 287 | return 1; 288 | } 289 | } else if (file_is_ibootim(input_path)) { 290 | ibootim_path = input_path; 291 | png_path = output_path; 292 | 293 | ibootim *image = NULL; 294 | char *path = alloca(strlen(png_path) + 7); 295 | unsigned int images_count = ibootim_count_images_in_file(ibootim_path, NULL); 296 | 297 | if (images_count == 1) { 298 | warning_fmt_len_less = warning_fmt_len_less_o; 299 | warning_fmt_len_more = warning_fmt_len_more_o; 300 | error_fmt_create_png = error_fmt_create_png_o; 301 | error_fmt_image_corrupt = error_fmt_image_corrupt_o; 302 | } else { 303 | warning_fmt_len_less = warning_fmt_len_less_m; 304 | warning_fmt_len_more = warning_fmt_len_more_m; 305 | error_fmt_create_png = error_fmt_create_png_m; 306 | error_fmt_image_corrupt = error_fmt_image_corrupt_m; 307 | } 308 | 309 | for (unsigned int i = 0; i < images_count; i++) { 310 | if ((rc = ibootim_load_at_index(ibootim_path, &image, i)) != 0) { 311 | switch (rc) { 312 | case ENOMEM: 313 | puts("ERROR: Not enough memory."); 314 | break; 315 | case EFTYPE: 316 | printf(error_fmt_image_corrupt, i); 317 | break; 318 | case ENOENT: 319 | printf("ERROR: Failed to open '%s' for reading: %s.\n", ibootim_path, strerror(errno)); 320 | break; 321 | default: 322 | printf("ERROR: Unknown error (code %i).\n", rc); 323 | break; 324 | } 325 | 326 | return 1; 327 | } 328 | 329 | if (force_argb) { 330 | rc = ibootim_convert_to_colorspace(image, ibootim_color_space_argb); 331 | if (rc != 0) { 332 | puts("[-] Failed to convert image to the requested color space."); 333 | ibootim_close(image); 334 | return 1; 335 | } 336 | } else if (force_grayscale) { 337 | rc = ibootim_convert_to_colorspace(image, ibootim_color_space_grayscale); 338 | if (rc != 0) { 339 | puts("[-] Failed to convert image to the requested color space."); 340 | ibootim_close(image); 341 | return 1; 342 | } 343 | } 344 | 345 | if (i > 0) path_add_index(path, png_path, i); 346 | else strcpy(path, png_path); 347 | 348 | int16_t x_offset = ibootim_get_x_offset(image); 349 | int16_t y_offset = ibootim_get_y_offset(image); 350 | if ((x_offset != 0) || (y_offset != 0)) { 351 | if (images_count > 1) printf("%u:\n", i); 352 | if (x_offset != 0) printf(" X offset = %i\n", x_offset); 353 | if (y_offset != 0) printf(" Y offset = %i\n", y_offset); 354 | } 355 | 356 | /*unsigned int length = ibootim_get_content_length(image); 357 | unsigned int expected_length = ibootim_get_expected_content_size(image); 358 | if (length < expected_length) printf(warning_fmt_len_less, i); 359 | else if (length > expected_length) printf(warning_fmt_len_more, i);*/ 360 | 361 | if (ibootim_write_png(image, path) != 0) 362 | printf(error_fmt_create_png, i); 363 | 364 | ibootim_close(image); 365 | } 366 | } else { 367 | puts("Input file must be a PNG or an iBoot Image File (legacy images \n" 368 | "decoding is not supported yet)."); 369 | return 1; 370 | } 371 | 372 | return 0; 373 | } 374 | --------------------------------------------------------------------------------