Rev 3723 | Rev 4385 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1652 | terminx | 1 | //------------------------------------------------------------------------- |
2 | /* |
||
3 | Copyright (C) 2010 EDuke32 developers and contributors |
||
4 | |||
5 | This file is part of EDuke32. |
||
6 | |||
7 | EDuke32 is free software; you can redistribute it and/or |
||
8 | modify it under the terms of the GNU General Public License version 2 |
||
9 | as published by the Free Software Foundation. |
||
10 | |||
11 | This program is distributed in the hope that it will be useful, |
||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
||
14 | |||
15 | See the GNU General Public License for more details. |
||
16 | |||
17 | You should have received a copy of the GNU General Public License |
||
18 | along with this program; if not, write to the Free Software |
||
19 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
||
20 | */ |
||
21 | //------------------------------------------------------------------------- |
||
22 | |||
241 | terminx | 23 | #include "compat.h" |
24 | #include "baselayer.h" |
||
25 | |||
26 | #include "scriptfile.h" |
||
27 | #include "cache1d.h" |
||
28 | #include "crc32.h" |
||
29 | |||
30 | #include "duke3d.h" |
||
2726 | hendricks2 | 31 | #include "common_game.h" |
241 | terminx | 32 | #include "grpscan.h" |
33 | |||
3708 | terminx | 34 | // custom GRP support for the startup window, file format reflects the structure below |
35 | #define GAMELISTFILE "games.list" |
||
3803 | terminx | 36 | // name crc size flags dependency scriptname defname |
3708 | terminx | 37 | struct grpfile internalgrpfiles[NUMGRPFILES] = |
559 | terminx | 38 | { |
3803 | terminx | 39 | { "Duke Nukem 3D", DUKE13_CRC, 26524524, GAMEFLAG_DUKE, 0, NULL, NULL, NULL }, |
40 | { "Duke Nukem 3D (South Korean Censored)", DUKEKR_CRC, 26385383, GAMEFLAG_DUKE, 0, NULL, NULL, NULL }, |
||
41 | { "Duke Nukem 3D: Atomic Edition", DUKE15_CRC, 44356548, GAMEFLAG_DUKE, 0, NULL, NULL, NULL }, |
||
42 | { "Duke Nukem 3D: Plutonium Pak", DUKEPP_CRC, 44348015, GAMEFLAG_DUKE, 0, NULL, NULL, NULL }, |
||
43 | { "Duke Nukem 3D Shareware 0.99", DUKE099_CRC, 9690241, GAMEFLAG_DUKE|GAMEFLAG_DUKEBETA, 0, NULL, NULL, NULL }, |
||
44 | { "Duke Nukem 3D Shareware 1.0", DUKE10_CRC, 10429258, GAMEFLAG_DUKE|GAMEFLAG_SHAREWARE, 0, NULL, NULL, NULL }, |
||
45 | { "Duke Nukem 3D Shareware 1.1", DUKE11_CRC, 10442980, GAMEFLAG_DUKE|GAMEFLAG_SHAREWARE, 0, NULL, NULL, NULL }, |
||
46 | { "Duke Nukem 3D Shareware 1.3D", DUKESW_CRC, 11035779, GAMEFLAG_DUKE|GAMEFLAG_SHAREWARE, 0, NULL, NULL, NULL }, |
||
47 | { "Duke Nukem 3D Mac Demo", DUKEMD_CRC, 10444391, GAMEFLAG_DUKE|GAMEFLAG_SHAREWARE, 0, NULL, NULL, NULL }, |
||
48 | { "Duke it out in D.C.", DUKEDC_CRC, 8410183 , GAMEFLAG_DUKE|GAMEFLAG_ADDON, DUKE15_CRC, NULL, NULL, NULL }, |
||
49 | { "Duke Caribbean: Life's a Beach", DUKECB_CRC, 22213819, GAMEFLAG_DUKE|GAMEFLAG_ADDON, DUKE15_CRC, NULL, NULL, NULL }, |
||
50 | { "Duke: Nuclear Winter", DUKENW_CRC, 16169365, GAMEFLAG_DUKE|GAMEFLAG_ADDON, DUKE15_CRC, "nwinter.con", NULL, NULL }, |
||
51 | { "NAM", NAM_CRC, 43448927, GAMEFLAG_NAM, 0, "nam.con", "nam.def", NULL }, |
||
52 | { "NAPALM", NAPALM_CRC, 44365728, GAMEFLAG_NAM|GAMEFLAG_NAPALM, 0, "napalm.con", "nam.def", NULL }, |
||
53 | { "WWII GI", WW2GI_CRC, 77939508, GAMEFLAG_WW2GI|GAMEFLAG_NAM, 0, "ww2gi.con", "ww2gi.def", NULL }, |
||
559 | terminx | 54 | }; |
241 | terminx | 55 | struct grpfile *foundgrps = NULL; |
3708 | terminx | 56 | struct grpfile *listgrps = NULL; |
241 | terminx | 57 | |
3708 | terminx | 58 | static void LoadList(const char * filename) |
59 | { |
||
60 | struct grpfile *fg; |
||
61 | |||
62 | char *grpend = NULL; |
||
63 | |||
64 | scriptfile *script = scriptfile_fromfile(filename); |
||
65 | |||
66 | if (!script) |
||
67 | return; |
||
68 | |||
69 | scriptfile_addsymbolvalue("GAMEFLAG_DUKE", GAMEFLAG_DUKE); |
||
70 | scriptfile_addsymbolvalue("GAMEFLAG_ADDON", GAMEFLAG_DUKE|GAMEFLAG_ADDON); |
||
71 | scriptfile_addsymbolvalue("DUKE15_CRC", DUKE15_CRC); |
||
72 | scriptfile_addsymbolvalue("DUKE13_CRC", DUKE13_CRC); |
||
73 | scriptfile_addsymbolvalue("DUKEDC_CRC", DUKEDC_CRC); |
||
74 | scriptfile_addsymbolvalue("DUKECB_CRC", DUKECB_CRC); |
||
75 | scriptfile_addsymbolvalue("DUKENW_CRC", DUKENW_CRC); |
||
76 | |||
3711 | helixhorne | 77 | while (!scriptfile_eof(script)) |
3708 | terminx | 78 | { |
3711 | helixhorne | 79 | enum |
80 | { |
||
81 | T_GRPINFO, |
||
82 | T_GAMENAME, |
||
83 | T_CRC, |
||
84 | T_SIZE, |
||
85 | T_DEPCRC, |
||
86 | T_SCRIPTNAME, |
||
87 | T_DEFNAME, |
||
88 | T_FLAGS, |
||
89 | }; |
||
3708 | terminx | 90 | |
3711 | helixhorne | 91 | static const tokenlist profiletokens[] = |
92 | { |
||
93 | { "grpinfo", T_GRPINFO }, |
||
94 | }; |
||
3708 | terminx | 95 | |
96 | int32_t token = getatoken(script,profiletokens,sizeof(profiletokens)/sizeof(tokenlist)); |
||
97 | switch (token) |
||
98 | { |
||
99 | case T_GRPINFO: |
||
3711 | helixhorne | 100 | { |
101 | int32_t gsize = 0, gcrcval = 0, gflags = GAMEFLAG_DUKE, gdepcrc = DUKE15_CRC; |
||
102 | char *gname = NULL, *gscript = NULL, *gdef = NULL; |
||
103 | |||
104 | static const tokenlist grpinfotokens[] = |
||
3708 | terminx | 105 | { |
3711 | helixhorne | 106 | { "name", T_GAMENAME }, |
107 | { "scriptname", T_SCRIPTNAME }, |
||
108 | { "defname", T_DEFNAME }, |
||
109 | { "crc", T_CRC }, |
||
110 | { "dependency", T_DEPCRC }, |
||
111 | { "size", T_SIZE }, |
||
112 | { "flags", T_FLAGS }, |
||
3708 | terminx | 113 | |
3711 | helixhorne | 114 | }; |
3708 | terminx | 115 | |
3711 | helixhorne | 116 | if (scriptfile_getbraces(script,&grpend)) break; |
3708 | terminx | 117 | |
3711 | helixhorne | 118 | while (script->textptr < grpend) |
119 | { |
||
120 | int32_t token = getatoken(script,grpinfotokens,sizeof(grpinfotokens)/sizeof(tokenlist)); |
||
3708 | terminx | 121 | |
3711 | helixhorne | 122 | switch (token) |
3708 | terminx | 123 | { |
3711 | helixhorne | 124 | case T_GAMENAME: |
125 | scriptfile_getstring(script,&gname); break; |
||
126 | case T_SCRIPTNAME: |
||
127 | scriptfile_getstring(script,&gscript); break; |
||
128 | case T_DEFNAME: |
||
129 | scriptfile_getstring(script,&gdef); break; |
||
3708 | terminx | 130 | |
3711 | helixhorne | 131 | case T_FLAGS: |
132 | scriptfile_getsymbol(script,&gflags); break; |
||
133 | case T_DEPCRC: |
||
134 | scriptfile_getsymbol(script,&gdepcrc); break; |
||
135 | case T_CRC: |
||
136 | scriptfile_getsymbol(script,&gcrcval); break; |
||
137 | case T_SIZE: |
||
138 | scriptfile_getnumber(script,&gsize); break; |
||
139 | default: |
||
140 | break; |
||
141 | } |
||
3708 | terminx | 142 | |
3711 | helixhorne | 143 | fg = (struct grpfile *)Bcalloc(1, sizeof(struct grpfile)); |
144 | fg->next = listgrps; |
||
145 | listgrps = fg; |
||
3708 | terminx | 146 | |
3711 | helixhorne | 147 | if (gname) |
148 | fg->name = Bstrdup(gname); |
||
3708 | terminx | 149 | |
3711 | helixhorne | 150 | fg->size = gsize; |
151 | fg->crcval = gcrcval; |
||
152 | fg->dependency = gdepcrc; |
||
153 | fg->game = gflags; |
||
3708 | terminx | 154 | |
3711 | helixhorne | 155 | if (gscript) |
156 | fg->scriptname = dup_filename(gscript); |
||
3708 | terminx | 157 | |
3711 | helixhorne | 158 | if (gdef) |
159 | fg->defname = dup_filename(gdef); |
||
3708 | terminx | 160 | } |
3711 | helixhorne | 161 | break; |
162 | } |
||
3708 | terminx | 163 | |
164 | default: |
||
165 | break; |
||
166 | } |
||
167 | } |
||
168 | |||
169 | scriptfile_close(script); |
||
170 | scriptfile_clearsymbols(); |
||
171 | } |
||
172 | |||
173 | static void LoadGameList(void) |
||
174 | { |
||
175 | struct grpfile *fg; |
||
176 | CACHE1D_FIND_REC *srch, *sidx; |
||
177 | |||
178 | int32_t i; |
||
179 | |||
180 | for (i = 0; i<NUMGRPFILES; i++) |
||
181 | { |
||
182 | fg = (struct grpfile *)Bcalloc(1, sizeof(struct grpfile)); |
||
183 | |||
184 | fg->name = Bstrdup(internalgrpfiles[i].name); |
||
185 | fg->crcval = internalgrpfiles[i].crcval; |
||
186 | fg->size = internalgrpfiles[i].size; |
||
187 | fg->game = internalgrpfiles[i].game; |
||
188 | fg->dependency = internalgrpfiles[i].dependency; |
||
189 | |||
190 | if (internalgrpfiles[i].scriptname) |
||
191 | fg->scriptname = dup_filename(internalgrpfiles[i].scriptname); |
||
192 | |||
193 | if (internalgrpfiles[i].defname) |
||
194 | fg->defname = dup_filename(internalgrpfiles[i].defname); |
||
195 | |||
196 | fg->next = listgrps; |
||
197 | listgrps = fg; |
||
198 | } |
||
199 | |||
200 | srch = klistpath("/", "*.grpinfo", CACHE1D_FIND_FILE); |
||
201 | |||
202 | for (sidx = srch; sidx; sidx = sidx->next) |
||
203 | LoadList(srch->name); |
||
204 | |||
205 | klistfree(srch); |
||
206 | } |
||
207 | |||
208 | static void FreeGameList(void) |
||
209 | { |
||
210 | struct grpfile *fg; |
||
211 | |||
212 | while (listgrps) |
||
213 | { |
||
214 | fg = listgrps->next; |
||
215 | Bfree(listgrps->name); |
||
216 | |||
217 | if (listgrps->scriptname) |
||
218 | Bfree(listgrps->scriptname); |
||
219 | |||
220 | if (listgrps->defname) |
||
221 | Bfree(listgrps->defname); |
||
222 | |||
223 | Bfree(listgrps); |
||
224 | listgrps = fg; |
||
225 | } |
||
226 | } |
||
227 | |||
228 | |||
241 | terminx | 229 | #define GRPCACHEFILE "grpfiles.cache" |
335 | terminx | 230 | static struct grpcache |
231 | { |
||
241 | terminx | 232 | struct grpcache *next; |
1205 | terminx | 233 | int32_t size; |
234 | int32_t mtime; |
||
235 | int32_t crcval; |
||
1457 | terminx | 236 | char name[BMAX_PATH]; |
335 | terminx | 237 | } |
238 | *grpcache = NULL, *usedgrpcache = NULL; |
||
241 | terminx | 239 | |
1205 | terminx | 240 | static int32_t LoadGroupsCache(void) |
241 | terminx | 241 | { |
242 | struct grpcache *fg; |
||
243 | |||
1205 | terminx | 244 | int32_t fsize, fmtime, fcrcval; |
241 | terminx | 245 | char *fname; |
246 | |||
247 | scriptfile *script; |
||
248 | |||
249 | script = scriptfile_fromfile(GRPCACHEFILE); |
||
250 | if (!script) return -1; |
||
251 | |||
335 | terminx | 252 | while (!scriptfile_eof(script)) |
253 | { |
||
2726 | hendricks2 | 254 | if (scriptfile_getstring(script, &fname)) break; // filename |
255 | if (scriptfile_getnumber(script, &fsize)) break; // filesize |
||
256 | if (scriptfile_getnumber(script, &fmtime)) break; // modification time |
||
257 | if (scriptfile_getnumber(script, &fcrcval)) break; // crc checksum |
||
241 | terminx | 258 | |
3176 | helixhorne | 259 | fg = (struct grpcache *)Bcalloc(1, sizeof(struct grpcache)); |
241 | terminx | 260 | fg->next = grpcache; |
261 | grpcache = fg; |
||
262 | |||
1221 | terminx | 263 | Bstrncpy(fg->name, fname, BMAX_PATH); |
241 | terminx | 264 | fg->size = fsize; |
265 | fg->mtime = fmtime; |
||
266 | fg->crcval = fcrcval; |
||
267 | } |
||
268 | |||
269 | scriptfile_close(script); |
||
270 | return 0; |
||
271 | } |
||
272 | |||
273 | static void FreeGroupsCache(void) |
||
274 | { |
||
275 | struct grpcache *fg; |
||
276 | |||
335 | terminx | 277 | while (grpcache) |
278 | { |
||
241 | terminx | 279 | fg = grpcache->next; |
1527 | terminx | 280 | Bfree(grpcache); |
241 | terminx | 281 | grpcache = fg; |
282 | } |
||
283 | } |
||
284 | |||
3654 | terminx | 285 | void RemoveGroup(int32_t crcval) |
286 | { |
||
3661 | terminx | 287 | struct grpfile *grp; |
3654 | terminx | 288 | |
289 | for (grp = foundgrps; grp; grp=grp->next) |
||
290 | { |
||
291 | if (grp->crcval == crcval) |
||
292 | { |
||
293 | if (grp == foundgrps) |
||
294 | foundgrps = grp->next; |
||
3661 | terminx | 295 | else |
296 | { |
||
297 | struct grpfile *fg; |
||
3654 | terminx | 298 | |
3661 | terminx | 299 | for (fg = foundgrps; fg; fg=fg->next) |
300 | { |
||
301 | if (fg->next == grp) |
||
302 | { |
||
303 | fg->next = grp->next; |
||
304 | break; |
||
305 | } |
||
306 | } |
||
307 | } |
||
308 | |||
3654 | terminx | 309 | Bfree((char *)grp->name); |
310 | Bfree(grp); |
||
311 | |||
3661 | terminx | 312 | RemoveGroup(crcval); |
313 | |||
3654 | terminx | 314 | break; |
315 | } |
||
316 | } |
||
317 | } |
||
318 | |||
319 | struct grpfile * FindGroup(int32_t crcval) |
||
320 | { |
||
321 | struct grpfile *grp; |
||
322 | |||
323 | for (grp = foundgrps; grp; grp=grp->next) |
||
324 | { |
||
325 | if (grp->crcval == crcval) |
||
326 | return grp; |
||
327 | } |
||
328 | |||
329 | return NULL; |
||
330 | } |
||
331 | |||
1205 | terminx | 332 | int32_t ScanGroups(void) |
241 | terminx | 333 | { |
334 | CACHE1D_FIND_REC *srch, *sidx; |
||
335 | struct grpcache *fg, *fgg; |
||
336 | struct grpfile *grp; |
||
337 | char *fn; |
||
338 | struct Bstat st; |
||
1430 | terminx | 339 | #define BUFFER_SIZE (1024 * 1024 * 8) |
3176 | helixhorne | 340 | uint8_t *buf = (uint8_t *)Bmalloc(BUFFER_SIZE); |
241 | terminx | 341 | |
1431 | terminx | 342 | if (!buf) |
1430 | terminx | 343 | { |
344 | initprintf("Error allocating %d byte buffer to scan GRPs!\n", BUFFER_SIZE); |
||
345 | return 0; |
||
346 | } |
||
347 | |||
1643 | terminx | 348 | initprintf("Searching for game data...\n"); |
241 | terminx | 349 | |
3708 | terminx | 350 | LoadGameList(); |
241 | terminx | 351 | LoadGroupsCache(); |
352 | |||
353 | srch = klistpath("/", "*.grp", CACHE1D_FIND_FILE); |
||
354 | |||
335 | terminx | 355 | for (sidx = srch; sidx; sidx = sidx->next) |
356 | { |
||
357 | for (fg = grpcache; fg; fg = fg->next) |
||
358 | { |
||
241 | terminx | 359 | if (!Bstrcmp(fg->name, sidx->name)) break; |
360 | } |
||
361 | |||
335 | terminx | 362 | if (fg) |
363 | { |
||
2726 | hendricks2 | 364 | if (findfrompath(sidx->name, &fn)) continue; // failed to resolve the filename |
335 | terminx | 365 | if (Bstat(fn, &st)) |
366 | { |
||
1527 | terminx | 367 | Bfree(fn); |
335 | terminx | 368 | continue; |
2726 | hendricks2 | 369 | } // failed to stat the file |
1527 | terminx | 370 | Bfree(fn); |
335 | terminx | 371 | if (fg->size == st.st_size && fg->mtime == st.st_mtime) |
372 | { |
||
808 | terminx | 373 | grp = (struct grpfile *)Bcalloc(1, sizeof(struct grpfile)); |
1527 | terminx | 374 | grp->name = Bstrdup(sidx->name); |
241 | terminx | 375 | grp->crcval = fg->crcval; |
376 | grp->size = fg->size; |
||
377 | grp->next = foundgrps; |
||
378 | foundgrps = grp; |
||
379 | |||
808 | terminx | 380 | fgg = (struct grpcache *)Bcalloc(1, sizeof(struct grpcache)); |
241 | terminx | 381 | strcpy(fgg->name, fg->name); |
382 | fgg->size = fg->size; |
||
383 | fgg->mtime = fg->mtime; |
||
384 | fgg->crcval = fg->crcval; |
||
385 | fgg->next = usedgrpcache; |
||
386 | usedgrpcache = fgg; |
||
387 | continue; |
||
388 | } |
||
389 | } |
||
390 | |||
391 | { |
||
1205 | terminx | 392 | int32_t b, fh; |
393 | int32_t crcval; |
||
241 | terminx | 394 | |
395 | fh = openfrompath(sidx->name, BO_RDONLY|BO_BINARY, BS_IREAD); |
||
396 | if (fh < 0) continue; |
||
3171 | helixhorne | 397 | if (Bfstat(fh, &st)) continue; |
241 | terminx | 398 | |
399 | initprintf(" Checksumming %s...", sidx->name); |
||
1205 | terminx | 400 | crc32init((uint32_t *)&crcval); |
335 | terminx | 401 | do |
402 | { |
||
1430 | terminx | 403 | b = read(fh, buf, BUFFER_SIZE); |
1205 | terminx | 404 | if (b > 0) crc32block((uint32_t *)&crcval, (uint8_t *)buf, b); |
335 | terminx | 405 | } |
1430 | terminx | 406 | while (b == BUFFER_SIZE); |
1205 | terminx | 407 | crc32finish((uint32_t *)&crcval); |
241 | terminx | 408 | close(fh); |
409 | initprintf(" Done\n"); |
||
410 | |||
808 | terminx | 411 | grp = (struct grpfile *)Bcalloc(1, sizeof(struct grpfile)); |
1527 | terminx | 412 | grp->name = Bstrdup(sidx->name); |
241 | terminx | 413 | grp->crcval = crcval; |
414 | grp->size = st.st_size; |
||
415 | grp->next = foundgrps; |
||
416 | foundgrps = grp; |
||
417 | |||
808 | terminx | 418 | fgg = (struct grpcache *)Bcalloc(1, sizeof(struct grpcache)); |
1221 | terminx | 419 | Bstrncpy(fgg->name, sidx->name, BMAX_PATH); |
241 | terminx | 420 | fgg->size = st.st_size; |
421 | fgg->mtime = st.st_mtime; |
||
422 | fgg->crcval = crcval; |
||
423 | fgg->next = usedgrpcache; |
||
424 | usedgrpcache = fgg; |
||
425 | } |
||
426 | } |
||
427 | |||
428 | klistfree(srch); |
||
429 | FreeGroupsCache(); |
||
430 | |||
3654 | terminx | 431 | for (grp = foundgrps; grp; /*grp=grp->next*/) |
432 | { |
||
3708 | terminx | 433 | struct grpfile *igrp; |
434 | for (igrp = listgrps; igrp; igrp=igrp->next) |
||
435 | if (grp->crcval == igrp->crcval) break; |
||
3654 | terminx | 436 | |
3708 | terminx | 437 | if (igrp == NULL) |
438 | { |
||
439 | grp = grp->next; |
||
440 | continue; |
||
441 | } |
||
3654 | terminx | 442 | |
3708 | terminx | 443 | if (igrp->dependency) |
3654 | terminx | 444 | { |
3723 | terminx | 445 | struct grpfile *depgrp; |
446 | |||
3654 | terminx | 447 | //initprintf("found grp with dep\n"); |
3723 | terminx | 448 | for (depgrp = foundgrps; depgrp; depgrp=depgrp->next) |
449 | if (depgrp->crcval == igrp->dependency) break; |
||
3654 | terminx | 450 | |
3723 | terminx | 451 | if (depgrp == NULL || depgrp->crcval != igrp->dependency) // couldn't find dependency |
3654 | terminx | 452 | { |
453 | //initprintf("removing %s\n", grp->name); |
||
3708 | terminx | 454 | RemoveGroup(igrp->crcval); |
3654 | terminx | 455 | grp = foundgrps; |
456 | continue; |
||
457 | } |
||
458 | } |
||
459 | |||
3803 | terminx | 460 | if (igrp->game && !grp->game) |
461 | grp->game = igrp->game; |
||
462 | |||
3654 | terminx | 463 | grp=grp->next; |
464 | } |
||
465 | |||
335 | terminx | 466 | if (usedgrpcache) |
467 | { |
||
1205 | terminx | 468 | int32_t i = 0; |
241 | terminx | 469 | FILE *fp; |
470 | fp = fopen(GRPCACHEFILE, "wt"); |
||
335 | terminx | 471 | if (fp) |
472 | { |
||
473 | for (fg = usedgrpcache; fg; fg=fgg) |
||
474 | { |
||
241 | terminx | 475 | fgg = fg->next; |
476 | fprintf(fp, "\"%s\" %d %d %d\n", fg->name, fg->size, fg->mtime, fg->crcval); |
||
1527 | terminx | 477 | Bfree(fg); |
1000 | terminx | 478 | i++; |
241 | terminx | 479 | } |
480 | fclose(fp); |
||
481 | } |
||
1178 | terminx | 482 | // initprintf("Found %d recognized GRP %s.\n",i,i>1?"files":"file"); |
2978 | helixhorne | 483 | |
484 | Bfree(buf); |
||
1000 | terminx | 485 | return 0; |
241 | terminx | 486 | } |
1642 | terminx | 487 | |
488 | initprintf("Found no recognized game data!\n"); |
||
489 | |||
2978 | helixhorne | 490 | Bfree(buf); |
241 | terminx | 491 | return 0; |
492 | } |
||
493 | |||
3654 | terminx | 494 | |
241 | terminx | 495 | void FreeGroups(void) |
496 | { |
||
497 | struct grpfile *fg; |
||
498 | |||
335 | terminx | 499 | while (foundgrps) |
500 | { |
||
241 | terminx | 501 | fg = foundgrps->next; |
1677 | terminx | 502 | Bfree((char *)foundgrps->name); |
1527 | terminx | 503 | Bfree(foundgrps); |
241 | terminx | 504 | foundgrps = fg; |
505 | } |
||
3708 | terminx | 506 | |
507 | FreeGameList(); |
||
241 | terminx | 508 | } |
509 |