diff --git a/lfpsplitter.c b/lfpsplitter.c index 8ca3baa..08f2879 100644 --- a/lfpsplitter.c +++ b/lfpsplitter.c @@ -1,12 +1,12 @@ /* lfpsplitter - Splits the .lfp files generated by Lytro's desktop app into * .jpg, .json, and .txt files in a hopefully platform independent way - * + * * Copyright (c) 2011, Nirav Patel - * + * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. - * + * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR @@ -22,12 +22,8 @@ #include #include #include -#ifdef _WIN32 -#include -typedef unsigned int uint32_t; -#else -#include -#endif +#include + #include "lfpsplitter.h" #define SHA1_LENGTH 45 @@ -41,6 +37,26 @@ typedef unsigned int uint32_t; #define snprintf _snprintf #endif +int is_little_endian() +{ + //This implementation was found in http://stackoverflow.com/a/14791372/1577940 + uint32_t one = 0x00000001; + uint8_t is_little_endian = *(uint8_t *)&one; + return is_little_endian; +} + +uint32_t uint32_from_big_endian(uint32_t dword) +{ + //This implementation was found in http://stackoverflow.com/a/14791372/1577940 + if (!is_little_endian()) return dword; + + //Picking all bytes and putting them in reverse order + return (((dword >> 0) & 0xff) << 24) + | (((dword >> 8) & 0xff) << 16) + | (((dword >> 16) & 0xff) << 8) + | (((dword >> 24) & 0xff) << 0); +} + // TODO: read directly from the file instead of copying to a string // since camera backup files can be in the hundreds of megabytes static lfp_file_p lfp_create(const char *filename) @@ -50,19 +66,19 @@ static lfp_file_p lfp_create(const char *filename) if (!lfp) { return NULL; } - + if (!(fp = fopen(filename, "rb"))) { return NULL; } - + fseek(fp, 0, SEEK_END); lfp->len = ftell(fp); fseek(fp, 0, SEEK_SET); lfp->data = (char*)malloc(lfp->len); - + lfp->len = fread(lfp->data, 1, lfp->len, fp); fclose(fp); - + return lfp; } @@ -79,49 +95,49 @@ static lfp_section_p parse_section(char **lfp, int *in_len) int len = *in_len; lfp_section_p section = calloc(1,sizeof(lfp_section_t)); if (!section) return NULL; - + // There may be some null region between sections while (*ptr == '\0' && len) { ptr++; len--; } - + if (len <= MAGIC_LENGTH+sizeof(uint32_t)+SHA1_LENGTH+BLANK_LENGTH) { free(section); return NULL; } - + // Copy the magic type memcpy(section->typecode, ptr, 4); // Move past the magic and the first 4 bytes of 0s ptr += MAGIC_LENGTH; len -= MAGIC_LENGTH; - + // the length is stored as a big endian unsigned 32 bit int - section->len = ntohl(*(uint32_t *)ptr); + section->len = uint32_from_big_endian(*(uint32_t *)ptr); ptr += sizeof(uint32_t); len -= sizeof(uint32_t); - + // copy and move past the sha1 string and the 35 byte empty space memcpy(section->sha1, ptr, SHA1_LENGTH); ptr += SHA1_LENGTH+BLANK_LENGTH; len -= SHA1_LENGTH+BLANK_LENGTH; - + // make sure there exists as much data as the header claims if (len < section->len) { free(section); return NULL; } - + // just directly reference the existing buffer section->data = ptr; - + ptr += section->len; len -= section->len; - + *lfp = ptr; *in_len = len; - + return section; } @@ -132,10 +148,10 @@ static char *depth_string(const char *data, int *datalen, int len) char *depth = (char*)malloc(filelen); char *start = depth; int i = 0; - + if (!depth) return NULL; depth[0] = '\0'; - + for (i = 0; i < len/4; i++) { char val[20]; int vallen = 0; @@ -144,9 +160,9 @@ static char *depth_string(const char *data, int *datalen, int len) strncpy(depth, val, vallen); depth += vallen; } - + *datalen = depth-start; - + return start; } @@ -156,38 +172,38 @@ static char *converted_image(const unsigned char *data, int *datalen, int len) const unsigned char *ptr = data; unsigned short *image = (unsigned short*)malloc(filelen*sizeof(short)); unsigned short *start = image; - + if (!image) return NULL; // Turn the 12 bits per pixel packed array into 16 bits per pixel // to make it easier to import into other libraries while (ptr < data+len) { *image++ = (*ptr << 8) | (*(ptr+1) & 0xF0); *image++ = ((*(ptr+1) & 0x0F) << 12) | (*(ptr+2) << 4); - + ptr += 3; } - + *datalen = filelen; - + return (char *)start; } static int save_data(const char *data, int len, const char *filename) { FILE *fp; - + if (!(fp = fopen(filename, "wb"))) { fprintf(stderr, "Failed to open %s for writing\n", filename); return 0; } - + if (fwrite(data, 1, len, fp) != len) { fprintf(stderr, "Failed to write %s\n", filename); return 0; } - + fclose(fp); - + return 1; } @@ -204,28 +220,28 @@ static void lfp_identify_section(lfp_file_p lfp, lfp_section_p section) // Move backwards to the corresponding name while (quotecount < 3 && (ptr-- > lfp->table->data)) if (*ptr == '"') quotecount++; - + // Read the name if we can if ((!quotecount == 3) || (sscanf(ptr, "\"%255[^\"]\"", section->name) != 1)) strcpy(section->name, "unknown"); } - + // Test for Lytro Version 1 depth map if (section->len == LUT_DIM_V1 * LUT_DIM_V1 * 4) { section->type = LFP_DEPTH_LUT; strcpy(section->name, "depth"); return; } - + // Test for Lytro Version 2 LUT (depth or confidence) map if (section->len == LUT_DIM_V2 * LUT_DIM_V2 * 4) { section->type = LFP_LUT; strcpy(section->name, "lut"); return; } - + // Check for the magic bytes to see if its a jpg - if ((section->len > sizeof(jpeg)) && + if ((section->len > sizeof(jpeg)) && (memcmp(section->data, jpeg, sizeof(jpeg)) == 0)) { section->type = LFP_JPEG; strcpy(section->name, "image"); @@ -237,7 +253,7 @@ static void lfp_identify_section(lfp_file_p lfp, lfp_section_p section) section->type = LFP_BLOCK_OF_IMAGES; return; } - + // Assume anything else that isn't called imageRef is plain text json if (strcmp(section->name, "imageRef")) section->type = LFP_JSON; @@ -247,23 +263,23 @@ static void lfp_parse_sections(lfp_file_p lfp) { char *ptr = lfp->data; int len = lfp->len; - + // Move past the first header ptr += MAGIC_LENGTH+sizeof(uint32_t); len -= MAGIC_LENGTH+sizeof(uint32_t); - + // Assume the first section is always the table of contents lfp->table = parse_section(&ptr, &len); lfp->table->type = LFP_JSON; lfp->table->name = "table"; - + lfp_section_p cur_section = NULL; while (len > 0) { lfp_section_p new_section = parse_section(&ptr, &len); if (!new_section) break; - + lfp_identify_section(lfp, new_section); - + if (!lfp->sections) lfp->sections = new_section; else if (cur_section) cur_section->next = new_section; cur_section = new_section; @@ -277,12 +293,12 @@ static void lfp_save_sections(lfp_file_p lfp) int jpeg = 0, raw = 0, text = 0, image_block = 0, lut = 0; char *buf; int buflen = 0; - + // Save the plaintext json metadata snprintf(name, STRING_LENGTH, "%s_%s.json", lfp->filename, lfp->table->name); if (save_data(lfp->table->data, lfp->table->len, name)) printf("Saved %s\n", name); - + while (section != NULL) { switch (section->type) { case LFP_RAW_IMAGE: @@ -294,13 +310,13 @@ static void lfp_save_sections(lfp_file_p lfp) free(buf); } break; - + case LFP_JSON: snprintf(name, STRING_LENGTH, "%s_%s%d.json", lfp->filename, section->name, text++); if (save_data(section->data, section->len, name)) printf("Saved %s\n", name); break; - + case LFP_DEPTH_LUT: // Parse the depth lookup table and save as plaintext buf = depth_string(section->data, &buflen, section->len); @@ -328,7 +344,7 @@ static void lfp_save_sections(lfp_file_p lfp) free(buf); } break; - + case LFP_JPEG: snprintf(name, STRING_LENGTH, "%s_%.2d.jpg", lfp->filename, jpeg++); if (save_data(section->data, section->len, name)) @@ -342,7 +358,7 @@ static void lfp_save_sections(lfp_file_p lfp) printf("Saved %s\n", name); break; } - + section = section->next; } } @@ -351,7 +367,7 @@ static void lfp_close(lfp_file_p lfp) { if (lfp) { lfp_section_p section = lfp->sections; - + if (lfp->data) free(lfp->data); if (lfp->filename) free(lfp->filename); while (section) { @@ -373,19 +389,19 @@ int main(int argc, char *argv[]) fprintf(stderr, "Usage: lfpsplitter file.lfp\n"); return 1; } - + if (!(lfp = lfp_create(argv[1]))) { fprintf(stderr, "Failed to open file %s\n", argv[1]); lfp_close(lfp); return 1; } - + if (!lfp_file_check(lfp)) { fprintf(stderr, "File %s does not look like an lfp\n", argv[1]); lfp_close(lfp); return 1; } - + // save the first part of the filename to name the jpgs later if (!(lfp->filename = strdup(argv[1]))) { lfp_close(lfp); @@ -395,9 +411,9 @@ int main(int argc, char *argv[]) if (period) *period = '\0'; lfp_parse_sections(lfp); - + lfp_save_sections(lfp); - + lfp_close(lfp); return 0; }