/* ************************************************************************** */ /* _____ _ */ /* parse/picture.c |_ _|__ _ _| |__ ___ _ _ */ /* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */ /* | | (_) | |_| | | | | __/ |_| | */ /* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ /* Last updated: 2016/12/01 13:33:45 |___/ */ /* */ /* ************************************************************************** */ #include #include /** * g3p_deobfuscate: * De-obfuscate the image data. * * @arg buf the buffer. * @arg n the buffer size. */ static void g3p_deobfuscate(uint8_t *buf, size_t n) { while (n--) { int byte = *buf; *buf++ = (byte >> 3) | ((byte & 0x7) << 5); } } /** * g1m_parse_g3p: * Parse a G3P file. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg std the standard header. * @return the error code (0 if ok). */ int g1m_parse_g3p(g1m_t *handle, FILE *stream, struct standard_header *std) { /* get the G3P global header */ DREAD(hd, g3p_subheader) hd.g3p_size = be32toh(hd.g3p_size); /* check magic */ if (memcmp(hd.magic, "CP", 2)) { log_fatal("could not read 'CP'."); return (g1m_error_magic); } /* read the image header */ DREAD(ihd, g3p_imageheader) /* correct endianness */ ihd.df_size = be32toh(ihd.df_size); ihd.width = be16toh(ihd.width); ihd.height = be16toh(ihd.height); ihd.color_depth = be16toh(ihd.color_depth); ihd.data_size = be32toh(ihd.data_size); /* check some little things */ int is_obfuscated = ((std->obfuscated + 8) & 0xff) != ((std->filesize & 0xff00) >> 8); int has_footer = 1; /* might change? */ size_t deflated_image_size = ihd.data_size - has_footer * 0x4; /* log info */ log_info("Width: %" PRIu16 "px, height: %" PRIu16 "px", ihd.width, ihd.height); log_info("Pixel depth: %s", (ihd.color_depth == g3p_color_4bit) ? "4-bit" : "16-bit"); log_info("Deflated image length: %" PRIuSIZE "o", deflated_image_size); log_info("Is obfuscated: %s", is_obfuscated ? "yes" : "no"); /* read image */ uint8_t deflated_image[deflated_image_size]; READ(deflated_image, deflated_image_size) /* read footer and check sum */ if (has_footer) { DREAD(ft, g3p_imagefooter); ft.checksum = be32toh(ft.checksum); uLong adl = adler32(0, deflated_image, deflated_image_size); if (adl != ft.checksum) { log_fatal("Incorrect Adler32 checksum!"); log_fatal("Expected %" PRIu32 ", got %lu", ft.checksum, adl); return (g1m_error_magic); } } /* unobfuscate if required */ if (is_obfuscated) g3p_deobfuscate(deflated_image, deflated_image_size); /* find out length of inflated data */ uLongf inflated_size = 0L; int z_err; if ((z_err = uncompress(Z_NULL, &inflated_size, deflated_image, deflated_image_size)) != Z_MEM_ERROR) { log_fatal("Zlib error: error #%d", z_err); return (g1m_error_magic); } log_info("Inflated image size is %lu", inflated_size); /* allocate space for it, and uncompress */ uint8_t inflated_image[inflated_size]; if ((z_err = uncompress(inflated_image, &inflated_size, deflated_image, deflated_image_size))) { log_fatal("Zlib error: error #%d", z_err); return (g1m_error_magic); } /* allocate picture in handle */ int w = ihd.width, h = ihd.height; handle->width = w; handle->height = h; handle->pixels = alloc_pixels(w, h); if (!handle->pixels) return (g1m_error_alloc); prepare_pixels(handle->pixels, w, h) /* then store it */ switch (ihd.color_depth) { case g3p_color_4bit: g1m_pixels_from_packed4bits(handle->pixels, inflated_image, w, h); default: /* case g3p_color_16bit: */ g1m_pixels_from_16bits(handle->pixels, inflated_image, w, h); } /* no error */ return (0); } /** * g1m_parse_c2p: * Parse Classpad images. * * @arg handle the handle. * @arg stream the stream to parse from. * @return the error code (0 if ok). */ int g1m_parse_c2p(g1m_t *handle, FILE *stream, struct standard_header *std) { (void)std; (void)handle; (void)stream; /* TODO */ log_info("C2P files are not managed yet."); log_info("Size is %" PRIuSIZE " o", sizeof(struct c2p_subheader)); /* no error */ return (0); }