nwztool/upgtool: add support for compression
To decompress some output file(s), simply pass -z <idx> where idx is the index of the file to decompress, starting from 0. For example upgtool -e NW_WM_FW.UPG -o tmp/ -m nw-wm1a -z 6 -z 7 to decompress files 6 and 7. To compress file, use the same options: upgtool -c NW_WM_FW.UPG -m nw-wm1a -z 2 script.sh md5sum.txt system.img Change-Id: I1ef0b3e02c98e58154f1a959fb1ad70ad2ce6500
This commit is contained in:
parent
1f338f7078
commit
1b5e824405
4 changed files with 134 additions and 31 deletions
|
@ -31,7 +31,7 @@ endif
|
|||
|
||||
CXXFLAGS+=-g -Wall $(DEFINES)
|
||||
CFLAGS+=-g -Wall -std=c99 $(DEFINES)
|
||||
LDLIBS+=-lpthread
|
||||
LDLIBS+=-lpthread -lz
|
||||
|
||||
BINS=upgtool$(EXE_EXT)
|
||||
|
||||
|
|
15
utils/nwztools/upgtools/make_release.sh
Executable file
15
utils/nwztools/upgtools/make_release.sh
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ $# -ne 1 ];
|
||||
then
|
||||
echo "usage: $0 version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ver="$1"
|
||||
echo "version: $ver"
|
||||
|
||||
echo "Building windows version..."
|
||||
make PREFIX=i686-w64-mingw32- EXE_EXT=.exe clean && make PREFIX=i686-w64-mingw32- EXE_EXT=.exe && cp upgtool.exe upgtool-v${ver}.exe
|
||||
echo "Building linux version..."
|
||||
make clean && make && cp upgtool upgtool_64-v${ver}
|
|
@ -319,13 +319,6 @@ void *upg_write_memory(struct upg_file_t *file, const char *key,
|
|||
err_printf(GREY, "A UPG file must have at least one file\n");
|
||||
return NULL;
|
||||
}
|
||||
if(key_len == 16 && file->nr_files == 1)
|
||||
{
|
||||
err_printf(RED, "This will probably not work: the firmware updater for this device expects at least two files in the archive.\n");
|
||||
err_printf(RED, "The first one is a shell script and the second is a MD5 file. You can probably put whatever you want in this file,\n");
|
||||
err_printf(RED, "even make it empty, but it needs to be there.\n");
|
||||
/* let it run just in case */
|
||||
}
|
||||
|
||||
bool is_v2 = false;
|
||||
size_t min_chunk_size, hdr_sz, ent_sz;
|
||||
|
@ -337,7 +330,7 @@ void *upg_write_memory(struct upg_file_t *file, const char *key,
|
|||
}
|
||||
else if(key_len == 16)
|
||||
{
|
||||
min_chunk_size = 16;
|
||||
min_chunk_size = 4096; /* experimentally, V2 UPG files always seem to have 4k sizes */
|
||||
hdr_sz = sizeof(struct upg_header_v2_t);
|
||||
ent_sz = sizeof(struct upg_entry_v2_t);
|
||||
is_v2 = true;
|
||||
|
@ -348,6 +341,15 @@ void *upg_write_memory(struct upg_file_t *file, const char *key,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* V2 wants at least two files, the second of which is supposed to contain MD5 sums */
|
||||
if(is_v2 && file->nr_files == 1)
|
||||
{
|
||||
err_printf(RED, "This will probably not work: the firmware updater for this device expects at least two files in the archive.\n");
|
||||
err_printf(RED, "The first one is a shell script and the second is a MD5 file. You can probably put whatever you want in this file,\n");
|
||||
err_printf(RED, "even make it empty, but it needs to be there.\n");
|
||||
/* let it run just in case */
|
||||
}
|
||||
|
||||
/* compute total size and create buffer */
|
||||
size_t tot_hdr_siz = hdr_sz + file->nr_files * ent_sz;
|
||||
size_t tot_size = sizeof(struct upg_md5_t) + tot_hdr_siz;
|
||||
|
@ -386,17 +388,6 @@ void *upg_write_memory(struct upg_file_t *file, const char *key,
|
|||
cprintf(RED, " %d\n", i);
|
||||
cprintf_field(" Offset: ", "0x%lx\n", offset);
|
||||
cprintf_field(" Size: ", "0x%lx\n", file->files[i].size);
|
||||
if(!is_v2)
|
||||
{
|
||||
entry_v1[i].offset = offset;
|
||||
entry_v1[i].size = file->files[i].size;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry_v2[i].offset = offset;
|
||||
entry_v2[i].size = file->files[i].size;
|
||||
entry_v2[i].pad[0] = entry_v2[i].pad[1] = 0;
|
||||
}
|
||||
/* copy data to buffer, with padding */
|
||||
size_t r_size = ROUND_UP(file->files[i].size, min_chunk_size);
|
||||
void *data_ptr = (uint8_t *)buf + offset;
|
||||
|
@ -413,6 +404,18 @@ void *upg_write_memory(struct upg_file_t *file, const char *key,
|
|||
aes_cbc_enc_set_key_iv((uint8_t *)key, (uint8_t *)g_aes_iv);
|
||||
aes_cbc_enc(data_ptr, r_size, data_ptr);
|
||||
}
|
||||
/* write header */
|
||||
if(!is_v2)
|
||||
{
|
||||
entry_v1[i].offset = offset;
|
||||
entry_v1[i].size = file->files[i].size;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry_v2[i].offset = offset;
|
||||
entry_v2[i].size = r_size; /* the V2 seems to write the padded size here */
|
||||
entry_v2[i].pad[0] = entry_v2[i].pad[1] = 0;
|
||||
}
|
||||
|
||||
offset += r_size;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <ctype.h>
|
||||
#include "misc.h"
|
||||
#include <sys/stat.h>
|
||||
#include <zlib.h>
|
||||
#include "keysig_search.h"
|
||||
#include "upg.h"
|
||||
|
||||
|
@ -46,17 +47,11 @@ static char *g_kas = NULL;
|
|||
static char *g_key = NULL;
|
||||
static char *g_sig = NULL;
|
||||
static int g_nr_threads = 1;
|
||||
#define MAX_NR_FILES 32
|
||||
bool g_compress[MAX_NR_FILES] = {false};
|
||||
|
||||
enum keysig_search_method_t g_keysig_search = KEYSIG_SEARCH_NONE;
|
||||
|
||||
#define let_the_force_flow(x) do { if(!g_force) return x; } while(0)
|
||||
#define continue_the_force(x) if(x) let_the_force_flow(x)
|
||||
|
||||
#define check_field(v_exp, v_have, str_ok, str_bad) \
|
||||
if((v_exp) != (v_have)) \
|
||||
{ cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \
|
||||
else { cprintf(RED, str_ok); }
|
||||
|
||||
#define cprintf(col, ...) do {color(col); printf(__VA_ARGS__); }while(0)
|
||||
|
||||
#define cprintf_field(str1, ...) do{ cprintf(GREEN, str1); cprintf(YELLOW, __VA_ARGS__); }while(0)
|
||||
|
@ -176,7 +171,51 @@ static int do_upg(void *buf, long size)
|
|||
continue;
|
||||
}
|
||||
free(str);
|
||||
if(g_compress[i])
|
||||
{
|
||||
void *buf = file->files[i].data;
|
||||
int size = file->files[i].size;
|
||||
int pos = 0;
|
||||
/* the fwpup tool seems to assume that every block decompresses to less than 4096 bytes,
|
||||
* so I guess the encoder splits the input in chunks */
|
||||
int max_chunk_size = 4096;
|
||||
void *chunk = malloc(max_chunk_size);
|
||||
if(g_debug)
|
||||
cprintf(GREY, "decompressing file %d with chunk size %d...\n", i, max_chunk_size);
|
||||
while(pos + 4 <= size)
|
||||
{
|
||||
int compressed_chunk_size = *(uint32_t *)(buf + pos);
|
||||
if(g_debug)
|
||||
cprintf(GREY, "%d ", compressed_chunk_size);
|
||||
if(compressed_chunk_size < 0)
|
||||
{
|
||||
cprintf(RED, "invalid block size when decompressing, something is wrong\n");
|
||||
break;
|
||||
}
|
||||
if(compressed_chunk_size == 0)
|
||||
break;
|
||||
uLongf chunk_size = max_chunk_size;
|
||||
int zres = uncompress(chunk, &chunk_size, buf + pos + 4, compressed_chunk_size);
|
||||
if(zres == Z_BUF_ERROR)
|
||||
cprintf(RED, "the encoder produced a block greater than %d, I can't handle that\n", max_chunk_size);
|
||||
if(zres == Z_DATA_ERROR)
|
||||
cprintf(RED, "the compressed data seems corrupted\n");
|
||||
if(zres != Z_OK)
|
||||
{
|
||||
cprintf(RED, "the compressed suffered an error %d\n", zres);
|
||||
break;
|
||||
}
|
||||
pos += 4 + compressed_chunk_size;
|
||||
fwrite(chunk, 1, chunk_size, f);
|
||||
}
|
||||
free(chunk);
|
||||
if(g_debug)
|
||||
cprintf(GREY, "done.");
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite(file->files[i].data, 1, file->files[i].size, f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
upg_free(file);
|
||||
|
@ -277,6 +316,39 @@ static int create_upg(int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
fclose(f);
|
||||
if(g_compress[i])
|
||||
{
|
||||
int out_buf_max_sz = size; /* we expect that the output will not take more space */
|
||||
void *out_buf = malloc(out_buf_max_sz);
|
||||
int out_buf_pos = 0, in_buf_pos = 0;
|
||||
int max_chunk_size = 4096; /* the OF encoder/decoder expect that */
|
||||
while(in_buf_pos < size)
|
||||
{
|
||||
int chunk_size = MIN(size - in_buf_pos, max_chunk_size);
|
||||
uLongf dest_len = out_buf_max_sz - out_buf_pos - 4; /* we reserve 4 for the size */
|
||||
int zres = compress(out_buf + out_buf_pos + 4, &dest_len, buf + in_buf_pos, chunk_size);
|
||||
if(zres == Z_BUF_ERROR)
|
||||
{
|
||||
cprintf(RED, "the compresser produced a file greater than its input, I can't handle that\n");
|
||||
return 1;
|
||||
}
|
||||
else if(zres != Z_OK)
|
||||
{
|
||||
cprintf(RED, "the compresser suffered an error %d\n", zres);
|
||||
return 1;
|
||||
}
|
||||
/* write output size in the output buffer */
|
||||
*(uint32_t *)(out_buf + out_buf_pos) = dest_len;
|
||||
out_buf_pos += 4 + dest_len;
|
||||
in_buf_pos += chunk_size;
|
||||
}
|
||||
/* add an extra zero-length chunk */
|
||||
*(uint32_t *)(out_buf + out_buf_pos) = 0;
|
||||
out_buf_pos += 4;
|
||||
upg_append(upg, out_buf, out_buf_pos);
|
||||
free(buf);
|
||||
}
|
||||
else
|
||||
upg_append(upg, buf, size);
|
||||
}
|
||||
|
||||
|
@ -325,6 +397,7 @@ static void usage(void)
|
|||
printf(" -s/--sig <sig>\tForce sig\n");
|
||||
printf(" -e/--extract\t\tExtract a UPG archive\n");
|
||||
printf(" -c/--create\t\tCreate a UPG archive\n");
|
||||
printf(" -z/--compress <idx>\t\t(De)compress file <idx> (starts at 0)\n");
|
||||
printf("keysig search method:\n");
|
||||
for(int i = KEYSIG_SEARCH_FIRST; i < KEYSIG_SEARCH_LAST; i++)
|
||||
printf(" %-10s\t%s\n", keysig_search_desc[i].name, keysig_search_desc[i].comment);
|
||||
|
@ -355,10 +428,11 @@ int main(int argc, char **argv)
|
|||
{"extract", no_argument, 0, 'e'},
|
||||
{"create", no_argument, 0 ,'c'},
|
||||
{"threads", required_argument, 0, 't'},
|
||||
{"compress", required_argument, 0, 'z'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int c = getopt_long(argc, argv, "?dnfo:m:l:a:k:s:ect:", long_options, NULL);
|
||||
int c = getopt_long(argc, argv, "?dnfo:m:l:a:k:s:ect:z:", long_options, NULL);
|
||||
if(c == -1)
|
||||
break;
|
||||
switch(c)
|
||||
|
@ -418,6 +492,17 @@ int main(int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
{
|
||||
int idx = strtol(optarg, NULL, 0);
|
||||
if(idx < 0 || idx >= MAX_NR_FILES)
|
||||
{
|
||||
cprintf(GREY, "Invalid file index\n");
|
||||
return 1;
|
||||
}
|
||||
g_compress[idx] = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue