129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen/*
229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * Copyright (C) 2015 The Android Open Source Project
329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *
429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * Licensed under the Apache License, Version 2.0 (the "License");
529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * you may not use this file except in compliance with the License.
629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * You may obtain a copy of the License at
729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *
829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *      http://www.apache.org/licenses/LICENSE-2.0
929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen *
1029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * Unless required by applicable law or agreed to in writing, software
1129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * distributed under the License is distributed on an "AS IS" BASIS,
1229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * See the License for the specific language governing permissions and
1429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen * limitations under the License.
1529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen */
1629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
1729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenextern "C" {
1829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    #include <fec.h>
1929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
2029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
2129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#undef NDEBUG
2229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
2329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <assert.h>
2429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <errno.h>
2529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <getopt.h>
2629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <fcntl.h>
2729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <pthread.h>
2829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <stdbool.h>
2929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <stdlib.h>
3029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include <string.h>
31f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
3266dd09e8e2407082ce93bf0784de641298131912Elliott Hughes#include <android-base/file.h>
3329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen#include "image.h"
3429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
3529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenenum {
3629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    MODE_ENCODE,
3729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    MODE_DECODE,
3829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    MODE_PRINTSIZE,
3929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    MODE_GETECCSTART,
4029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    MODE_GETVERITYSTART
4129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen};
4229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
4329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic void encode_rs(struct image_proc_ctx *ctx)
4429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
4529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    struct image *fcx = ctx->ctx;
4629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    int j;
4729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint8_t data[fcx->rs_n];
4829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint64_t i;
4929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
5029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
5129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        for (j = 0; j < fcx->rs_n; ++j) {
5229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            data[j] = image_get_interleaved_byte(i + j, fcx);
5329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
5429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
5529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        encode_rs_char(ctx->rs, data, &fcx->fec[ctx->fec_pos]);
5629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        ctx->fec_pos += fcx->roots;
5729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
5829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
5929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
6029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic void decode_rs(struct image_proc_ctx *ctx)
6129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
6229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    struct image *fcx = ctx->ctx;
6329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    int j, rv;
6429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint8_t data[fcx->rs_n + fcx->roots];
6529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    uint64_t i;
6629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
6729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    assert(sizeof(data) == FEC_RSM);
6829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
6929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    for (i = ctx->start; i < ctx->end; i += fcx->rs_n) {
7029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        for (j = 0; j < fcx->rs_n; ++j) {
7129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            data[j] = image_get_interleaved_byte(i + j, fcx);
7229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
7329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
7429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        memcpy(&data[fcx->rs_n], &fcx->fec[ctx->fec_pos], fcx->roots);
7529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        rv = decode_rs_char(ctx->rs, data, NULL, 0);
7629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
7729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (rv < 0) {
7829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            FATAL("failed to recover [%" PRIu64 ", %" PRIu64 ")\n",
7929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                i, i + fcx->rs_n);
8029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        } else if (rv > 0) {
8129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            /* copy corrected data to output */
8229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            for (j = 0; j < fcx->rs_n; ++j) {
8329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                image_set_interleaved_byte(i + j, fcx, data[j]);
8429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            }
8529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
8629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx->rv += rv;
8729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
8829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
8929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        ctx->fec_pos += fcx->roots;
9029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
9129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
9229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
9329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic int usage()
9429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
9529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    printf("fec: a tool for encoding and decoding files using RS(255, N).\n"
9629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "\n"
9729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "usage: fec <mode> [ <options> ] [ <data> <fec> [ <output> ] ]\n"
9829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "mode:\n"
9929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -e  --encode                      encode (default)\n"
10029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -d  --decode                      decode\n"
10129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -s, --print-fec-size=<data size>  print FEC size\n"
10229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -E, --get-ecc-start=data          print ECC offset in data\n"
10329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -V, --get-verity-start=data       print verity offset\n"
10429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "options:\n"
10529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -h                                show this help\n"
10629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -v                                enable verbose logging\n"
10729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -r, --roots=<bytes>               number of parity bytes\n"
10829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -j, --threads=<threads>           number of threads to use\n"
10929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -S                                treat data as a sparse file\n"
11061fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen           "encoding options:\n"
11161fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen           "  -p, --padding=<bytes>             add padding after ECC data\n"
11229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "decoding options:\n"
11329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen           "  -i, --inplace                     correct <data> in place\n"
11429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        );
11529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
11629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return 1;
11729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
11829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
11929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic uint64_t parse_arg(const char *arg, const char *name, uint64_t maxval)
12029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
12129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    char* endptr;
12229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    errno = 0;
12329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
12429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    unsigned long long int value = strtoull(arg, &endptr, 0);
12529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
12629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (arg[0] == '\0' || *endptr != '\0' ||
12729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            (errno == ERANGE && value == ULLONG_MAX)) {
12829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("invalid value of %s\n", name);
12929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
13029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (value > maxval) {
13129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("value of roots too large (max. %" PRIu64 ")\n", maxval);
13229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
13329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
13429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return (uint64_t)value;
13529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
13629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
13729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenstatic int print_size(image& ctx)
13829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
13929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    /* output size including header */
14029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    printf("%" PRIu64 "\n", fec_ecc_get_size(ctx.inp_size, ctx.roots));
14129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return 0;
14229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
14329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
144f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanenstatic int get_start(int mode, const std::string& filename)
14529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
14629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    fec::io fh(filename, O_RDONLY, FEC_VERITY_DISABLE);
14729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
14829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!fh) {
14929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to open input\n");
15029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
15129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
15229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (mode == MODE_GETECCSTART) {
15329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        fec_ecc_metadata data;
15429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
15529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (!fh.get_ecc_metadata(data)) {
15629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            FATAL("no ecc data\n");
15729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
15829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
1594b71c1b3ab941a3edb258e55eb25dcf5fc013d49Sami Tolvanen        printf("%" PRIu64 "\n", data.start);
16029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    } else {
16129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        fec_verity_metadata data;
16229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
16329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (!fh.get_verity_metadata(data)) {
16429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            FATAL("no verity data\n");
16529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
16629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
16729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        printf("%" PRIu64 "\n", data.data_size);
16829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
16929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
17029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return 0;
17129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
17229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
173f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanenstatic int encode(image& ctx, const std::vector<std::string>& inp_filenames,
174f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        const std::string& fec_filename)
17529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
17629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx.inplace) {
17729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("invalid parameters: inplace can only used when decoding\n");
17829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
17929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
180cace33a52a3b4f7360219bae1f209ebc5d3fadcbSami Tolvanen    if (!image_load(inp_filenames, &ctx)) {
18129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to read input\n");
18229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
18329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
18429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!image_ecc_new(fec_filename, &ctx)) {
18529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to allocate ecc\n");
18629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
18729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
188f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    INFO("encoding RS(255, %d) to '%s' for input files:\n", ctx.rs_n,
189f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        fec_filename.c_str());
190f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
191f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    size_t n = 1;
192f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
193f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    for (auto fn : inp_filenames) {
194f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        INFO("\t%zu: '%s'\n", n++, fn.c_str());
195f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    }
19629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
19729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx.verbose) {
19829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("\traw fec size: %u\n", ctx.fec_size);
19929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
20029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
20129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
20229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
20329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!image_process(encode_rs, &ctx)) {
20429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to process input\n");
20529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
20629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
20729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!image_ecc_save(&ctx)) {
20829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to write output\n");
20929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
21029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
21129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image_free(&ctx);
21229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return 0;
21329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
21429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
215f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanenstatic int decode(image& ctx, const std::vector<std::string>& inp_filenames,
216f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        const std::string& fec_filename, std::string& out_filename)
21729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
218f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    const std::string& inp_filename = inp_filenames.front();
219f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
22029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx.inplace && ctx.sparse) {
22129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("invalid parameters: inplace cannot be used with sparse "
22229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            "files\n");
22329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
22429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
22561fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen    if (ctx.padding) {
22661fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen        FATAL("invalid parameters: padding is only relevant when encoding\n");
22761fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen    }
22861fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen
22929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!image_ecc_load(fec_filename, &ctx) ||
230cace33a52a3b4f7360219bae1f209ebc5d3fadcbSami Tolvanen            !image_load(inp_filenames, &ctx)) {
23129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to read input\n");
23229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
23329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
23429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx.inplace) {
235f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        INFO("correcting '%s' using RS(255, %d) from '%s'\n",
236f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            inp_filename.c_str(), ctx.rs_n, fec_filename.c_str());
23729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
23829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        out_filename = inp_filename;
23929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    } else {
24029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("decoding '%s' to '%s' using RS(255, %d) from '%s'\n",
241f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            inp_filename.c_str(),
242f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            out_filename.empty() ? out_filename.c_str() : "<none>", ctx.rs_n,
243f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            fec_filename.c_str());
24429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
24529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
24629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx.verbose) {
24729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("\traw fec size: %u\n", ctx.fec_size);
24829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("\tblocks: %" PRIu64 "\n", ctx.blocks);
24929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("\trounds: %" PRIu64 "\n", ctx.rounds);
25029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
25129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
25229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (!image_process(decode_rs, &ctx)) {
25329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to process input\n");
25429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
25529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
25629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    if (ctx.rv) {
25729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("corrected %" PRIu64 " errors\n", ctx.rv);
25829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    } else {
25929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        INFO("no errors found\n");
26029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
26129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
262f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    if (!out_filename.empty() && !image_save(out_filename, &ctx)) {
26329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        FATAL("failed to write output\n");
26429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
26529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
26629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image_free(&ctx);
26729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return 0;
26829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
26929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
27029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanenint main(int argc, char **argv)
27129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen{
272f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    std::string fec_filename;
273f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    std::string out_filename;
274f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    std::vector<std::string> inp_filenames;
27529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    int mode = MODE_ENCODE;
27629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image ctx;
27729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
27829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    image_init(&ctx);
27929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    ctx.roots = FEC_DEFAULT_ROOTS;
28029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
28129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    while (1) {
28229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        const static struct option long_options[] = {
28329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"help", no_argument, 0, 'h'},
28429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"encode", no_argument, 0, 'e'},
28529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"decode", no_argument, 0, 'd'},
28629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"sparse", no_argument, 0, 'S'},
28729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"roots", required_argument, 0, 'r'},
28829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"inplace", no_argument, 0, 'i'},
28929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"threads", required_argument, 0, 'j'},
29029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"print-fec-size", required_argument, 0, 's'},
29129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"get-ecc-start", required_argument, 0, 'E'},
29229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"get-verity-start", required_argument, 0, 'V'},
29361fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen            {"padding", required_argument, 0, 'p'},
29429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {"verbose", no_argument, 0, 'v'},
29529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            {NULL, 0, 0, 0}
29629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        };
29761fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen        int c = getopt_long(argc, argv, "hedSr:ij:s:E:V:p:v", long_options, NULL);
29829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (c < 0) {
29929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
30029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
30129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
30229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        switch (c) {
30329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'h':
30429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            return usage();
30529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'S':
30629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx.sparse = true;
30729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
30829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'e':
30929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            if (mode != MODE_ENCODE) {
31029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                return usage();
31129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            }
31229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
31329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'd':
31429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            if (mode != MODE_ENCODE) {
31529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                return usage();
31629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            }
31729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            mode = MODE_DECODE;
31829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
31929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'r':
32029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx.roots = (int)parse_arg(optarg, "roots", FEC_RSM);
32129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
32229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'i':
32329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx.inplace = true;
32429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
32529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'j':
32629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx.threads = (int)parse_arg(optarg, "threads", IMAGE_MAX_THREADS);
32729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
32829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 's':
32929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            if (mode != MODE_ENCODE) {
33029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                return usage();
33129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            }
33229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            mode = MODE_PRINTSIZE;
33329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx.inp_size = parse_arg(optarg, "print-fec-size", UINT64_MAX);
33429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
33529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'E':
33629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            if (mode != MODE_ENCODE) {
33729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                return usage();
33829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            }
33929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            mode = MODE_GETECCSTART;
340f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            inp_filenames.push_back(optarg);
34129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
34229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'V':
34329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            if (mode != MODE_ENCODE) {
34429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                return usage();
34529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            }
34629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            mode = MODE_GETVERITYSTART;
347f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            inp_filenames.push_back(optarg);
34829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
34961fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen        case 'p':
35061fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen            ctx.padding = (uint32_t)parse_arg(optarg, "padding", UINT32_MAX);
35161fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen            if (ctx.padding % FEC_BLOCKSIZE) {
35261fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen                FATAL("padding must be multiple of %u\n", FEC_BLOCKSIZE);
35361fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen            }
35461fce29c45cd5c2b94b836d68b49d231b0c6d2bbSami Tolvanen            break;
35529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case 'v':
35629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            ctx.verbose = true;
35729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            break;
35829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        case '?':
35929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            return usage();
36029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        default:
36129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            abort();
36229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
36329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
36429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
36529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    argc -= optind;
36629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    argv += optind;
36729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
36829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    assert(ctx.roots > 0 && ctx.roots < FEC_RSM);
36929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
37029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    /* check for input / output parameters */
371f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    if (mode == MODE_ENCODE) {
372f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        /* allow multiple input files */
373f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        for (int i = 0; i < (argc - 1); ++i) {
374f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            inp_filenames.push_back(argv[i]);
375f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        }
376f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
377f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        if (inp_filenames.empty()) {
378f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            return usage();
379f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        }
380f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen
381f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        /* the last one is the output file */
382f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        fec_filename = argv[argc - 1];
383f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen    } else if (mode == MODE_DECODE) {
38429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        if (argc < 2 || argc > 3) {
38529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            return usage();
38629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        } else if (argc == 3) {
387f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen            if (ctx.inplace) {
38829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen                return usage();
38929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            }
39029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen            out_filename = argv[2];
39129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        }
39229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
393f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        inp_filenames.push_back(argv[0]);
39429bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        fec_filename = argv[1];
39529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
39629bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
39729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    switch (mode) {
39829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    case MODE_PRINTSIZE:
39929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        return print_size(ctx);
40029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    case MODE_GETECCSTART:
40129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    case MODE_GETVERITYSTART:
402f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        return get_start(mode, inp_filenames.front());
40329bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    case MODE_ENCODE:
404f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        return encode(ctx, inp_filenames, fec_filename);
40529bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    case MODE_DECODE:
406f35a15d11e79892c06ce303e0b983c132955d261Sami Tolvanen        return decode(ctx, inp_filenames, fec_filename, out_filename);
40729bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    default:
40829bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen        abort();
40929bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    }
41029bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen
41129bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen    return 1;
41229bf737e56e10c2742f1e14fe9f07184d59bbcc0Sami Tolvanen}
413