source: abuse/branches/lol/src/tool/abuse-tool.cpp @ 732

Last change on this file since 732 was 732, checked in by Sam Hocevar, 8 years ago

build: SDL2 compilation fixes.

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