1 /*------------------------------------------------------------------------.
2 | Copyright 1997, 1998, 2000  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 under |
7 | the terms of the GNU General Public License version 2 as published by   |
8 | the Free Software Foundation.                                           |
9 |                                                                         |
10 | Heroes is distributed in the hope that it will be useful, but WITHOUT   |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or   |
12 | FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License   |
13 | for more details.                                                       |
14 |                                                                         |
15 | You should have received a copy of the GNU General Public License along |
16 | with this program; if not, write to the Free Software Foundation, Inc., |
17 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                   |
18 `------------------------------------------------------------------------*/
19 
20 /*
21  *   Create the list of extra levels.
22  */
23 
24 #include "system.h"
25 #include "extras.h"
26 #include "generic_list.h"
27 #include "hedlite.h"
28 #include "misc.h"
29 #include "rsc_files.h"
30 #include "debugmsg.h"
31 #include "levellst.h"
32 
33 typedef struct {
34   a_filename	filename;
35   char		is_in_user_dir;	/* Is this extra level a user level?
36 				 (user levels come from the ~/.heroes/level/
37 				 directory) */
38 } an_extradir_info;
39 
40 static void free_extradir_info (an_extradir_info* ei);
41 
42 NEW_LIST (an_extradir, an_extradir_info*, STD_EQUAL, free_extradir_info);
43 
44 /* a_level_list is used for building a temporaly list of all extra
45    levels seen in directories.  The list will then be converted to an
46    array, by copying the pointed struct, therefore the destructor should
47    free only the struct, not the elements pointer by the struct's members. */
48 NEW_LIST (a_level, an_extra_level*, STD_EQUAL, free);
49 
50 an_extradir_list edir;
51 
52 unsigned int extra_nbr = 0;	/* the total number of extra levels */
53 unsigned int extra_user_nbr = 0; /* The number of user levels from */
54 
55 an_extra_level *extra_list = 0;	/* The list of extra-levels, the user's
56 				   extra-levels are at the beginning */
57 char *extra_selected_list = 0;	/* For each level: 1 if selected, 0 if not */
58 
59 
free_extradir_info(an_extradir_info * ei)60 static void free_extradir_info (an_extradir_info* ei)
61 {
62   free (ei->filename);
63   free (ei);
64 }
65 
66 /* compare two extra-levels for sorting,
67    we want to sort user's levels first, and then alphabetically */
68 static int
cmp_extralevels(const an_extra_level * l,const an_extra_level * r)69 cmp_extralevels (const an_extra_level* l, const an_extra_level* r)
70 {
71   int d = r->is_in_user_dir - l->is_in_user_dir;
72 
73   if (d != 0)
74     return d;
75   return strcasecmp (l->level_name, r->level_name);
76 }
77 
78 /* Browse a directory, adds the levels found to ll.
79    Update extra_nbr and extra_user_nbr. */
80 static void
browse_extra_directory(an_extradir_info * edi,a_level_list * ll)81 browse_extra_directory (an_extradir_info* edi, a_level_list* ll)
82 {
83   DIR* dir;
84   struct dirent* de;
85   int n = 0;
86 
87   dmsg (D_FILE, "browsing directory %s ...", edi->filename);
88 
89   dir = opendir (edi->filename);
90   if (!dir) {
91     /* Always output an error message when handling a user directory.
92        If not, we are probably reading a default extra directory,
93        maybe system wide configured or hard coded in the source;
94        it's best to assume this is not an error (this allow the
95        addition of extra levels as packages or such).  */
96     if (edi->is_in_user_dir)
97       perror (edi->filename);
98     dperror ("scandir");
99     return;
100   }
101 
102   while ((de = readdir (dir)))
103     if (select_file_lvl (de)) {
104       /* add the file to the list */
105       char* fn;
106       NEW (an_extra_level, tmp);
107 
108       XMALLOC_ARRAY (fn, (strlen (edi->filename) + 1 +
109 			  strlen (de->d_name) + 1));
110       tmp->level_name = xstrdup (de->d_name);
111       sprintf (fn, "%s/%s", edi->filename, tmp->level_name);
112       tmp->full_name = fn;
113       tmp->is_in_user_dir = edi->is_in_user_dir;
114 
115       strupr (tmp->level_name);
116       if ((fn = strchr (tmp->level_name, '.')))
117 	*fn = 0;
118 
119       a_level_push (ll, tmp);
120       ++n;
121     }
122 
123   closedir (dir);
124 
125   dmsg (D_FILE, "... %d files", n);
126 
127   extra_nbr +=n;
128   if (edi->is_in_user_dir)
129     extra_user_nbr += n;
130 }
131 
132 void
browse_extra_directories(void)133 browse_extra_directories (void)
134 {
135   an_extradir_list ed = edir;
136   a_level_list ll = 0;
137   a_level_list ll_cur;
138   unsigned int i;
139 
140   /* build the list of the files found in each directory */
141   while (ed) {
142     browse_extra_directory (ed->car, &ll);
143     ed = ed->cdr;
144   }
145 
146   /* return if no extra level was found */
147   if (!extra_nbr) {
148     XFREE0 (extra_list);
149     XFREE0 (extra_selected_list);
150     return;
151   }
152 
153   /* convert the list to an array (BTW, this array is called extra_list :)) */
154   XREALLOC_ARRAY (extra_list, extra_nbr);
155   for (i = 0, ll_cur = ll; ll_cur; ll_cur = ll_cur->cdr, ++i)
156     extra_list[i] = *(ll_cur->car);
157   assert (i == extra_nbr);
158 
159   /* ll is now useless */
160   a_level_clear (&ll);
161 
162   /* sort the files list */
163   qsort (extra_list, extra_nbr, sizeof(*extra_list),
164 	 (int (*)(const void*,const void*))cmp_extralevels);
165 
166   /* allocate the selection array */
167   XREALLOC_ARRAY (extra_selected_list, extra_nbr);
168   memset (extra_selected_list, 0, extra_nbr);
169 }
170 
171 void
add_extra_directory(a_filename fn)172 add_extra_directory (a_filename fn)
173 {
174   NEW (an_extradir_info, tmp);
175   tmp->filename = xstrdup (fn);
176   tmp->is_in_user_dir = 0;
177   an_extradir_push (&edir, tmp);
178 }
179 
180 static void
add_extra_in_user_directory(a_filename fn)181 add_extra_in_user_directory (a_filename fn)
182 {
183   NEW (an_extradir_info, tmp);
184   tmp->filename = xstrdup (fn);
185   tmp->is_in_user_dir = 1;
186   an_extradir_push (&edir, tmp);
187 }
188 
189 void
add_default_extra_directories(void)190 add_default_extra_directories (void)
191 {
192   char* t;
193   dmsg (D_SECTION, "setup default extra directory");
194   t = get_rsc_file ("extra-levels-dir");
195   if (t) {
196     add_extra_directory (t);
197     free (t);
198   }
199   if (!create_levels_output_dir ())
200     add_extra_in_user_directory (levels_output_dir);
201 }
202 
203 void
free_extra_list(void)204 free_extra_list (void)
205 {
206   unsigned int i;
207 
208   if (extra_nbr == 0)
209     return;
210 
211   dmsg (D_MISC, "freeing extra list");
212 
213   for (i = 0; i < extra_nbr; ++i) {
214     free (extra_list[i].full_name);
215     free (extra_list[i].level_name);
216   }
217   extra_nbr = 0;
218   extra_user_nbr = 0;
219   XFREE0 (extra_list);
220   XFREE0 (extra_selected_list);
221 }
222 
223 void
free_extra_directories(void)224 free_extra_directories (void)
225 {
226   dmsg (D_MISC, "free extra directories");
227   an_extradir_clear (&edir);
228 
229   free_levels_output_dir ();
230 }
231