]> git.draconx.ca Git - liblbx.git/blob - src/lbxtool.c
liblbx: Correct EOF handling in lbx_file_read.
[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 © 2006-2011, 2013 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 enum {
43         MODE_NONE,
44         MODE_LIST,
45         MODE_EXTRACT,
46 };
47
48 int filematch(char **argv, const char *name)
49 {
50         int i;
51
52         for (i = 0; argv[i]; i++) {
53                 switch(fnmatch(argv[i], name, 0)) {
54                 case 0:
55                         return 0;
56                 case FNM_NOMATCH:
57                         break;
58                 default:
59                         tool_err(-1, "error matching glob: %s", argv[i]);
60                         return 1;
61                 }
62         }
63
64         return i ? -1: 0;
65 }
66
67 int list(struct lbx *lbx, int verbose, char **argv)
68 {
69         unsigned int i;
70
71         if (verbose) {
72                 printf("Files in archive: %u\n", lbx->nfiles);
73         }
74
75         for (i = 0; i < lbx->nfiles; i++) {
76                 struct lbx_statbuf stat;
77
78                 lbx_file_stat(lbx, i, &stat);
79
80                 switch (filematch(argv, stat.name)) {
81                 case -1: continue;
82                 case  0: break;
83                 default: return EXIT_FAILURE;
84                 }
85
86                 printf("%s", stat.name);
87                 if (verbose) {
88                         printf(" size=%zu bytes", stat.size);
89                 }
90
91                 putchar('\n');
92         }
93
94         return EXIT_SUCCESS;
95 }
96
97 int extract_file(LBXfile *f, const struct lbx_statbuf *stat)
98 {
99         int ret = -1;
100         size_t rc;
101         FILE *of;
102
103         of = fopen(stat->name, "wb");
104         if (!of) {
105                 tool_err(0, "%s: fopen", stat->name);
106                 return -1;
107         }
108
109         while (1) {
110                 unsigned char buf[1024];
111
112                 rc = lbx_file_read(f, buf, sizeof buf);
113                 if (rc > 0) {
114                         /* Write out any data we got. */
115                         if (fwrite(buf, rc, 1, of) != 1) {
116                                 tool_err(0, "%s: fwrite", stat->name);
117                                 break;
118                         }
119                 }
120
121                 /* Now test for read errors */
122                 if (rc < sizeof buf) {
123                         if (!lbx_file_eof(f))
124                                 tool_err(-1, "error reading archive: %s", lbx_errmsg());
125                         else
126                                 ret = 0;
127                         break;
128                 }
129         }
130
131         if (fclose(of) == EOF) {
132                 tool_err(0, "%s: fclose", stat->name);
133                 return -1;
134         }
135
136         return ret;
137 }
138
139 int extract(struct lbx *lbx, int verbose, char **argv)
140 {
141         unsigned int i;
142
143         if (verbose) {
144                 printf("Files in archive: %u\n", lbx->nfiles);
145         }
146
147         for (i = 0; i < lbx->nfiles; i++) {
148                 struct lbx_statbuf stat;
149                 LBXfile *file;
150
151                 lbx_file_stat(lbx, i, &stat);
152
153                 switch (filematch(argv, stat.name)) {
154                 case -1: continue;
155                 case  0: break;
156                 default: return EXIT_FAILURE;
157                 }
158
159                 file = lbx_file_open(lbx, i);
160                 if (!file) {
161                         tool_err(-1, "%s: %s", stat.name, lbx_errmsg());
162                         continue;
163                 }
164
165                 if (extract_file(file, &stat) == -1)
166                         return EXIT_FAILURE;
167                 lbx_file_close(file);
168         }
169
170         return EXIT_SUCCESS;
171 }
172
173 int main(int argc, char **argv)
174 {
175         int mode = MODE_NONE, verbose = 0, opt, rc = EXIT_FAILURE;
176         struct lbx_pipe_state stdin_handle = { .f = stdin };
177         const char *file = NULL;
178         struct lbx *lbx;
179
180         static const char         *sopts   = "lxf:i:vV";
181         static const struct option lopts[] = {
182                 { "list",    0, NULL, 'l' },
183                 { "extract", 0, NULL, 'x' },
184
185                 { "file",    1, NULL, 'f' },
186                 { "index",   1, NULL, 'i' },
187
188                 { "verbose", 0, NULL, 'v' },
189
190                 { "version", 0, NULL, 'V' },
191                 { "usage",   0, NULL, 'U' },
192                 { "help",    0, NULL, 'H' },
193
194                 { 0 }
195         };
196
197         tool_init("lbxtool", argc, argv);
198         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
199                 switch(opt) {
200                 case 'l':
201                         mode = MODE_LIST;
202                         break;
203                 case 'x':
204                         mode = MODE_EXTRACT;
205                         break;
206                 case 'f':
207                         file = optarg;
208                         break;
209                 case 'i':
210                         /* FIXME: Add index file support. */
211                         break;
212                 case 'v':
213                         verbose = 1;
214                         break;
215                 case 'V':
216                         tool_version();
217                         return EXIT_SUCCESS;
218                 case 'U':
219                         printusage();
220                         return EXIT_SUCCESS;
221                 case 'H':
222                         printhelp();
223                         return EXIT_SUCCESS;
224                 default:
225                         return EXIT_FAILURE;
226                 }
227         }
228
229         if (file)
230                 lbx = lbx_fopen(file);
231         else
232                 lbx = lbx_open(&stdin_handle, &lbx_pipe_fops, NULL, "stdin");
233
234         if (!lbx) {
235                 tool_err(-1, "%s: %s", file ? file : "stdin", lbx_errmsg());
236                 return EXIT_FAILURE;
237         }
238
239         switch (mode) {
240         case MODE_LIST:
241                 rc = list(lbx, verbose, &argv[optind]);
242                 break;
243         case MODE_EXTRACT:
244                 rc = extract(lbx, verbose, &argv[optind]);
245                 break;
246         default:
247                 tool_err(-1, "no mode specified");
248         }
249
250         lbx_close(lbx);
251         return rc;
252 }