16e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root/*
26e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * Copyright (C) 2010 The Android Open Source Project
36e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root *
46e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * Licensed under the Apache License, Version 2.0 (the "License");
56e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * you may not use this file except in compliance with the License.
66e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * You may obtain a copy of the License at
76e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root *
86e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root *      http://www.apache.org/licenses/LICENSE-2.0
96e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root *
106e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * Unless required by applicable law or agreed to in writing, software
116e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * distributed under the License is distributed on an "AS IS" BASIS,
126e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * See the License for the specific language governing permissions and
146e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * limitations under the License.
156e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root */
166e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
17b13b9bdad2baf6ad1ec2e56b6b7598fa20f55fc4Mathias Agopian#include <androidfw/ObbFile.h>
186e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root#include <utils/String8.h>
196e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
206e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root#include <getopt.h>
216e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root#include <stdio.h>
226e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root#include <stdlib.h>
233b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root#include <string.h>
246e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
256e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootusing namespace android;
266e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
276e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootstatic const char* gProgName = "obbtool";
286e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootstatic const char* gProgVersion = "1.0";
296e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
306e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootstatic int wantUsage = 0;
316e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootstatic int wantVersion = 0;
326e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
333b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root#define SALT_LEN 8
343b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root
353b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root#define ADD_OPTS "n:v:os:"
366e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootstatic const struct option longopts[] = {
376e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    {"help",       no_argument, &wantUsage,   1},
386e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    {"version",    no_argument, &wantVersion, 1},
396e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
406e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    /* Args for "add" */
416e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    {"name",       required_argument, NULL, 'n'},
426e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    {"version",    required_argument, NULL, 'v'},
4302ca31fbae9f35dd30f79de6927fae11b549391aKenny Root    {"overlay",    optional_argument, NULL, 'o'},
443b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    {"salt",       required_argument, NULL, 's'},
456e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
466e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    {NULL, 0, NULL, '\0'}
476e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root};
486e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
493b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Rootclass PackageInfo {
503b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Rootpublic:
513b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    PackageInfo()
523b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            : packageName(NULL)
533b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            , packageVersion(-1)
543b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            , overlay(false)
553b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            , salted(false)
563b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    {
573b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        memset(&salt, 0, sizeof(salt));
583b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    }
593b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root
606e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    char* packageName;
616e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    int packageVersion;
6202ca31fbae9f35dd30f79de6927fae11b549391aKenny Root    bool overlay;
633b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    bool salted;
643b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    unsigned char salt[SALT_LEN];
656e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root};
666e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
676e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root/*
686e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * Print usage info.
696e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root */
706e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootvoid usage(void)
716e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root{
726e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    fprintf(stderr, "Opaque Binary Blob (OBB) Tool\n\n");
736e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    fprintf(stderr, "Usage:\n");
746e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    fprintf(stderr,
756e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        " %s a[dd] [ OPTIONS ] FILENAME\n"
766e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        "   Adds an OBB signature to the file.\n\n", gProgName);
776e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    fprintf(stderr,
783b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        "   Options:\n"
793b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        "     -n <package name>      sets the OBB package name (required)\n"
803b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        "     -v <OBB version>       sets the OBB version (required)\n"
813b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        "     -o                     sets the OBB overlay flag\n"
823b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        "     -s <8 byte hex salt>   sets the crypto key salt (if encrypted)\n"
833b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        "\n");
843b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    fprintf(stderr,
856e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        " %s r[emove] FILENAME\n"
866e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        "   Removes the OBB signature from the file.\n\n", gProgName);
876e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    fprintf(stderr,
886e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        " %s i[nfo] FILENAME\n"
896e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        "   Prints the OBB signature information of a file.\n\n", gProgName);
906e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root}
916e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
923b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Rootvoid doAdd(const char* filename, struct PackageInfo* info) {
936e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    ObbFile *obb = new ObbFile();
946e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (obb->readFrom(filename)) {
956e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        fprintf(stderr, "ERROR: %s: OBB signature already present\n", filename);
966e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        return;
976e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
986e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
996e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    obb->setPackageName(String8(info->packageName));
1006e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    obb->setVersion(info->packageVersion);
10102ca31fbae9f35dd30f79de6927fae11b549391aKenny Root    obb->setOverlay(info->overlay);
1023b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    if (info->salted) {
1033b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        obb->setSalt(info->salt, SALT_LEN);
1043b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    }
1056e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1066e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (!obb->writeTo(filename)) {
1076e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        fprintf(stderr, "ERROR: %s: couldn't write OBB signature: %s\n",
1086e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root                filename, strerror(errno));
1096e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        return;
1106e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
1116e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1126e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    fprintf(stderr, "OBB signature successfully written\n");
1136e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root}
1146e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1156e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootvoid doRemove(const char* filename) {
1166e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    ObbFile *obb = new ObbFile();
1176e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (!obb->readFrom(filename)) {
1186e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        fprintf(stderr, "ERROR: %s: no OBB signature present\n", filename);
1196e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        return;
1206e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
1216e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1226e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (!obb->removeFrom(filename)) {
1236e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        fprintf(stderr, "ERROR: %s: couldn't remove OBB signature\n", filename);
1246e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        return;
1256e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
1266e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1276e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    fprintf(stderr, "OBB signature successfully removed\n");
1286e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root}
1296e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1306e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootvoid doInfo(const char* filename) {
1316e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    ObbFile *obb = new ObbFile();
1326e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (!obb->readFrom(filename)) {
1336e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        fprintf(stderr, "ERROR: %s: couldn't read OBB signature\n", filename);
1346e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        return;
1356e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
1366e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1376e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    printf("OBB info for '%s':\n", filename);
1386e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    printf("Package name: %s\n", obb->getPackageName().string());
1396e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    printf("     Version: %d\n", obb->getVersion());
14002ca31fbae9f35dd30f79de6927fae11b549391aKenny Root    printf("       Flags: 0x%08x\n", obb->getFlags());
14102ca31fbae9f35dd30f79de6927fae11b549391aKenny Root    printf("     Overlay: %s\n", obb->isOverlay() ? "true" : "false");
1423b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    printf("        Salt: ");
1433b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root
1443b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    size_t saltLen;
1453b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    const unsigned char* salt = obb->getSalt(&saltLen);
1463b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    if (salt != NULL) {
1473b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        for (int i = 0; i < SALT_LEN; i++) {
1483b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            printf("%02x", salt[i]);
1493b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        }
1503b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        printf("\n");
1513b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    } else {
1523b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        printf("<empty>\n");
1533b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    }
1543b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root}
1553b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root
1563b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Rootbool fromHex(char h, unsigned char *b) {
1573b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    if (h >= '0' && h <= '9') {
1583b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        *b = h - '0';
1593b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        return true;
1603b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    } else if (h >= 'a' && h <= 'f') {
1613b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        *b = h - 'a' + 10;
1623b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        return true;
1633b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    } else if (h >= 'A' && h <= 'F') {
1643b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        *b = h - 'A' + 10;
1653b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        return true;
1663b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    }
1673b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    return false;
1683b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root}
1693b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root
1703b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Rootbool hexToByte(char h1, char h2, unsigned char* b) {
1713b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    unsigned char first, second;
1723b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    if (!fromHex(h1, &first)) return false;
1733b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    if (!fromHex(h2, &second)) return false;
1743b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    *b = (first << 4) | second;
1753b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    return true;
1766e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root}
1776e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1786e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root/*
1796e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root * Parse args.
1806e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root */
1816e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootint main(int argc, char* const argv[])
1826e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root{
1836e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    int opt;
1846e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    int option_index = 0;
1853b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root    struct PackageInfo package_info;
1866e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1876e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    int result = 1;    // pessimistically assume an error.
1886e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1896e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (argc < 2) {
1906e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        wantUsage = 1;
1916e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        goto bail;
1926e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
1936e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
1946e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    while ((opt = getopt_long(argc, argv, ADD_OPTS, longopts, &option_index)) != -1) {
1956e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        switch (opt) {
1966e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        case 0:
1976e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            if (longopts[option_index].flag)
1986e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root                break;
1996e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            fprintf(stderr, "'%s' requires an argument\n", longopts[option_index].name);
2006e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            wantUsage = 1;
2016e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            goto bail;
2026e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        case 'n':
2036e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            package_info.packageName = optarg;
2046e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            break;
20502ca31fbae9f35dd30f79de6927fae11b549391aKenny Root        case 'v': {
2063b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            char* end;
2076e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            package_info.packageVersion = strtol(optarg, &end, 10);
2086e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            if (*optarg == '\0' || *end != '\0') {
2096e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root                fprintf(stderr, "ERROR: invalid version; should be integer!\n\n");
2106e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root                wantUsage = 1;
2116e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root                goto bail;
2126e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            }
2136e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            break;
21402ca31fbae9f35dd30f79de6927fae11b549391aKenny Root        }
21502ca31fbae9f35dd30f79de6927fae11b549391aKenny Root        case 'o':
21602ca31fbae9f35dd30f79de6927fae11b549391aKenny Root            package_info.overlay = true;
2176e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            break;
2183b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root        case 's':
2193b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            if (strlen(optarg) != SALT_LEN * 2) {
2203b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                fprintf(stderr, "ERROR: salt must be 8 bytes in hex (e.g., ABCD65031337D00D)\n\n");
2213b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                wantUsage = 1;
2223b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                goto bail;
2233b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            }
2243b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root
2253b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            package_info.salted = true;
2263b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root
2273b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            unsigned char b;
2283b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            for (int i = 0, j = 0; i < SALT_LEN; i++, j+=2) {
2293b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                if (!hexToByte(optarg[j], optarg[j+1], &b)) {
2303b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                    fprintf(stderr, "ERROR: salt must be in hex (e.g., ABCD65031337D00D)\n");
2313b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                    wantUsage = 1;
2323b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                    goto bail;
2333b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                }
2343b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root                package_info.salt[i] = b;
2353b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            }
2363b1abba6bbc895d63da3e82e9b158c01bd12edddKenny Root            break;
2376e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        case '?':
2386e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            wantUsage = 1;
2396e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            goto bail;
2406e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        }
2416e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
2426e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2436e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (wantVersion) {
2446e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        fprintf(stderr, "%s %s\n", gProgName, gProgVersion);
2456e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
2466e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2476e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (wantUsage) {
2486e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        goto bail;
2496e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
2506e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2516e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root#define CHECK_OP(name) \
2526e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (strncmp(op, name, opsize)) { \
2536e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        fprintf(stderr, "ERROR: unknown function '%s'!\n\n", op); \
2546e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        wantUsage = 1; \
2556e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        goto bail; \
2566e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
2576e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2586e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (optind < argc) {
2596e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        const char* op = argv[optind++];
2606e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        const int opsize = strlen(op);
2616e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2626e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        if (optind >= argc) {
2636e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            fprintf(stderr, "ERROR: filename required!\n\n");
2646e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            wantUsage = 1;
2656e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            goto bail;
2666e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        }
2676e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2686e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        const char* filename = argv[optind++];
2696e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2706e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        switch (op[0]) {
2716e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        case 'a':
2726e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            CHECK_OP("add");
2736e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            if (package_info.packageName == NULL) {
2746e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root                fprintf(stderr, "ERROR: arguments required 'packageName' and 'version'\n");
2756e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root                goto bail;
2766e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            }
2776e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            doAdd(filename, &package_info);
2786e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            break;
2796e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        case 'r':
2806e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            CHECK_OP("remove");
2816e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            doRemove(filename);
2826e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            break;
2836e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        case 'i':
2846e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            CHECK_OP("info");
2856e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            doInfo(filename);
2866e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            break;
2876e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        default:
2886e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            fprintf(stderr, "ERROR: unknown command '%s'!\n\n", op);
2896e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            wantUsage = 1;
2906e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root            goto bail;
2916e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        }
2926e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
2936e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
2946e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Rootbail:
2956e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    if (wantUsage) {
2966e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        usage();
2976e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root        result = 2;
2986e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    }
2996e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root
3006e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root    return result;
3016e7ac5f0bceddf51947fbf3b376e278df0735603Kenny Root}
302