aboutsummaryrefslogtreecommitdiffstats
path: root/image-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'image-parser.c')
-rw-r--r--image-parser.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/image-parser.c b/image-parser.c
new file mode 100644
index 0000000..cb385cc
--- /dev/null
+++ b/image-parser.c
@@ -0,0 +1,208 @@
+/* Copyright 2013 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#define _BSD_SOURCE
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <endian.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#define MAGIC 0xFEEDBABE
+
+struct Header { /* Big Endian */
+ uint32_t magic; /* 0xFEEDBABE */
+ uint32_t len; /* Length of file excluding header */
+ uint32_t checksum; /* 32-bit sum of all bytes in file and header, excluding checksum */
+ uint32_t counter; /* Unknown */
+ uint32_t start_offset; /* Unknown */
+ char name[0x80]; /* File name */
+} __attribute__((packed));
+
+struct File {
+ struct Header header;
+ uint8_t *data;
+ uint32_t decompressed_size;
+ uint8_t *decompressed_data;
+};
+
+uint8_t* decompress_data(uint8_t *data, uint32_t compressed_length, uint32_t decompressed_length)
+{
+ uint8_t *decompressed_data;
+ z_stream strm;
+
+ decompressed_data = malloc(decompressed_length);
+ if (!decompressed_data)
+ return NULL;
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = compressed_length;
+ strm.next_in = data;
+ strm.avail_out = decompressed_length;
+ strm.next_out = decompressed_data;
+
+ if (inflateInit(&strm) != Z_OK) {
+ fprintf(stderr, "inflateInit: %s\n", strm.msg);
+ goto error;
+ }
+ if (inflate(&strm, Z_SYNC_FLUSH) != Z_STREAM_END) {
+ fprintf(stderr, "inflate: %s\n", strm.msg);
+ goto error;
+ }
+
+ goto out;
+error:
+ free(decompressed_data);
+ decompressed_data = NULL;
+out:
+ inflateEnd(&strm);
+ return decompressed_data;
+}
+
+void free_file(struct File *file)
+{
+ if (file->decompressed_data)
+ free(file->decompressed_data);
+}
+
+uint32_t checksum(uint8_t *data, uint32_t length)
+{
+ uint32_t sum = 0;
+
+ while (length--) {
+ /* The checksum does not include itself, so we skip that field. */
+ if (length >= offsetof(struct Header, checksum) && length < offsetof(struct Header, counter))
+ continue;
+ sum += data[length];
+ }
+
+ return sum;
+}
+
+bool read_file(uint8_t *data, struct File *file)
+{
+ memcpy(&file->header, data, sizeof(file->header));
+ file->header.magic = be32toh(file->header.magic);
+ file->header.len = be32toh(file->header.len);
+ file->header.checksum = be32toh(file->header.checksum);
+ file->header.counter = be32toh(file->header.counter);
+ file->header.start_offset = be32toh(file->header.start_offset);
+ file->header.name[0x7f] = '\0';
+
+ file->data = data + sizeof(file->header);
+ file->decompressed_data = NULL;
+ file->decompressed_size = 0;
+ if (!strcmp(file->header.name, "rg_conf")) {
+ /* When the file is gzipped, the length of the decompressed file is
+ * the first four bytes in big endian. */
+ file->decompressed_size = be32toh(*((uint32_t *)file->data));
+ file->decompressed_data = decompress_data(file->data + sizeof(uint32_t), file->header.len - sizeof(uint32_t), file->decompressed_size);
+ if (!file->decompressed_data)
+ file->decompressed_size = 0;
+ }
+
+ return file->header.magic == MAGIC && file->header.checksum == checksum(data, sizeof(file->header) + file->header.len);
+}
+
+void print_file(struct File *file, bool valid)
+{
+ printf("==== %s (%s) ====\n", file->header.name, valid ? "Valid Checksum" : "INVALID CHECKSUM");
+ printf("Length: %u bytes\n", file->header.len);
+ if (file->decompressed_size)
+ printf("Decompressed Length: %u bytes\n", file->decompressed_size);
+}
+
+void save_file(struct File *file, bool valid, uint32_t offset)
+{
+ char filename[0x100];
+ char *slash;
+ int fd;
+
+ sprintf(filename, "%#.8x-%s%s", offset, file->header.name, valid ? "" : ".corrupt");
+ for (slash = filename; (slash = strchr(slash, '/')) != NULL; ++slash)
+ *slash = '_';
+
+ printf("Saving to: %s\n", filename);
+
+ fd = creat(filename, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ perror("creat");
+ exit(EXIT_FAILURE);
+ }
+ if (file->decompressed_size) {
+ if (write(fd, file->decompressed_data, file->decompressed_size) < 0) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ if (write(fd, file->data, file->header.len) < 0) {
+ perror("write");
+ exit(EXIT_FAILURE);
+ }
+ }
+ close(fd);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ bool valid;
+ uint8_t *data, *next;
+ struct stat sbuf;
+ uint32_t needle;
+ struct File file;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s image_filename\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ fd = open(argv[1], 0, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return EXIT_FAILURE;
+ }
+
+ if (fstat(fd, &sbuf)) {
+ perror("fstat");
+ return EXIT_FAILURE;
+ }
+
+ data = mmap(NULL, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ perror("mmap");
+ return EXIT_FAILURE;
+ }
+
+ needle = htobe32(MAGIC);
+ next = data;
+ for (;;) {
+ next = memmem(next, sbuf.st_size - (next - data), &needle, sizeof(needle));
+ if (!next)
+ break;
+
+ valid = read_file(next, &file);
+ print_file(&file, valid);
+ save_file(&file, valid, (uint32_t)(next - data));
+ free_file(&file);
+
+ printf("\n");
+
+ ++next;
+ }
+
+ munmap(data, sbuf.st_size);
+ close(fd);
+ return EXIT_SUCCESS;
+
+}