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