/* * 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; }