]> git.draconx.ca Git - liblbx.git/blob - src/lbxtool.c
Trivial manual fixes.
[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-2014, 2021 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
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <getopt.h>
26 #include <fnmatch.h>
27
28 #include "help.h"
29
30 #include "tools.h"
31 #include "lbx.h"
32 #include "error.h"
33
34 #include "toolopts.h"
35 static const char sopts[] = SOPT_STRING;
36 static const struct option lopts[] = { LOPTS_INITIALIZER, {0} };
37
38 static void print_usage(FILE *f)
39 {
40         const char *progname = tool_invocation();
41
42         fprintf(f, "Usage: %s [options] [-l|-x] [file ...]\n", progname);
43         if (f != stdout)
44                 fprintf(f, "Try %s --help for more information.\n", progname);
45 }
46
47 static void print_help(void)
48 {
49         const struct option *opt;
50
51         print_usage(stdout);
52
53         puts("This is \"lbxtool\": a command-line tool for manipulating the LBX archives\n"
54              "used in Moo2.");
55
56         putchar('\n');
57         puts("Options:");
58         for (opt = lopts; opt->name; opt++) {
59                 struct lopt_help help;
60                 int w;
61
62                 if (!lopt_get_help(opt, &help))
63                         continue;
64
65                 help_print_option(opt, help.arg, help.desc, 20);
66         }
67         putchar('\n');
68
69         puts("For more information, see the lbxtool(1) man page.");
70         putchar('\n');
71
72         printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
73 }
74
75 enum {
76         MODE_NONE,
77         MODE_LIST,
78         MODE_EXTRACT,
79 };
80
81 int filematch(char **argv, const char *name)
82 {
83         int i;
84
85         for (i = 0; argv[i]; i++) {
86                 switch(fnmatch(argv[i], name, 0)) {
87                 case 0:
88                         return 0;
89                 case FNM_NOMATCH:
90                         break;
91                 default:
92                         tool_err(-1, "error matching glob: %s", argv[i]);
93                         return 1;
94                 }
95         }
96
97         return i ? -1: 0;
98 }
99
100 int list(struct lbx *lbx, int verbose, char **argv)
101 {
102         unsigned int i;
103
104         if (verbose) {
105                 printf("Files in archive: %u\n", lbx->nfiles);
106         }
107
108         for (i = 0; i < lbx->nfiles; i++) {
109                 struct lbx_statbuf stat;
110
111                 lbx_file_stat(lbx, i, &stat);
112
113                 switch (filematch(argv, stat.name)) {
114                 case -1: continue;
115                 case  0: break;
116                 default: return EXIT_FAILURE;
117                 }
118
119                 printf("%s", stat.name);
120                 if (verbose) {
121                         printf(" size=%zu bytes", stat.size);
122                 }
123
124                 putchar('\n');
125         }
126
127         return EXIT_SUCCESS;
128 }
129
130 int extract_file(LBXfile *f, const struct lbx_statbuf *stat)
131 {
132         int ret = -1;
133         size_t rc;
134         FILE *of;
135
136         of = fopen(stat->name, "wb");
137         if (!of) {
138                 tool_err(0, "%s: fopen", stat->name);
139                 return -1;
140         }
141
142         while (1) {
143                 unsigned char buf[1024];
144
145                 rc = lbx_file_read(f, buf, sizeof buf);
146                 if (rc > 0) {
147                         /* Write out any data we got. */
148                         if (fwrite(buf, rc, 1, of) != 1) {
149                                 tool_err(0, "%s: fwrite", stat->name);
150                                 break;
151                         }
152                 }
153
154                 /* Now test for read errors */
155                 if (rc < sizeof buf) {
156                         if (!lbx_file_eof(f))
157                                 tool_err(-1, "error reading archive: %s", lbx_errmsg());
158                         else
159                                 ret = 0;
160                         break;
161                 }
162         }
163
164         if (fclose(of) == EOF) {
165                 tool_err(0, "%s: fclose", stat->name);
166                 return -1;
167         }
168
169         return ret;
170 }
171
172 int extract(struct lbx *lbx, int verbose, char **argv)
173 {
174         unsigned int i;
175
176         if (verbose) {
177                 printf("Files in archive: %u\n", lbx->nfiles);
178         }
179
180         for (i = 0; i < lbx->nfiles; i++) {
181                 struct lbx_statbuf stat;
182                 LBXfile *file;
183
184                 lbx_file_stat(lbx, i, &stat);
185
186                 switch (filematch(argv, stat.name)) {
187                 case -1: continue;
188                 case  0: break;
189                 default: return EXIT_FAILURE;
190                 }
191
192                 file = lbx_file_open(lbx, i);
193                 if (!file) {
194                         tool_err(-1, "%s: %s", stat.name, lbx_errmsg());
195                         continue;
196                 }
197
198                 if (extract_file(file, &stat) == -1)
199                         return EXIT_FAILURE;
200                 lbx_file_close(file);
201         }
202
203         return EXIT_SUCCESS;
204 }
205
206 int main(int argc, char **argv)
207 {
208         int mode = MODE_NONE, verbose = 0, opt, rc = EXIT_FAILURE;
209         struct lbx_pipe_state stdin_handle = { .f = stdin };
210         const char *file = NULL;
211         struct lbx *lbx;
212
213         tool_init("lbxtool", argc, argv);
214         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
215                 switch(opt) {
216                 case 'l':
217                         mode = MODE_LIST;
218                         break;
219                 case 'x':
220                         mode = MODE_EXTRACT;
221                         break;
222                 case 'f':
223                         file = optarg;
224                         break;
225                 case 'v':
226                         verbose = 1;
227                         break;
228                 case 'V':
229                         tool_version();
230                         return EXIT_SUCCESS;
231                 case 'H':
232                         print_help();
233                         return EXIT_SUCCESS;
234                 default:
235                         return EXIT_FAILURE;
236                 }
237         }
238
239         if (file)
240                 lbx = lbx_fopen(file);
241         else
242                 lbx = lbx_open(&stdin_handle, &lbx_pipe_fops, NULL, "stdin");
243
244         if (!lbx) {
245                 tool_err(-1, "%s: %s", file ? file : "stdin", lbx_errmsg());
246                 return EXIT_FAILURE;
247         }
248
249         switch (mode) {
250         case MODE_LIST:
251                 rc = list(lbx, verbose, &argv[optind]);
252                 break;
253         case MODE_EXTRACT:
254                 rc = extract(lbx, verbose, &argv[optind]);
255                 break;
256         default:
257                 tool_err(-1, "no mode specified");
258         }
259
260         lbx_close(lbx);
261         return rc;
262 }