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

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

tool: compute and display SPEC item CRCs.

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