1 /*------------------------------------------------------------------.
2 | Copyright 2001, 2002 Alexandre Duret-Lutz <duret_g@epita.fr> |
3 | |
4 | This file is part of Heroes. |
5 | |
6 | Heroes is free software; you can redistribute it and/or modify it |
7 | under the terms of the GNU General Public License version 2 as |
8 | published by the Free Software Foundation. |
9 | |
10 | Heroes is distributed in the hope that it will be useful, but |
11 | WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; if not, write to the Free Software |
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
18 | 02111-1307 USA |
19 `------------------------------------------------------------------*/
20
21 #include "system.h"
22 #include "error.h"
23 #include "lvl.h"
24
25 const char *program_name; /* argv[0] */
26 int exit_status = 0; /* $? */
27
28 struct an_option_set {
29 bool print_directions; /* --print d */
30 bool print_filename; /* --print f */
31 bool print_header; /* --print h */
32 bool print_walls; /* --print w */
33 bool print_types; /* --print t */
34 bool print_type_keys; /* --print T */
35 bool print_tunnels; /* --print @ */
36 bool print_tile_details; /* --print i */
37 const char *indent; /* Set to a prefix output on every
38 lines, e.g. " ". (--indent) */
39 } options = {
40 false,
41 false,
42 false,
43 false,
44 false,
45 false,
46 false,
47 false,
48 ""
49 };
50
51 static void
default_options(void)52 default_options (void)
53 {
54 options.print_filename = true;
55 options.print_header = true;
56 options.indent = " ";
57 }
58
59 static void
version(void)60 version (void)
61 {
62 puts ("heroeslvl (Heroes) " VERSION "\n");
63 printf (_("Copyright (C) %d Alexandre Duret-Lutz.\n"), 2002);
64 puts (_("This is free software; see the source for copying conditions. "
65 "There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS"
66 " FOR A PARTICULAR PURPOSE."));
67 exit (0);
68 }
69
70 static void
usage(int status)71 usage (int status)
72 {
73 if (status) {
74 fprintf (stderr, _("Try '%s --help' for more information.\n"),
75 program_name);
76 exit (status);
77 }
78
79 printf (_("Usage: %s [OPTIONS]... levels\n\n"), program_name);
80 puts (_("Heroeslvl is a tool used to inspect Heroes' level files.\n"));
81 puts (_("\
82 Mandatory arguments to long options are mandatory for short options too.\n"));
83 puts (_("\
84 -v, --version display version number"));
85 puts (_("\
86 -h, --help display this help"));
87 puts (_("\
88 -p, --print=WHAT select information to display. WHAT should be\n\
89 one or more of these characters:"));
90 printf ("%34s%s\n", "", _("d print square directions"));
91 printf ("%34s%s\n", "", _("f print filename"));
92 printf ("%34s%s\n", "", _("h print header"));
93 printf ("%34s%s\n", "", _("i print tile details"));
94 printf ("%34s%s\n", "", _("t print square type map"));
95 printf ("%34s%s\n", "", _("T print type keys"));
96 printf ("%34s%s\n", "", _("w print square wall map"));
97 printf ("%34s%s\n", "", _("@ print tunnels"));
98 puts (_("\
99 -i, --indent indent everything but the filename"));
100 puts ("");
101 puts (_("When no options are given, the default is -ipfh."));
102 puts (_("Report bugs to <heroes-bugs@lists.sourceforge.net>."));
103 exit (status);
104 }
105
106 const struct option long_options[] = {
107 {"help", no_argument, NULL, 'h'},
108 {"indent", no_argument, NULL, 'i'},
109 {"print", required_argument, NULL, 'p'},
110 {"version", no_argument, NULL, 'v'},
111 {NULL, 0, NULL, 0}
112 };
113
114 static void
decode_print_options(const char * flags)115 decode_print_options (const char *flags)
116 {
117 while (*flags) {
118 switch (*flags) {
119 case 'd':
120 options.print_directions = true;
121 break;
122 case 'f':
123 options.print_filename = true;
124 break;
125 case 'h':
126 options.print_header = true;
127 break;
128 case 'i':
129 options.print_tile_details = true;
130 break;
131 case 't':
132 options.print_types = true;
133 break;
134 case 'T':
135 options.print_type_keys = true;
136 break;
137 case 'w':
138 options.print_walls = true;
139 break;
140 case '@':
141 options.print_tunnels = true;
142 break;
143 default:
144 error (0, 0, "Unknown --print selection '%c'.", *flags);
145 usage (1);
146 }
147 ++flags;
148 }
149 }
150
151 static void
decode_switches(int argc,char ** argv)152 decode_switches (int argc, char **argv)
153 {
154 int c;
155 /* If DEFAULT_PRINT is still true after processing all the options,
156 that means the default printing options should be used. */
157 bool default_print = true;
158
159 while ((c = getopt_long (argc, argv, "hip:v", long_options, NULL)) != -1) {
160 switch (c) {
161 case 'h':
162 usage (0);
163 case 'v':
164 version ();
165 case 'i':
166 options.indent = " ";
167 break;
168 case 'p':
169 decode_print_options (optarg);
170 default_print = false;
171 break;
172 default:
173 usage (1);
174 }
175 }
176
177 if (default_print)
178 default_options ();
179 }
180
181 static const char *
dir_to_string(a_dir dir)182 dir_to_string (a_dir dir)
183 {
184 switch (dir) {
185 case D_UP:
186 return "up";
187 case D_RIGHT:
188 return "right";
189 case D_DOWN:
190 return "down";
191 case D_LEFT:
192 return "left";
193 }
194 exit_status = 4;
195 return "error";
196 }
197
198 static void
print_header(const a_level * lvl)199 print_header (const a_level *lvl)
200 {
201 printf (_("%sheight:\t%d tiles\t(%d squares)\n"),
202 options.indent, lvl->tile_height, lvl->square_height);
203 printf (_("%swidth:\t%d tiles\t(%d squares)\n"),
204 options.indent, lvl->tile_width, lvl->square_width);
205 if (lvl->tile_height_wrap == DONT_WRAP)
206 printf (_("%sY-wrap:\tno\n"), options.indent);
207 else
208 printf (_("%sY-wrap:\t%d tiles\t(%d squares)\n"),
209 options.indent, lvl->tile_height_wrap, lvl->square_height_wrap);
210 if (lvl->tile_width_wrap == DONT_WRAP)
211 printf (_("%sX-wrap:\tno\n"), options.indent);
212 else
213 printf (_("%sX-wrap:\t%d tiles\t(%d squares)\n"),
214 options.indent, lvl->tile_width_wrap, lvl->square_width_wrap);
215 printf (_("%ssound track alias:\t%s\n"),
216 options.indent, lvl_sound_track (lvl));
217 printf (_("%stile map basename:\t%s\n"),
218 options.indent, lvl_tile_sprite_map_basename (lvl));
219 printf (_("%sstarting squares and directions (y x dir):\n"), options.indent);
220 {
221 int i;
222 for (i = 0; i < 4; ++i) {
223 a_square_corrd_pair co;
224 a_dir di;
225 lvl_start_position (lvl, i, &co, &di);
226 printf ("%s #%d: %2d %2d %s\n", options.indent, i + 1,
227 co.y, co.x, dir_to_string (di));
228 }
229 }
230 }
231
232 static char
type_to_char(a_dir dir)233 type_to_char (a_dir dir)
234 {
235 switch (dir) {
236 case T_NONE:
237 return ' ';
238 case T_STOP:
239 return 'h';
240 case T_SPEED:
241 return 's';
242 case T_TUNNEL:
243 return 't';
244 case T_BOOM:
245 return 'b';
246 case T_ANIM:
247 return 'a';
248 case T_ICE:
249 return 'i';
250 case T_DUST:
251 return 'd';
252 case T_OUTWAY:
253 return 'X';
254 default:
255 exit_status = 4;
256 return '?';
257 }
258 }
259
260 const char *type_names[T_MAXTYPE] = {
261 N_("none"),
262 N_("stop"),
263 N_("speed"),
264 N_("tunnel"),
265 N_("boom"),
266 N_("anim"),
267 N_("ice"),
268 N_("dust"),
269 N_("outway")
270 };
271
272 static void
print_type_keys(void)273 print_type_keys (void)
274 {
275 int i;
276 const int ncols = 3;
277
278 for (i = 0; i < T_MAXTYPE; ++i) {
279 if (i % ncols == 0)
280 printf ("%s", options.indent);
281 printf ("'%c' %-20s", type_to_char (i), _(type_names[i]));
282 if (i % ncols == ncols - 1)
283 puts ("");
284 }
285 if (i % ncols != 0)
286 puts ("");
287 }
288
289 static void
print_square_types(const a_level * lvl)290 print_square_types (const a_level *lvl)
291 {
292 a_square_coord y, x;
293 a_square_index idx;
294 for (y = 0, idx = 0; y < lvl->square_height; ++y) {
295 printf ("%s|", options.indent);
296 for (x = 0; x < lvl->square_width; ++x, ++idx)
297 putchar (type_to_char (lvl->square_type[idx]));
298 printf ("|%2u\n", y);
299 }
300 }
301
302 static void
print_dir_mask(a_dir_mask dm)303 print_dir_mask (a_dir_mask dm)
304 {
305 int nw = 0;
306
307 if (dm & DM_UP)
308 ++nw;
309 if (dm & DM_RIGHT)
310 ++nw;
311 if (dm & DM_DOWN)
312 ++nw;
313 if (dm & DM_LEFT)
314 ++nw;
315
316 if (nw)
317 printf ("%d", nw);
318 else
319 putchar (' ');
320 }
321
322 static void
print_square_walls(const a_level * lvl)323 print_square_walls (const a_level *lvl)
324 {
325 a_square_coord y, x;
326 a_square_index idx;
327 for (y = 0, idx = 0; y < lvl->square_height; ++y) {
328 printf ("%s|", options.indent);
329 for (x = 0; x < lvl->square_width; ++x, ++idx)
330 print_dir_mask (lvl->square_walls_out[idx]);
331 printf ("|%2u\n", y);
332 }
333 }
334
335 static char
dir_to_char(a_dir d)336 dir_to_char (a_dir d)
337 {
338 switch (d) {
339 case D_UP:
340 return '^';
341 case D_RIGHT:
342 return '>';
343 case D_DOWN:
344 return '.';
345 case D_LEFT:
346 return '<';
347 default:
348 return 'X';
349 }
350 }
351
352 static void
print_square_directions(const a_level * lvl)353 print_square_directions (const a_level *lvl)
354 {
355 a_square_coord y, x;
356 a_square_index idx;
357 for (y = 0, idx = 0; y < lvl->square_height; ++y) {
358 printf ("%s|", options.indent);
359 for (x = 0; x < lvl->square_width; ++x, ++idx)
360 putchar (dir_to_char (lvl->square_direction[idx]));
361 printf ("|%2u\n", y);
362 }
363 }
364
365 static void
print_tunnels(const a_level * lvl)366 print_tunnels (const a_level *lvl)
367 {
368 a_square_coord y, x;
369 a_square_index idx;
370 int tunbr = 0; /* Number of tunnels. */
371 int curtun = 0; /* Curent tunnel index. */
372 a_square_index *outputs;
373
374 for (idx = 0; idx < lvl->square_count; ++idx)
375 if (lvl->square_type[idx] == T_TUNNEL)
376 ++tunbr;
377
378 if (tunbr == 0) {
379 printf ("%sNo tunnels.\n", options.indent);
380 return;
381 }
382
383 XMALLOC_ARRAY (outputs, tunbr);
384
385 for (curtun = 0, idx = 0; idx < lvl->square_count; ++idx)
386 if (lvl->square_type[idx] == T_TUNNEL)
387 outputs[curtun++] = idx;
388
389 printf (_("%sTunnels:\n"), options.indent);
390 /* TRANS: This is the header of an array (output by heroeslvl -p@),
391 so the position of these words is important.
392
393 SRCIDX is a short for 'source index' (entrance of a tunnel) and
394 DESTIDX means 'destination index' (output of the tunnel). */
395 printf (_("%s NBR SRCIDX Y X DIR DESTIDX\n"), options.indent);
396
397 for (curtun = 0, y = 0, idx = 0; y < lvl->square_height; ++y)
398 for (x = 0; x < lvl->square_width; ++x, ++idx)
399 if (lvl->square_type[idx] == T_TUNNEL) {
400 a_dir d = lvl->square_direction[idx];
401 a_square_index outidx = lvl->square_move[d][idx];
402
403 /* Search the number of the output tunnel. */
404 int i;
405 for (i = 0; i < tunbr; ++i)
406 if (outputs[i] == outidx)
407 break;
408
409 printf ("%s #%-2d %6d %3d %3d %-8s %7d",
410 options.indent, ++curtun, idx, y, x,
411 dir_to_string (d), lvl->square_move[d][idx]);
412 if (i < tunbr)
413 printf ("\t#%d", i + 1);
414 puts ("");
415 }
416
417 free (outputs);
418 }
419
420 static const char *
anim_kind_to_str(an_anim_kind k)421 anim_kind_to_str (an_anim_kind k)
422 {
423 switch (k) {
424 case A_NONE:
425 /* TRANS: `still' is used to describe tiles which are not animated. */
426 return _("still");
427 case A_LOOP:
428 /* TRANS: `loop' is for animated tiles where frames are displayed
429 from 1 to n, and then again from 1 to n, etc. */
430 return _("loop");
431 case A_PINGPONG:
432 /* TRANS: `pingpong' is for tiles animated in loop where frame
433 displayed from 1 to n, then from n to 1, and then again from 1
434 to n, etc. */
435 return _("pingpong");
436 }
437 assert (0);
438 }
439
440 static void
print_tile_details(a_level * lvl)441 print_tile_details (a_level *lvl)
442 {
443 a_tile_index i;
444
445 /* TRANS: This is the header of an array (output by heroeslvl -pi) so
446 the position of these word is important.
447
448 SPRITE is the address of the sprite used to render the tile. OVERLAY
449 is the address of a sprite that should be displayed on top
450 of the players (e.g. trees). ANIM-TYPE is the kind of animation used
451 for the tile, FRM the number of frame to display, DEL the delay
452 between each frame. */
453 printf (_("\
454 %sTILE Y X TYPE SPRITE OVERLAY ANIM-TYPE FRM DEL\n"),
455 options.indent);
456 for (i = 0; i < lvl->tile_count; ++i) {
457 unsigned int o, c, d;
458 an_anim_kind k;
459
460 printf ("%s%4u %2u %2u %-10s 0x%08x", options.indent, i,
461 TILE_INDEX_TO_COORD_Y (lvl, i),
462 TILE_INDEX_TO_COORD_X (lvl, i),
463 type_names[lvl_tile_type (lvl, i)],
464 lvl_tile_sprite_offset (lvl, i));
465
466 o = lvl_tile_sprite_overlay_offset (lvl, i);
467 if (o)
468 printf (" 0x%08x", o);
469 else
470 printf (" ");
471
472 lvl_animation_info (lvl, i, &c, &d, &k);
473 printf (" %-10s", anim_kind_to_str (k));
474 if (k)
475 printf (" %2u %2u", c, d);
476 else
477 printf (" ");
478
479 puts ("");
480 }
481 }
482
483 static void
process(const char * filename)484 process (const char *filename)
485 {
486 int err;
487 a_level lvl;
488 bool load_full;
489
490 /* Sometime we don't need to load the full level, only the header is
491 needed to print the requested information. */
492 if (options.print_types
493 | options.print_walls
494 | options.print_directions
495 | options.print_tunnels
496 | options.print_tile_details)
497 load_full = true;
498 else
499 load_full = false;
500
501 err = lvl_load_file (filename, &lvl, load_full);
502 if (err) {
503 error (0, err, _("cannot load %s"), filename);
504 exit_status = 3;
505 } else {
506 if (options.print_filename)
507 printf (_("File:\t%s\n"), filename);
508 if (options.print_header)
509 print_header (&lvl);
510 if (options.print_type_keys)
511 print_type_keys ();
512 if (options.print_types)
513 print_square_types (&lvl);
514 if (options.print_walls)
515 print_square_walls (&lvl);
516 if (options.print_directions)
517 print_square_directions (&lvl);
518 if (options.print_tunnels)
519 print_tunnels (&lvl);
520 if (options.print_tile_details)
521 print_tile_details (&lvl);
522 }
523 lvl_free (&lvl);
524 }
525
526 int
main(int argc,char * argv[])527 main (int argc, char *argv[])
528 {
529 mtrace (); /* GNU libc's malloc debugging facility */
530 program_name = argv[0];
531
532 setlocale (LC_ALL, "");
533 /* FIXME: Use $(locale-dir) as in Heroes. */
534 bindtextdomain (PACKAGE, PREFIX "/" FORWARD_RELATIVE_LOCALEDIR);
535 textdomain (PACKAGE);
536
537 decode_switches (argc, argv);
538 if (optind == argc) {
539 error (0, 0, _("Missing filename."));
540 usage (1);
541 }
542
543 /* Process all files. */
544 for (; optind < argc; ++optind) {
545 process (argv[optind]);
546 }
547
548 return exit_status;
549 }
550