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