source: abuse/trunk/src/tool/abuse-tool.cpp @ 550

Last change on this file since 550 was 550, checked in by Sam Hocevar, 11 years ago

tool: implement the type', put' and `putpcx' commands.

  • Property svn:keywords set to Id
File size: 9.6 KB
Line 
1//
2// Abuse Tool - package manager for Abuse format
3//
4// Copyright: (c) 2011 Sam Hocevar <sam@hocevar.net>
5//   This program is free software; you can redistribute it and/or
6//   modify it under the terms of the Do What The Fuck You Want To
7//   Public License, Version 2, as published by Sam Hocevar. See
8//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
9//
10
11#include "config.h"
12
13#include <cstring>
14#include <cstdio>
15
16#include "common.h"
17#include "specs.h"
18#include "image.h"
19#include "pcxread.h"
20
21static void Usage();
22
23enum
24{
25    CMD_INVALID,
26    CMD_LIST,
27    CMD_GET,
28    CMD_MOVE,
29    CMD_DEL,
30    CMD_PUT,
31    CMD_RENAME,
32    CMD_TYPE,
33    CMD_GETPCX,
34    CMD_PUTPCX,
35};
36
37int main(int argc, char *argv[])
38{
39    if (argc < 3)
40    {
41        Usage();
42        return EXIT_FAILURE;
43    }
44
45    int cmd = !strcmp(argv[2], "list") ? CMD_LIST
46            : !strcmp(argv[2], "get") ? CMD_GET
47            : !strcmp(argv[2], "del") ? CMD_DEL
48            : !strcmp(argv[2], "put") ? CMD_PUT
49            : !strcmp(argv[2], "move") ? CMD_MOVE
50            : !strcmp(argv[2], "rename") ? CMD_RENAME
51            : !strcmp(argv[2], "type") ? CMD_TYPE
52            : !strcmp(argv[2], "getpcx") ? CMD_GETPCX
53            : !strcmp(argv[2], "putpcx") ? CMD_PUTPCX
54            : CMD_INVALID;
55
56    if (cmd == CMD_INVALID)
57    {
58        fprintf(stderr, "abuse-tool: unknown command `%s'\n", argv[2]);
59        return EXIT_FAILURE;
60    }
61
62    /* Check argument count and file access mode */
63    char const *mode = "rwb";
64    int minargc = 3;
65
66    switch (cmd)
67    {
68    case CMD_LIST:
69        mode = "rb"; // Read-only access
70        break;
71    case CMD_GET:
72        minargc = 4;
73        mode = "rb"; // Read-only access
74        break;
75    case CMD_PUT:
76        minargc = 5;
77        break;
78    case CMD_MOVE:
79        minargc = 5;
80        break;
81    case CMD_RENAME:
82        minargc = 5;
83        break;
84    case CMD_TYPE:
85        minargc = 5;
86        break;
87    case CMD_DEL:
88        minargc = 4;
89        break;
90    case CMD_GETPCX:
91        minargc = 4;
92        mode = "rb"; // Read-only access
93        break;
94    case CMD_PUTPCX:
95        minargc = 5;
96        break;
97    }
98
99    if (argc < minargc)
100    {
101        fprintf(stderr, "abuse-tool: too few arguments to command `%s'\n",
102                         argv[2]);
103        return EXIT_FAILURE;
104    }
105
106    /* Open the SPEC file */
107    char tmpfile[4096];
108    char const *file = argv[1];
109    snprintf(tmpfile, 4096, "%s.tmp", file);
110
111    jFILE fp(file, mode);
112    if (fp.open_failure())
113    {
114        fprintf(stderr, "ERROR - could not open %s\n", file);
115        return EXIT_FAILURE;
116    }
117
118    spec_directory dir(&fp);
119
120    /* Now really execute commands */
121    if (cmd == CMD_LIST)
122    {
123        printf("   id  type     size  name & information\n");
124        printf(" ----  ----  -------  ----------------------------\n");
125
126        for (int i = 0; i < dir.total; i++)
127        {
128            spec_entry *se = dir.entries[i];
129
130            /* Print basic information */
131            printf("% 5i   % 3i % 8i  %s",
132                   i, se->type, (int)se->size, se->name);
133
134            /* Is there anything special to say? */
135            switch (se->type)
136            {
137            case SPEC_IMAGE:
138            case SPEC_FORETILE:
139            case SPEC_BACKTILE:
140            case SPEC_CHARACTER:
141            case SPEC_CHARACTER2:
142              {
143                image *im = new image(&fp, se);
144                printf(" (%i x %i pixels)", im->Size().x, im->Size().y);
145                delete im;
146                break;
147              }
148            case SPEC_PALETTE:
149              {
150                palette *pal = new palette(se, &fp);
151                printf(" (%i colors)", pal->pal_size());
152                delete pal;
153                break;
154              }
155            }
156
157            /* Finish line */
158            putchar('\n');
159        }
160
161        return EXIT_SUCCESS;
162    }
163    else if (cmd == CMD_GET)
164    {
165        int id = atoi(argv[3]);
166
167        if (id < 0 || id >= dir.total)
168        {
169            fprintf(stderr, "abuse-tool: id %i not found in %s\n", id, file);
170            return EXIT_FAILURE;
171        }
172
173        spec_entry *se = dir.entries[id];
174        fp.seek(se->offset, SEEK_SET);
175
176        for (size_t todo = se->size; todo > 0; )
177        {
178            uint8_t buf[1024];
179            int step = Min(todo, 1024);
180            fp.read(buf, step);
181            fwrite(buf, step, 1, stdout);
182            todo -= step;
183        }
184        return EXIT_SUCCESS;
185    }
186    else if (cmd == CMD_GETPCX)
187    {
188        palette *pal;
189        int imgid = atoi(argv[3]);
190        int palid = argc > 4 ? atoi(argv[4]) : -1;
191
192        for (int i = 0; palid == -1 && i < dir.total; i++)
193            if (dir.entries[i]->type == SPEC_PALETTE)
194                palid = i;
195
196        if (palid == -1)
197            pal = new palette(256);
198        else
199            pal = new palette(dir.entries[palid], &fp);
200
201        image *im = new image(&fp, dir.entries[imgid]);
202        write_PCX(im, pal, "/dev/stdout");
203        delete im;
204        delete pal;
205        return EXIT_SUCCESS;
206    }
207    else if (cmd == CMD_MOVE)
208    {
209        int src = atoi(argv[3]);
210        int dst = atoi(argv[4]);
211
212        if (src < 0 || dst < 0 || src >= dir.total || dst >= dir.total)
213        {
214            fprintf(stderr, "abuse-tool: ids %i/%i out of range\n", src, dst);
215            return EXIT_FAILURE;
216        }
217
218        dir.FullyLoad(&fp);
219
220        spec_entry *tmp = dir.entries[src];
221        for (int d = src < dst ? 1 : -1; src != dst; src += d)
222            dir.entries[src] = dir.entries[src + d];
223        dir.entries[dst] = tmp;
224    }
225    else if (cmd == CMD_RENAME || cmd == CMD_TYPE)
226    {
227        int id = atoi(argv[3]);
228
229        if (id < 0 || id >= dir.total)
230        {
231            fprintf(stderr, "abuse-tool: id %i out of range\n", id);
232            return EXIT_FAILURE;
233        }
234
235        dir.FullyLoad(&fp);
236        if (cmd == CMD_RENAME)
237            dir.entries[id]->name = argv[4];
238        else
239            dir.entries[id]->type = (uint8_t)atoi(argv[4]);
240    }
241    else if (cmd == CMD_DEL)
242    {
243        int id = atoi(argv[3]);
244
245        if (id < 0 || id >= dir.total)
246        {
247            fprintf(stderr, "abuse-tool: id %i out of range\n", id);
248            return EXIT_FAILURE;
249        }
250
251        dir.total--;
252        for (int i = id; i < dir.total; i++)
253            dir.entries[i] = dir.entries[i + 1];
254
255        dir.FullyLoad(&fp);
256    }
257    else if (cmd == CMD_PUT || cmd == CMD_PUTPCX)
258    {
259        int id = atoi(argv[3]);
260        uint8_t type = atoi(argv[4]);
261
262        if (id == -1)
263            id = dir.total;
264
265        if (id < 0 || id > dir.total)
266        {
267            fprintf(stderr, "abuse-tool: id %i out of range\n", id);
268            return EXIT_FAILURE;
269        }
270
271        dir.FullyLoad(&fp);
272        dir.total++;
273        dir.entries = (spec_entry **)realloc(dir.entries,
274                                             dir.total * sizeof(spec_entry *));
275        for (int i = id + 1; i < dir.total; i++)
276            dir.entries[i] = dir.entries[i - 1];
277
278        char *name = strrchr(argv[5], '/');
279        if (!name)
280            name = argv[5];
281
282        uint8_t *data;
283        size_t len;
284
285        if (cmd == CMD_PUT)
286        {
287            jFILE fp2(argv[5], "rb");
288            if (fp2.open_failure())
289            {
290                fprintf(stderr, "abuse-tool: cannot open %s\n", argv[5]);
291                return EXIT_FAILURE;
292            }
293            len = fp2.file_size();
294            data = (uint8_t *)malloc(len);
295            fp2.read(data, len);
296        }
297        else
298        {
299            palette *pal = NULL;
300            image *im = read_PCX(argv[5], pal);
301            if (!im)
302            {
303                fprintf(stderr, "abuse-tool: cannot open %s\n", argv[5]);
304                return EXIT_FAILURE;
305            }
306            vec2i size = im->Size();
307            len = 2 * sizeof(uint16_t) + size.x * size.y;
308            data = (uint8_t *)malloc(len);
309            uint16_t x = lltl((uint16_t)size.x);
310            uint16_t y = lltl((uint16_t)size.y);
311            memcpy(data, &x, sizeof(x));
312            memcpy(data + 2, &y, sizeof(y));
313            memcpy(data + 4, im->scan_line(0), size.x * size.y);
314        }
315        dir.entries[id] = new spec_entry(type, name, NULL, len, 0);
316        dir.entries[id]->data = data;
317    }
318    else
319    {
320        /* Not implemented yet */
321        return EXIT_FAILURE;
322    }
323
324    /* If we get here, we need to write the directory back */
325    dir.calc_offsets();
326    fp.seek(0, SEEK_SET); // FIXME: create a new file
327    dir.write(&fp);
328    for (int i = 0; i < dir.total; i++)
329        fp.write(dir.entries[i]->data, dir.entries[i]->size);
330
331    return EXIT_SUCCESS;
332}
333
334static void Usage()
335{
336    fprintf(stderr, "%s",
337        "Usage: abuse-tool <spec_file> <command> [args...]\n"
338        "\n"
339        "abuse-tool is a low-level tool to edit Abuse SPEC (.spe) files.\n"
340        "\n"
341        "Commands:\n"
342        "   list                         list the contents of a SPEC file\n"
343        "   get <id>                     dump entry <id> to stdout\n"
344        "   getpcx <id>                  dump PCX image <id> to stdout\n"
345        "   put <id> <type> <name>       insert file <name> of type <type> at position\n"
346        "                                <id>\n"
347        "   putpcx <id> <type> <name>    insert PCX image <name> of type <type> at\n"
348        "                                position <id>\n"
349        "   rename <id> <name>           rename entry <id> to <name>\n"
350        "   type <id> <type>             set entry <id> type to <type>\n"
351        "   move <id1> <id2>             move entry <id1> to position <id2>\n"
352        "   del <id>                     delete entry <id>\n"
353        "See the abuse-tool(6) manual page for more information.\n");
354}
355
Note: See TracBrowser for help on using the repository browser.