]> git.draconx.ca Git - liblbx.git/blob - src/lbxtool.c
gnulib: Use getopt-gnu module for increased tool portability.
[liblbx.git] / src / lbxtool.c
1 /*
2  *  2ooM: The Master of Orion II Reverse Engineering Project
3  *  Simple command-line tool to extract LBX archive files.
4  *  Copyright (C) 2006-2008 Nick Bowler
5  *
6  *  This program is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 #define _GNU_SOURCE
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <getopt.h>
25 #include <fnmatch.h>
26
27 #include "tools.h"
28 #include "lbx.h"
29
30
31 static void printusage(void)
32 {
33         puts("usage: lbxtool [-l|-x] [-v] [-f path] [file ...]");
34 }
35
36 static void printhelp(void)
37 {
38         printusage();
39         puts("For now, see the man page for detailed help.");
40 }
41
42 static const char *progname;
43 #define errmsg(fmt, ...) (\
44         fprintf(stderr, "%s: " fmt, progname, __VA_ARGS__)\
45 )
46
47 enum {
48         MODE_NONE,
49         MODE_LIST,
50         MODE_EXTRACT,
51 };
52
53 int filematch(char **argv, const char *name)
54 {
55         int rc, i;
56
57         for (i = 0; argv[i]; i++) {
58                 switch(fnmatch(argv[i], name, 0)) {
59                 case 0:
60                         return 0;
61                 case FNM_NOMATCH:
62                         break;
63                 default:
64                         errmsg("error matching glob: %s.\n", argv[i]);
65                         return 1;
66                 }
67         }
68
69         return i ? -1: 0;
70 }
71
72 int list(LBX *lbx, const char *name, int verbose, char **argv) {
73         size_t nfiles;
74         unsigned int i;
75
76         nfiles = lbx_numfiles(lbx);
77         if (verbose) {
78                 printf("Files in archive: %zu\n", nfiles);
79         }
80
81         for (i = 0; i < nfiles; i++) {
82                 struct lbx_statbuf stat;
83
84                 lbx_stat(lbx, i, &stat);
85
86                 switch (filematch(argv, stat.name)) {
87                 case -1: continue;
88                 case  0: break;
89                 default: return EXIT_FAILURE;
90                 }
91
92                 printf("%s", stat.name);
93                 if (verbose) {
94                         printf(" size=%zu bytes", stat.size);
95                 }
96
97                 putchar('\n');
98         }
99
100         return EXIT_SUCCESS;
101 }
102
103 int extract(LBX *lbx, const char *name, int verbose, char **argv) {
104         size_t nfiles;
105         unsigned int i;
106
107         nfiles = lbx_numfiles(lbx);
108         if (verbose) {
109                 printf("Files in archive: %zu\n", nfiles);
110         }
111
112         for (i = 0; i < nfiles; i++) {
113                 struct lbx_statbuf stat;
114                 size_t rc;
115                 FILE *of;
116                 int j;
117
118                 lbx_stat(lbx, i, &stat);
119
120                 switch (filematch(argv, stat.name)) {
121                 case -1: continue;
122                 case  0: break;
123                 default: return EXIT_FAILURE;
124                 }
125
126                 of = fopen(stat.name, "wbx");
127                 if (!of) {
128                         errmsg("failed to create output file %s: %m.\n",
129                                                              stat.name);
130                         return EXIT_FAILURE;
131                 }
132
133                 if (verbose) printf("extracting %s...\n", stat.name);
134                 rc = lbx_extract(lbx, i, of);
135                 if (rc < stat.size) {
136                         if (rc == 0) remove(stat.name);
137                         errmsg("error extracting %s: %s.\n",
138                                stat.name, lbx_strerror());
139                         return EXIT_FAILURE;
140                 }
141                 if (verbose) printf("wrote %zu bytes.\n", rc);
142         }
143
144         return EXIT_SUCCESS;
145 }
146
147 int main(int argc, char **argv)
148 {
149         int mode = MODE_NONE, verbose = 0, opt, rc = EXIT_FAILURE;
150         struct lbx_pipe_state state = { .f = stdin };
151         const char *name = "stdin";
152         LBX *lbx;
153
154         static const char         *sopts   = "lxf:i:vV";
155         static const struct option lopts[] = {
156                 { "list",    0, NULL, 'l' },
157                 { "extract", 0, NULL, 'x' },
158
159                 { "file",    1, NULL, 'f' },
160                 { "index",   1, NULL, 'i' },
161
162                 { "verbose", 0, NULL, 'v' },
163
164                 { "version", 0, NULL, 'V' },
165                 { "usage",   0, NULL, 'U' },
166                 { "help",    0, NULL, 'H' },
167
168                 { 0 }
169         };
170
171         progname = "lbxtool"; /* argv[0]; */
172         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
173                 switch(opt) {
174                 case 'l':
175                         mode = MODE_LIST;
176                         break;
177                 case 'x':
178                         mode = MODE_EXTRACT;
179                         break;
180                 case 'f':
181                         if (strcmp(optarg, "-") == 0)
182                                 break;
183
184                         name = strrchr(optarg, '/');
185                         name = name ? name+1 : optarg;
186
187                         if (!freopen(optarg, "rb", state.f)) {
188                                 errmsg("failed to open file %s: %m\n", optarg);
189                                 return EXIT_FAILURE;
190                         }
191                         break;
192                 case 'i':
193                         /* FIXME: Add index file support. */
194                         break;
195                 case 'v':
196                         verbose = 1;
197                         break;
198                 case 'V':
199                         puts(VERSION_BOILERPLATE("lbxtool"));
200                         return EXIT_SUCCESS;
201                 case 'U':
202                         printusage();
203                         return EXIT_SUCCESS;
204                 case 'H':
205                         printhelp();
206                         return EXIT_SUCCESS;
207                 default:
208                         return EXIT_FAILURE;
209                 }
210         }
211
212         if (fseek(state.f, 0, SEEK_CUR) == 0)
213                 lbx = lbx_open(state.f, &lbx_default_fops, NULL, name);
214         else
215                 lbx = lbx_open(&state, &lbx_pipe_fops, NULL, name);
216
217         if (!lbx) {
218                 errmsg("failed to open archive: %s.\n", lbx_strerror());
219                 return EXIT_FAILURE;
220         }
221
222         switch (mode) {
223         case MODE_LIST:
224                 rc = list(lbx, name, verbose, &argv[optind]);
225                 break;
226         case MODE_EXTRACT:
227                 rc = extract(lbx, name, verbose, &argv[optind]);
228                 break;
229         default:
230                 fprintf(stderr, "%s: you must specify a mode.\n", progname);
231         }
232
233         lbx_close(lbx);
234         return rc;
235 }