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