rockbox/utils/atj2137/atjboottool/atjboottool.c
Amaury Pouly ea679de837 atjboottool: put afi and fw code in its own files
Change-Id: I16347ebee0f82d5fdf32f5aa8f955c07fe148eba
2017-09-29 18:22:30 +02:00

260 lines
6.7 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2012 Amaury Pouly
*
* 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 <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/stat.h>
#include "misc.h"
#include "fwu.h"
#include "afi.h"
#include "fw.h"
bool g_debug = false;
char *g_out_prefix = NULL;
char *g_in_file = NULL;
/* [add]: string to add when there is no extension
* [replace]: string to replace extension */
static void build_out_prefix(char *add, char *replace, bool slash)
{
if(g_out_prefix)
return;
/** copy input filename with extra space */
g_out_prefix = malloc(strlen(g_in_file) + strlen(add) + 16);
strcpy(g_out_prefix, g_in_file);
/** remove extension and add '/' */
char *filename = strrchr(g_out_prefix, '/');
// have p points to the beginning or after the last '/'
filename = (filename == NULL) ? g_out_prefix : filename + 1;
// extension ?
char *dot = strrchr(filename, '.');
if(dot)
{
*dot = 0; // cut at the dot
strcat(dot, replace);
}
else
strcat(filename, add); // add extra string
if(slash)
{
strcat(filename, "/");
/** make sure the directory exists */
mkdir(g_out_prefix, S_IRWXU | S_IRGRP | S_IROTH);
}
}
static int do_fwu(uint8_t *buf, size_t size, enum fwu_mode_t mode)
{
int ret = fwu_decrypt(buf, &size, mode);
if(ret != 0)
return ret;
build_out_prefix(".afi", ".afi", false);
cprintf(GREY, "Descrambling to %s... ", g_out_prefix);
FILE *f = fopen(g_out_prefix, "wb");
if(f)
{
fwrite(buf, size, 1, f);
fclose(f);
cprintf(RED, "Ok\n");
return 0;
}
else
{
color(RED);
perror("Failed");
return 1;
}
}
static int unpack_afi_fw_cb(const char *filename, uint8_t *buf, size_t size)
{
char *name = malloc(strlen(g_out_prefix) + strlen(filename) + 16);
sprintf(name, "%s%s", g_out_prefix, filename);
cprintf(GREY, "Unpacking to %s... ", name);
FILE *f = fopen(name, "wb");
if(f)
{
fwrite(buf, size, 1, f);
fclose(f);
cprintf(RED, "Ok\n");
return 0;
}
else
{
color(RED);
perror("Failed");
return 1;
}
}
static int do_afi(uint8_t *buf, size_t size)
{
build_out_prefix(".fw", "", true);
return afi_unpack(buf, size, &unpack_afi_fw_cb);
}
static int do_fw(uint8_t *buf, size_t size)
{
build_out_prefix(".unpack", "", true);
return fw_unpack(buf, size, &unpack_afi_fw_cb);
}
static void usage(void)
{
printf("Usage: atjboottool [options] firmware\n");
printf("Options:\n");
printf(" -o <path> Set output file or output prefix\n");
printf(" -h/--help Display this message\n");
printf(" -d/--debug Display debug messages\n");
printf(" -c/--no-color Disable color output\n");
printf(" --fwu Unpack a FWU firmware file\n");
printf(" --afi Unpack a AFI archive file\n");
printf(" --fw Unpack a FW archive file\n");
printf(" --atj2127 Force ATJ2127 decryption mode\n");
printf("The default is to try to guess the format.\n");
printf("If several formats are specified, all are tried.\n");
printf("If no output prefix is specified, a default one is picked.\n");
exit(1);
}
int main(int argc, char **argv)
{
bool try_fwu = false;
bool try_afi = false;
bool try_fw = false;
enum fwu_mode_t fwu_mode = FWU_AUTO;
while(1)
{
static struct option long_options[] =
{
{"help", no_argument, 0, 'h'},
{"debug", no_argument, 0, 'd'},
{"no-color", no_argument, 0, 'c'},
{"fwu", no_argument, 0, 'u'},
{"afi", no_argument, 0, 'a'},
{"fw", no_argument, 0, 'w'},
{"atj2127", no_argument, 0, '2'},
{0, 0, 0, 0}
};
int c = getopt_long(argc, argv, "hdco:a2", long_options, NULL);
if(c == -1)
break;
switch(c)
{
case -1:
break;
case 'c':
enable_color(false);
break;
case 'd':
g_debug = true;
break;
break;
case 'h':
usage();
break;
case 'o':
g_out_prefix = optarg;
break;
case 'a':
try_afi = true;
break;
case 'u':
try_fwu = true;
break;
case 'w':
try_fw = true;
break;
case '2':
fwu_mode = FWU_ATJ2127;
break;
default:
abort();
}
}
if(argc - optind != 1)
{
usage();
return 1;
}
g_in_file = argv[optind];
FILE *fin = fopen(g_in_file, "r");
if(fin == NULL)
{
perror("Cannot open boot file");
return 1;
}
fseek(fin, 0, SEEK_END);
long size = ftell(fin);
fseek(fin, 0, SEEK_SET);
void *buf = malloc(size);
if(buf == NULL)
{
perror("Cannot allocate memory");
return 1;
}
if(fread(buf, size, 1, fin) != 1)
{
perror("Cannot read file");
return 1;
}
fclose(fin);
int ret = -99;
if(try_fwu || fwu_check(buf, size))
ret = do_fwu(buf, size, fwu_mode);
else if(try_afi || afi_check(buf, size))
ret = do_afi(buf, size);
else if(try_fw || fw_check(buf, size))
ret = do_fw(buf, size);
else
{
cprintf(GREY, "No valid format found\n");
ret = 1;
}
if(ret != 0)
{
cprintf(GREY, "Error: %d", ret);
printf("\n");
ret = 2;
}
free(buf);
color(OFF);
return ret;
}