Skip to content

Instantly share code, notes, and snippets.

@shinyquagsire23
Last active December 23, 2018 14:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shinyquagsire23/5ac38487b4c8f9252e78e0275814c90b to your computer and use it in GitHub Desktop.
Save shinyquagsire23/5ac38487b4c8f9252e78e0275814c90b to your computer and use it in GitHub Desktop.
iPod 'Photo Database' extraction
#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