| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2008 by William Poetra Yoga Hadisoeseno |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| #include <stdio.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| |
| void usage() |
| { |
| fprintf(stderr, "Usage: HXF2IHFS <hxf_img> <ihfs_img> [ihfs_size]\n"); |
| exit(1); |
| } |
| |
| typedef struct { |
| uint32_t signature; |
| char unknown1[4]; |
| char timestamp[12]; |
| uint32_t length; |
| uint32_t unknown2; |
| uint32_t unknown3; |
| char id[32]; |
| } hxf_header_t; |
| |
| int hxf_sanity(const int hxf_img) |
| { |
| struct stat statbuf; |
| hxf_header_t hxf_hdr; |
| int r, len_name, len_file, pos; |
| |
| printf("Starting sanity check for HXF image...\n"); |
| |
| lseek(hxf_img, 0, SEEK_SET); |
| read(hxf_img, &hxf_hdr, sizeof (hxf_hdr)); |
| |
| printf(" Checking for HXF signature...\n"); |
| if (hxf_hdr.signature != 0x46444157) |
| return 1; |
| |
| printf(" Checking for unknown value 1...\n"); |
| if (strncmp(hxf_hdr.unknown1, "0100", 4)) |
| return 1; |
| |
| printf(" Checking for length...\n"); |
| fstat(hxf_img, &statbuf); |
| if (hxf_hdr.length != statbuf.st_size) |
| return 1; |
| |
| printf(" Checking for unknown value 3...\n"); |
| if (hxf_hdr.unknown3 != 0x00000000) |
| return 1; |
| |
| printf(" Checking for id...\n"); |
| if (memcmp(hxf_hdr.id, "Chinachip PMP firmware V1.0\0\0\0\0\0", 32)) |
| return 1; |
| |
| printf(" Checking for file completeness...\n"); |
| while (1) { |
| r = read(hxf_img, &len_name, 4); |
| if (r != 4) |
| return 1; |
| if (len_name == 0) |
| break; |
| |
| r = lseek(hxf_img, len_name + 1, SEEK_CUR); |
| if (r < 0) |
| return 1; |
| |
| r = read(hxf_img, &len_file, 4); |
| if (r != 4) |
| return 1; |
| if (len_file <= 0) |
| return 1; |
| |
| r = lseek(hxf_img, len_file, SEEK_CUR); |
| if (r < 0) |
| return 1; |
| } |
| pos = lseek(hxf_img, 0, SEEK_CUR); |
| if (pos != statbuf.st_size) |
| return 1; |
| |
| return 0; |
| } |
| |
| typedef struct { |
| uint32_t signature; |
| uint32_t fslen; |
| uint32_t unknown1; |
| uint32_t unknown2; |
| char timestamp[12]; |
| uint32_t numfiles; |
| char zeros[476]; |
| uint32_t marker; |
| } ihfs_header_t; |
| |
| #define MAX_FILES 2048 |
| #define MAX_IHFS_PATH 56 |
| |
| typedef struct { |
| struct { |
| char fullpath[MAX_IHFS_PATH]; |
| uint32_t sector; |
| uint32_t length; |
| } files[MAX_FILES]; |
| } ihfs_file_table_t; |
| |
| #define SECTOR_SIZE 512 |
| |
| #define DEFAULT_IHFS_SIZE 65273856 |
| #define DEADFACE_START 0x02140000 |
| |
| |
| int hxf_ihfs_compatible(int hxf_img) |
| { |
| int n, len_name, len_file; |
| |
| lseek(hxf_img, sizeof (hxf_header_t), SEEK_SET); |
| |
| n = 0; |
| while (1) { |
| read(hxf_img, &len_name, 4); |
| if (len_name == 0) |
| break; |
| if (len_name > MAX_IHFS_PATH) |
| return 1; |
| lseek(hxf_img, len_name + 1, SEEK_CUR); |
| read(hxf_img, &len_file, 4); |
| lseek(hxf_img, len_file, SEEK_CUR); |
| ++n; |
| } |
| |
| if (n > MAX_FILES) |
| return 1; |
| |
| return 0; |
| } |
| |
| void hxf_to_ihfs(int hxf_img, int ihfs_img, int filesize) |
| { |
| char buf[SECTOR_SIZE]; |
| char ff[SECTOR_SIZE]; |
| int i, n, rem; |
| |
| n = filesize / SECTOR_SIZE; |
| rem = filesize % SECTOR_SIZE; |
| |
| for (i = 0; i < n; ++i) { |
| read(hxf_img, buf, SECTOR_SIZE); |
| write(ihfs_img, buf, SECTOR_SIZE); |
| } |
| |
| if (rem > 0) { |
| read(hxf_img, buf, rem); |
| write(ihfs_img, buf, rem); |
| /* pad with 0xff */ |
| memset(ff, 0xff, SECTOR_SIZE-rem); |
| write(ihfs_img, ff, SECTOR_SIZE-rem); |
| } |
| } |
| |
| int main(int argc, char **argv) |
| { |
| struct stat statbuf; |
| int hxf_img; |
| hxf_header_t hxf_hdr; |
| char s[32]; |
| int i, n; |
| |
| int ihfs_img; |
| ihfs_header_t ihfs_hdr; |
| ihfs_file_table_t ihfs_ftbl; |
| int ihfs_len; |
| unsigned char deadface[SECTOR_SIZE]; |
| char ff[SECTOR_SIZE]; |
| |
| int pathlen; |
| char *path; |
| char filetype; |
| int filesize; |
| int pos; |
| |
| /* check the arguments */ |
| if (argc != 3 && argc != 4) |
| usage(); |
| |
| stat(argv[1], &statbuf); |
| if (!S_ISREG(statbuf.st_mode)) |
| usage(); |
| |
| if (argc == 3) { |
| ihfs_len = DEFAULT_IHFS_SIZE; |
| } else { |
| errno = 0; |
| ihfs_len = strtol(argv[3], NULL, 10); |
| if (errno != 0 || ihfs_len < 0 || (ihfs_len % SECTOR_SIZE) != 0) |
| usage(); |
| } |
| |
| /* check the file */ |
| |
| hxf_img = open(argv[1], O_RDONLY); |
| |
| if (hxf_sanity(hxf_img)) { |
| printf("Non-HXF format!\n"); |
| return 1; |
| } else { |
| printf("Sanity check OK\n"); |
| } |
| |
| lseek(hxf_img, 0, SEEK_SET); |
| read(hxf_img, &hxf_hdr, sizeof (hxf_hdr)); |
| |
| printf("HXF info:\n"); |
| |
| printf(" Signature: 0x%08x\n", hxf_hdr.signature); |
| |
| strncpy(s, hxf_hdr.unknown1, 4); |
| s[4] = '\0'; |
| printf(" Unknown1: %s\n", s); |
| |
| strncpy(s, hxf_hdr.timestamp, 12); |
| s[12] = '\0'; |
| printf(" Timestamp: %s\n", s); |
| |
| printf(" File size: %d bytes\n", hxf_hdr.length); |
| printf(" Unknown2: 0x%08x\n", hxf_hdr.unknown2); |
| printf(" Unknown3: 0x%08x\n", hxf_hdr.unknown3); |
| printf(" Identifier: %s\n", hxf_hdr.id); |
| |
| if (hxf_ihfs_compatible(hxf_img)) { |
| printf("This HXF image can't be converted into IHFS\n"); |
| return 1; |
| } else { |
| printf("HXF can be converted into IHFS\n"); |
| } |
| |
| /* initialize IHFS structures */ |
| ihfs_hdr.signature = 0x49484653; |
| ihfs_hdr.fslen = ihfs_len / SECTOR_SIZE; |
| ihfs_hdr.unknown1 = 0x00000004; |
| ihfs_hdr.unknown2 = 0xfffff000; |
| memcpy(ihfs_hdr.timestamp, hxf_hdr.timestamp, 12); |
| ihfs_hdr.numfiles = 0; |
| memset(ihfs_hdr.zeros, 0, 476); |
| ihfs_hdr.marker = 0x55aa55aa; |
| |
| memset(&ihfs_ftbl, 0, sizeof (ihfs_ftbl)); |
| |
| /* start converting */ |
| lseek(hxf_img, sizeof (hxf_header_t), SEEK_SET); |
| |
| ihfs_img = open(argv[2], O_WRONLY|O_CREAT|O_TRUNC, 0644); |
| lseek(ihfs_img, sizeof (ihfs_header_t) + 3 * SECTOR_SIZE + sizeof (ihfs_file_table_t), SEEK_SET); |
| |
| i = 0; |
| while (lseek(hxf_img, 0, SEEK_CUR) < hxf_hdr.length) { |
| read(hxf_img, &pathlen, 4); |
| if (pathlen == 0) |
| break; |
| |
| path = malloc(pathlen + 1); |
| read(hxf_img, path, pathlen); |
| path[pathlen] = '\0'; |
| read(hxf_img, &filetype, 1); |
| read(hxf_img, &filesize, 4); |
| |
| /* update the file table and copy the data */ |
| strncpy(ihfs_ftbl.files[i].fullpath, path, MAX_IHFS_PATH); |
| ihfs_ftbl.files[i].sector = lseek(ihfs_img, 0, SEEK_CUR) / 512; |
| ihfs_ftbl.files[i].length = filesize; |
| |
| hxf_to_ihfs(hxf_img, ihfs_img, filesize); |
| |
| free(path); |
| ++i; |
| } |
| ihfs_hdr.numfiles = i; |
| |
| /* finalize the ihfs image */ |
| |
| pos = lseek(ihfs_img, 0, SEEK_CUR); |
| if ((pos % SECTOR_SIZE) != 0) { |
| printf("Something wrong happened during IHFS image creation at %d\n", pos); |
| return 1; |
| } |
| if (pos < ihfs_len && pos < DEADFACE_START) { |
| memset(ff, 0xff, SECTOR_SIZE); |
| n = (DEADFACE_START - pos) / SECTOR_SIZE; |
| for (i = 0; i < n; ++i) |
| write(ihfs_img, ff, SECTOR_SIZE); |
| pos = DEADFACE_START; |
| } |
| if (pos < ihfs_len) { |
| for (i = 0; i < SECTOR_SIZE; i += 4) { |
| deadface[i] = 0xde; |
| deadface[i+1] = 0xad; |
| deadface[i+2] = 0xfa; |
| deadface[i+3] = 0xce; |
| } |
| n = (ihfs_len - pos) / SECTOR_SIZE; |
| for (i = 0; i < n; ++i) |
| write(ihfs_img, deadface, SECTOR_SIZE); |
| } |
| |
| lseek(ihfs_img, 0, SEEK_SET); |
| write(ihfs_img, &ihfs_hdr, sizeof (ihfs_hdr)); |
| memset(ff, 0xff, SECTOR_SIZE); |
| write(ihfs_img, ff, SECTOR_SIZE); |
| write(ihfs_img, ff, SECTOR_SIZE); |
| write(ihfs_img, ff, SECTOR_SIZE); |
| write(ihfs_img, &ihfs_ftbl, sizeof (ihfs_ftbl)); |
| |
| /* cleanup */ |
| close(hxf_img); |
| close(ihfs_img); |
| |
| printf("Done!\n"); |
| |
| return 0; |
| } |