/*
* 2ooM: The Master of Orion II Reverse Engineering Project
* Simple command-line tool to extract LBX archive files.
* Copyright © 2006-2011, 2013-2014, 2021 Nick Bowler
*
* 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 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include "help.h"
#include "tools.h"
#include "lbx.h"
#include "error.h"
#include "toolopts.h"
static const char sopts[] = SOPT_STRING;
static const struct option lopts[] = { LOPTS_INITIALIZER, {0} };
static void print_usage(FILE *f)
{
const char *progname = tool_invocation();
fprintf(f, "Usage: %s [options] [-l|-x] [file ...]\n", progname);
if (f != stdout)
fprintf(f, "Try %s --help for more information.\n", progname);
}
static void print_help(void)
{
const struct option *opt;
print_usage(stdout);
puts("This is \"lbxtool\": a command-line tool for manipulating the LBX archives\n"
"used in Moo2.");
putchar('\n');
puts("Options:");
for (opt = lopts; opt->name; opt++) {
struct lopt_help help;
int w;
if (!lopt_get_help(opt, &help))
continue;
help_print_option(opt, help.arg, help.desc, 20);
}
putchar('\n');
puts("For more information, see the lbxtool(1) man page.");
putchar('\n');
printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
}
enum {
MODE_NONE,
MODE_LIST,
MODE_EXTRACT,
};
int filematch(char **argv, const char *name)
{
int i;
for (i = 0; argv[i]; i++) {
switch(fnmatch(argv[i], name, 0)) {
case 0:
return 0;
case FNM_NOMATCH:
break;
default:
tool_err(-1, "error matching glob: %s", argv[i]);
return 1;
}
}
return i ? -1: 0;
}
int list(struct lbx *lbx, int verbose, char **argv)
{
unsigned int i;
if (verbose) {
printf("Files in archive: %u\n", lbx->nfiles);
}
for (i = 0; i < lbx->nfiles; i++) {
struct lbx_statbuf stat;
lbx_file_stat(lbx, i, &stat);
switch (filematch(argv, stat.name)) {
case -1: continue;
case 0: break;
default: return EXIT_FAILURE;
}
printf("%s", stat.name);
if (verbose) {
printf(" size=%zu bytes", stat.size);
}
putchar('\n');
}
return EXIT_SUCCESS;
}
int extract_file(LBXfile *f, const struct lbx_statbuf *stat)
{
int ret = -1;
size_t rc;
FILE *of;
of = fopen(stat->name, "wb");
if (!of) {
tool_err(0, "%s: fopen", stat->name);
return -1;
}
while (1) {
unsigned char buf[1024];
rc = lbx_file_read(f, buf, sizeof buf);
if (rc > 0) {
/* Write out any data we got. */
if (fwrite(buf, rc, 1, of) != 1) {
tool_err(0, "%s: fwrite", stat->name);
break;
}
}
/* Now test for read errors */
if (rc < sizeof buf) {
if (!lbx_file_eof(f))
tool_err(-1, "error reading archive: %s", lbx_errmsg());
else
ret = 0;
break;
}
}
if (fclose(of) == EOF) {
tool_err(0, "%s: fclose", stat->name);
return -1;
}
return ret;
}
int extract(struct lbx *lbx, int verbose, char **argv)
{
unsigned int i;
if (verbose) {
printf("Files in archive: %u\n", lbx->nfiles);
}
for (i = 0; i < lbx->nfiles; i++) {
struct lbx_statbuf stat;
LBXfile *file;
lbx_file_stat(lbx, i, &stat);
switch (filematch(argv, stat.name)) {
case -1: continue;
case 0: break;
default: return EXIT_FAILURE;
}
file = lbx_file_open(lbx, i);
if (!file) {
tool_err(-1, "%s: %s", stat.name, lbx_errmsg());
continue;
}
if (extract_file(file, &stat) == -1)
return EXIT_FAILURE;
lbx_file_close(file);
}
return EXIT_SUCCESS;
}
int main(int argc, char **argv)
{
int mode = MODE_NONE, verbose = 0, opt, rc = EXIT_FAILURE;
struct lbx_pipe_state stdin_handle = { .f = stdin };
const char *file = NULL;
struct lbx *lbx;
tool_init("lbxtool", argc, argv);
while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
switch(opt) {
case 'l':
mode = MODE_LIST;
break;
case 'x':
mode = MODE_EXTRACT;
break;
case 'f':
file = optarg;
break;
case 'v':
verbose = 1;
break;
case 'V':
tool_version();
return EXIT_SUCCESS;
case 'H':
print_help();
return EXIT_SUCCESS;
default:
return EXIT_FAILURE;
}
}
if (file)
lbx = lbx_fopen(file);
else
lbx = lbx_open(&stdin_handle, &lbx_pipe_fops, NULL, "stdin");
if (!lbx) {
tool_err(-1, "%s: %s", file ? file : "stdin", lbx_errmsg());
return EXIT_FAILURE;
}
switch (mode) {
case MODE_LIST:
rc = list(lbx, verbose, &argv[optind]);
break;
case MODE_EXTRACT:
rc = extract(lbx, verbose, &argv[optind]);
break;
default:
tool_err(-1, "no mode specified");
}
lbx_close(lbx);
return rc;
}