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

Last change on this file since 564 was 564, checked in by Sam Hocevar, 9 years ago

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