Last active
December 23, 2018 14:44
-
-
Save shinyquagsire23/5ac38487b4c8f9252e78e0275814c90b to your computer and use it in GitHub Desktop.
iPod 'Photo Database' extraction
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <uchar.h> | |
#include <png.h> | |
typedef struct section | |
{ | |
uint32_t magic; | |
uint32_t size; | |
} section; | |
typedef struct mhfd_struct | |
{ | |
uint32_t magic; | |
uint32_t size; | |
uint64_t file_size; | |
uint32_t unk1; | |
uint32_t unk2; | |
uint64_t unk3; | |
uint8_t unk5[0x10]; | |
uint32_t unk6; | |
uint64_t unk7; | |
uint64_t unk8; | |
uint8_t unk9[0x40]; | |
} mhfd_struct; | |
typedef struct mhsd_struct | |
{ | |
} mhsd_struct; | |
typedef struct mhod_struct | |
{ | |
uint32_t magic; | |
uint32_t header_size; | |
uint32_t section_size; | |
uint32_t unk1; | |
uint32_t unk2; | |
uint32_t unk3; | |
} mhod_struct; | |
typedef struct mhni_struct | |
{ | |
uint32_t magic; | |
uint32_t header_size; | |
uint32_t section_size; | |
uint32_t unk1; | |
uint32_t type; | |
uint32_t offset; | |
uint32_t size; | |
uint32_t meta; | |
uint16_t height; | |
uint16_t width; | |
uint32_t unk7; | |
uint32_t unk8; | |
} mhni_struct; | |
typedef struct path_struct | |
{ | |
uint32_t path_size; | |
uint32_t bytes_per_char; | |
uint32_t unk; | |
char16_t path[]; | |
} path_struct; | |
typedef struct bgr565 | |
{ | |
uint16_t b : 5; | |
uint16_t g : 6; | |
uint16_t r : 5; | |
} bgr565; | |
#define MHNI_IMAGE 0x445 | |
#define MHNI_THUMB 0x444 | |
#define MHNI_ROT 0x00400000 | |
char* folder_path; | |
// stolen from http://www.labbookpages.co.uk/software/imgProc/files/libPNG/makePNG.c | |
int writeImage(char* filename, int width, int height, bgr565 *buffer) | |
{ | |
int code = 0; | |
FILE *fp = NULL; | |
png_structp png_ptr = NULL; | |
png_infop info_ptr = NULL; | |
png_bytep img = NULL; | |
// Open file for writing (binary mode) | |
fp = fopen(filename, "wb"); | |
if (fp == NULL) { | |
fprintf(stderr, "Could not open file %s for writing\n", filename); | |
code = 1; | |
goto finalise; | |
} | |
// Initialize write structure | |
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); | |
if (png_ptr == NULL) { | |
fprintf(stderr, "Could not allocate write struct\n"); | |
code = 1; | |
goto finalise; | |
} | |
// Initialize info structure | |
info_ptr = png_create_info_struct(png_ptr); | |
if (info_ptr == NULL) { | |
fprintf(stderr, "Could not allocate info struct\n"); | |
code = 1; | |
goto finalise; | |
} | |
// Setup Exception handling | |
if (setjmp(png_jmpbuf(png_ptr))) { | |
fprintf(stderr, "Error during png creation\n"); | |
code = 1; | |
goto finalise; | |
} | |
png_init_io(png_ptr, fp); | |
// Write header (8 bit colour depth) | |
png_set_IHDR(png_ptr, info_ptr, width, height, | |
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, | |
PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | |
png_write_info(png_ptr, info_ptr); | |
// Allocate memory for one row (3 bytes per pixel - RGB) | |
img = (png_bytep) malloc(3 * width * sizeof(png_byte)); | |
for (int j = 0; j < height; j++) | |
{ | |
for (int i = 0; i < width; i++) | |
{ | |
img[(i * 3) + 0] = buffer[(j * width) + i].r << 3; | |
img[(i * 3) + 1] = buffer[(j * width) + i].g << 2; | |
img[(i * 3) + 2] = buffer[(j * width) + i].b << 3; | |
} | |
png_write_row(png_ptr, img); | |
} | |
// End write | |
png_write_end(png_ptr, NULL); | |
finalise: | |
if (fp != NULL) fclose(fp); | |
if (info_ptr != NULL) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); | |
if (png_ptr != NULL) png_destroy_write_struct(&png_ptr, (png_infopp)NULL); | |
if (img != NULL) free(img); | |
return code; | |
} | |
void mhod_process(void* data) | |
{ | |
mhod_struct* header = (mhod_struct*)data; | |
if (header->unk1 != 2) return; | |
mhni_struct* mhni = (mhni_struct*)((void*)&header[0] + header->header_size); | |
mhod_struct* info = (mhod_struct*)((void*)&mhni[0] + mhni->header_size); | |
path_struct* path = (path_struct*)((void*)&info[0] + info->header_size); | |
char mbstring[0x400]; | |
for (int i = 0; i < path->path_size / 2; i++) | |
{ | |
mbstring[i] = (char)path->path[i]; | |
if (mbstring[i] == ':') | |
mbstring[i] = '/'; | |
} | |
mbstring[path->path_size / path->bytes_per_char] = 0; | |
char file_path[0x400]; | |
char out_path[0x400]; | |
strcpy(file_path, folder_path); | |
strcat(file_path, mbstring); | |
uint64_t offset; | |
int width, height; | |
offset = mhni->offset; | |
if (mhni->meta & MHNI_ROT) | |
{ | |
width = mhni->height; | |
height = mhni->width; | |
} | |
else | |
{ | |
width = mhni->width; | |
height = mhni->height; | |
} | |
/*if (strcmp(file_path, "/run/media/maxamillion/external/ipod/Thumbs/F1093_2.ithmb")) | |
return; | |
if (offset != 0x13e10000) return;*/ | |
if (mhni->type == MHNI_IMAGE) | |
{ | |
FILE* f = fopen(file_path, "rb"); | |
if (f) | |
{ | |
void* img = malloc(mhni->size); | |
fseeko64(f, offset, SEEK_SET); | |
size_t read = fread(img, 1, mhni->size, f); | |
fclose(f); | |
if (read != mhni->size) | |
{ | |
printf("Bad read! off %llx got %zx exp %zx %p\n", offset, read, mhni->size, img); | |
free(img); | |
return; | |
} | |
/*snprintf(out_path, 0x400, "%s/out%s_%016llx.bin", folder_path, mbstring, offset); | |
FILE* out = fopen(out_path, "wb"); | |
fwrite(img, mhni->size, 1, out); | |
fclose(out);*/ | |
snprintf(out_path, 0x400, "%s/out%s_%016llx_%08x.png", folder_path, mbstring, offset, mhni->size); | |
writeImage(out_path, width, height, img); | |
free(img); | |
} | |
} | |
printf("%s %08x type %08x off %016llx size %08x meta %08x %ux%u %08x %08x\n", file_path, mhni->unk1, mhni->type, offset, mhni->size, mhni->meta, width, height, mhni->unk7, mhni->unk8); | |
} | |
int main(int argc, char** argv) | |
{ | |
if (argc < 2) | |
{ | |
printf("Usage: %s </path/to/Photos/>\n", argv[0]); | |
return -1; | |
} | |
folder_path = argv[1]; | |
char db_path[0x200]; | |
snprintf(db_path, 0x200, "%s/Photo Database", argv[1]); | |
FILE* db = fopen(db_path, "rb"); | |
if (!db) | |
{ | |
printf("Failed to open %s!\n", db_path); | |
return -1; | |
} | |
while (1) | |
{ | |
section s; | |
fread(&s, sizeof(s), 1, db); | |
char magic_str[5]; | |
memcpy(magic_str, &s.magic, 4); | |
//printf("section: %04s (%08x) size %08x\n", magic_str, s.magic, s.size); | |
fseek(db, -sizeof(s), SEEK_CUR); | |
size_t section_size = s.size; | |
if (!strcmp(magic_str, "mhod")) | |
{ | |
mhod_struct mhod; | |
fread(&mhod, sizeof(mhod), 1, db); | |
fseek(db, -sizeof(mhod), SEEK_CUR); | |
section_size = mhod.section_size; | |
//printf("section %x\n", section_size); | |
} | |
void* data = malloc(section_size); | |
size_t read = fread(data, section_size, 1, db); | |
if (!strcmp(magic_str, "mhod")) | |
{ | |
mhod_process(data); | |
} | |
free(data); | |
if (!read) break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment