]> git.draconx.ca Git - liblbx.git/blob - src/lbxtool.c
liblbx: Kill lbx_numfiles.
[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-2010 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 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <getopt.h>
25 #include <fnmatch.h>
26
27 #include "tools.h"
28 #include "lbx.h"
29 #include "error.h"
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 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, int verbose, char **argv)
73 {
74         unsigned int i;
75
76         if (verbose) {
77                 printf("Files in archive: %u\n", lbx->nfiles);
78         }
79
80         for (i = 0; i < lbx->nfiles; i++) {
81                 struct lbx_statbuf stat;
82
83                 lbx_file_stat(lbx, i, &stat);
84
85                 switch (filematch(argv, stat.name)) {
86                 case -1: continue;
87                 case  0: break;
88                 default: return EXIT_FAILURE;
89                 }
90
91                 printf("%s", stat.name);
92                 if (verbose) {
93                         printf(" size=%zu bytes", stat.size);
94                 }
95
96                 putchar('\n');
97         }
98
99         return EXIT_SUCCESS;
100 }
101
102 int extract_file(LBXfile *f, const struct lbx_statbuf *stat)
103 {
104         int ret = -1;
105         size_t rc;
106         FILE *of;
107
108         of = fopen(stat->name, "wb");
109         if (!of) {
110                 errmsg("%s: fopen: %s\n",
111                         stat->name, strerror(errno));
112                 return -1;
113         }
114
115         while (1) {
116                 unsigned char buf[1024];
117
118                 rc = lbx_file_read(f, buf, sizeof buf);
119                 if (rc == 0) {
120                         if (lbx_file_eof(f))
121                                 ret = 0;
122                         break;
123                 }
124
125                 if (fwrite(buf, rc, 1, of) != 1) {
126                         errmsg("%s: fwrite: %s\n", stat->name, strerror(errno));
127                         break;
128                 }
129
130                 if (rc < sizeof buf) {
131                         if (lbx_file_eof(f))
132                                 ret = 0;
133                         break;
134                 }
135         }
136
137         if (fclose(of) == EOF) {
138                 errmsg("%s: fclose: %s\n", stat->name, strerror(errno));
139                 return -1;
140         }
141
142         return ret;
143 }
144
145 int extract(LBX *lbx, int verbose, char **argv)
146 {
147         unsigned int i;
148
149         if (verbose) {
150                 printf("Files in archive: %u\n", lbx->nfiles);
151         }
152
153         for (i = 0; i < lbx->nfiles; i++) {
154                 struct lbx_statbuf stat;
155                 LBXfile *file;
156
157                 lbx_file_stat(lbx, i, &stat);
158
159                 switch (filematch(argv, stat.name)) {
160                 case -1: continue;
161                 case  0: break;
162                 default: return EXIT_FAILURE;
163                 }
164
165                 file = lbx_file_open(lbx, i);
166                 if (!file) {
167                         errmsg("%s: %s.\n", stat.name, lbx_errmsg());
168                         continue;
169                 }
170
171                 if (extract_file(file, &stat) == -1)
172                         return EXIT_FAILURE;
173                 lbx_file_close(file);
174         }
175
176         return EXIT_SUCCESS;
177 }
178
179 int main(int argc, char **argv)
180 {
181         int mode = MODE_NONE, verbose = 0, opt, rc = EXIT_FAILURE;
182         struct lbx_pipe_state stdin_handle = { .f = stdin };
183         const char *file = NULL;
184         LBX *lbx;
185
186         static const char         *sopts   = "lxf:i:vV";
187         static const struct option lopts[] = {
188                 { "list",    0, NULL, 'l' },
189                 { "extract", 0, NULL, 'x' },
190
191                 { "file",    1, NULL, 'f' },
192                 { "index",   1, NULL, 'i' },
193
194                 { "verbose", 0, NULL, 'v' },
195
196                 { "version", 0, NULL, 'V' },
197                 { "usage",   0, NULL, 'U' },
198                 { "help",    0, NULL, 'H' },
199
200                 { 0 }
201         };
202
203         progname = "lbxtool"; /* argv[0]; */
204         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
205                 switch(opt) {
206                 case 'l':
207                         mode = MODE_LIST;
208                         break;
209                 case 'x':
210                         mode = MODE_EXTRACT;
211                         break;
212                 case 'f':
213                         file = optarg;
214                         break;
215                 case 'i':
216                         /* FIXME: Add index file support. */
217                         break;
218                 case 'v':
219                         verbose = 1;
220                         break;
221                 case 'V':
222                         puts(VERSION_BOILERPLATE("lbxtool"));
223                         return EXIT_SUCCESS;
224                 case 'U':
225                         printusage();
226                         return EXIT_SUCCESS;
227                 case 'H':
228                         printhelp();
229                         return EXIT_SUCCESS;
230                 default:
231                         return EXIT_FAILURE;
232                 }
233         }
234
235         if (file)
236                 lbx = lbx_fopen(file);
237         else
238                 lbx = lbx_open(&stdin_handle, &lbx_pipe_fops, NULL, "stdin");
239
240         if (!lbx) {
241                 errmsg("%s: %s.\n", file ? file : "stdin", lbx_errmsg());
242                 return EXIT_FAILURE;
243         }
244
245         switch (mode) {
246         case MODE_LIST:
247                 rc = list(lbx, verbose, &argv[optind]);
248                 break;
249         case MODE_EXTRACT:
250                 rc = extract(lbx, verbose, &argv[optind]);
251                 break;
252         default:
253                 fprintf(stderr, "%s: you must specify a mode.\n", progname);
254         }
255
256         lbx_close(lbx);
257         return rc;
258 }