]> git.draconx.ca Git - liblbx.git/blob - src/lbxtool.c
license: Upgrade to GPL version 3 or later.
[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 <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <getopt.h>
24 #include <fnmatch.h>
25
26 #include "tools.h"
27 #include "lbx.h"
28
29 static void printusage(void)
30 {
31         puts("usage: lbxtool [-l|-x] [-v] [-f path] [file ...]");
32 }
33
34 static void printhelp(void)
35 {
36         printusage();
37         puts("For now, see the man page for detailed help.");
38 }
39
40 static const char *progname;
41 #define errmsg(fmt, ...) (\
42         fprintf(stderr, "%s: " fmt, progname, __VA_ARGS__)\
43 )
44
45 enum {
46         MODE_NONE,
47         MODE_LIST,
48         MODE_EXTRACT,
49 };
50
51 int filematch(char **argv, const char *name)
52 {
53         int rc, i;
54
55         for (i = 0; argv[i]; i++) {
56                 switch(fnmatch(argv[i], name, 0)) {
57                 case 0:
58                         return 0;
59                 case FNM_NOMATCH:
60                         break;
61                 default:
62                         errmsg("error matching glob: %s.\n", argv[i]);
63                         return 1;
64                 }
65         }
66
67         return i ? -1: 0;
68 }
69
70 int list(FILE *f, const char *name, int verbose, char **argv) {
71         LBX *lbx;
72         size_t nfiles;
73         unsigned int i;
74
75         lbx = lbx_fopen(f, name);
76         if (!lbx) {
77                 errmsg("failed to open archive: %s.\n", lbx_strerror());
78                 return EXIT_FAILURE;
79         }
80
81         nfiles = lbx_numfiles(lbx);
82         if (verbose) {
83                 printf("Files in archive: %zu\n", nfiles);
84         }
85
86         for (i = 0; i < nfiles; i++) {
87                 struct lbx_statbuf stat;
88
89                 lbx_stat(lbx, i, &stat);
90
91                 switch (filematch(argv, stat.name)) {
92                 case -1: continue;
93                 case  0: break;
94                 default: goto err;
95                 }
96
97                 printf("%s", stat.name);
98                 if (verbose) {
99                         printf(" size=%zu bytes", stat.size);
100                 }
101
102                 putchar('\n');
103         }
104
105         lbx_close(lbx);
106         return EXIT_SUCCESS;
107 err:
108         lbx_close(lbx);
109         return EXIT_FAILURE;
110 }
111
112 int extract(FILE *f, const char *name, int verbose, char **argv) {
113         LBX *lbx;
114         size_t nfiles;
115         unsigned int i;
116
117         lbx = lbx_fopen(f, name);
118         if (!lbx) {
119                 errmsg("failed to open archive: %s.\n", lbx_strerror());
120                 return EXIT_FAILURE;
121         }
122
123         nfiles = lbx_numfiles(lbx);
124         if (verbose) {
125                 printf("Files in archive: %zu\n", nfiles);
126         }
127
128         for (i = 0; i < nfiles; i++) {
129                 struct lbx_statbuf stat;
130                 size_t rc;
131                 FILE *of;
132                 int j;
133
134                 lbx_stat(lbx, i, &stat);
135
136                 switch (filematch(argv, stat.name)) {
137                 case -1: continue;
138                 case  0: break;
139                 default: goto err;
140                 }
141
142                 of = fopen(stat.name, "wbx");
143                 if (!of) {
144                         errmsg("failed to create output file %s: %m.\n",
145                                                              stat.name);
146                         goto err;
147                 }
148
149                 if (verbose) printf("extracting %s...\n", stat.name);
150                 rc = lbx_extract(lbx, i, of);
151                 if (rc < stat.size) {
152                         if (rc == 0) remove(stat.name);
153                         errmsg("error extracting %s: %s.\n",
154                                stat.name, lbx_strerror());
155                         goto err;
156                 }
157                 if (verbose) printf("wrote %zu bytes.\n", rc);
158         }
159
160         lbx_close(lbx);
161         return EXIT_SUCCESS;
162 err:
163         lbx_close(lbx);
164         return EXIT_FAILURE;
165 }
166
167 int main(int argc, char **argv)
168 {
169         int mode = MODE_NONE, verbose = 0;
170         const char *name = "stdin";
171         FILE *f  = stdin;
172         int opt;
173
174         static const char         *sopts   = "lxf:i:vV";
175         static const struct option lopts[] = {
176                 { "list",    0, NULL, 'l' },
177                 { "extract", 0, NULL, 'x' },
178
179                 { "file",    1, NULL, 'f' },
180                 { "index",   1, NULL, 'i' },
181
182                 { "verbose", 0, NULL, 'v' },
183
184                 { "version", 0, NULL, 'V' },
185                 { "usage",   0, NULL, 'U' },
186                 { "help",    0, NULL, 'H' },
187
188                 { 0 }
189         };
190
191         progname = "lbxtool"; /* argv[0]; */
192         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
193                 switch(opt) {
194                 case 'l':
195                         mode = MODE_LIST;
196                         break;
197                 case 'x':
198                         mode = MODE_EXTRACT;
199                         break;
200                 case 'f':
201                         if (strcmp(optarg, "-") == 0)
202                                 break;
203
204                         name = strrchr(optarg, '/');
205                         name = name ? name+1 : optarg;
206
207                         f = fopen(optarg, "rb");
208                         if (!f) {
209                                 errmsg("failed to open file %s: %m\n", optarg);
210                                 return EXIT_FAILURE;
211                         }
212                         break;
213                 case 'i':
214                         /* FIXME: Add index file support. */
215                         break;
216                 case 'v':
217                         verbose = 1;
218                         break;
219                 case 'V':
220                         puts(VERSION_BOILERPLATE("lbxtool"));
221                         return EXIT_SUCCESS;
222                 case 'U':
223                         printusage();
224                         return EXIT_SUCCESS;
225                 case 'H':
226                         printhelp();
227                         return EXIT_SUCCESS;
228                 default:
229                         return EXIT_FAILURE;
230                 }
231         }
232
233         switch (mode) {
234         case MODE_LIST:
235                 return list(f, name, verbose, &argv[optind]);
236         case MODE_EXTRACT:
237                 return extract(f, name, verbose, &argv[optind]);
238         }
239
240         fprintf(stderr, "%s: you must specify a mode.\n", progname);
241         return EXIT_FAILURE;
242 }