1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // libwad: Doom WAD format interface library.
5 // Copyright (C) 2011 by Callum Dickinson.
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //-----------------------------------------------------------------------------
17 /// \file wad.c
18 /// \brief WAD interface functionality.
19
20 #include "wad_static.h"
21
22 //
23 // Offset for the first lump's data is always 12 bytes.
24 //
25 #define LUMPOFFSET 12
26
27 //
28 // Maximum of wad files used at the same time.
29 // (There is a max of simultaneous open files anyway, and this should be plenty.)
30 //
31 #define MAX_WADFILES 48
32
33 wad_t *wads[MAX_WADFILES];
34 wad_uint32_t wad_numwads;
35
36 wad_uint32_t wad_oldnumwads;
37 wad_uint32_t wad_removednum;
38
WAD_ChangeWADNum(wad_int32_t i,wad_t * wad)39 static inline void WAD_ChangeWADNum(wad_int32_t i, wad_t *wad)
40 {
41 wads[i] = wad;
42 }
43
WAD_CacheWAD(wad_t * wad)44 static inline void WAD_CacheWAD(wad_t *wad)
45 {
46 WAD_ChangeWADNum(wad_numwads, wad);
47 wad_oldnumwads = wad_numwads;
48 wad_numwads++;
49 }
50
WAD_UncacheWAD(wad_t * wad)51 static void WAD_UncacheWAD(wad_t *wad)
52 {
53 wad_uint32_t i;
54
55 for (i = 0; i < wad_numwads; i++)
56 if (WAD_WADByNum(i) == wad)
57 break;
58
59 WAD_ChangeWADNum(i, NULL);
60 wad_removednum = i;
61
62 for (; i < wad_numwads; i++)
63 {
64 if (WAD_WADByNum(i+1))
65 {
66 WAD_ChangeWADNum(i, WAD_WADByNum(i+1));
67 WAD_ChangeWADNum(i+1, NULL);
68 }
69 }
70
71 wad_oldnumwads = wad_numwads;
72 wad_numwads--;
73 }
74
WAD_WADLoopAdvance(wad_uint32_t i)75 wad_uint32_t WAD_WADLoopAdvance(wad_uint32_t i)
76 {
77 wad_uint32_t o;
78
79 o = wad_oldnumwads;
80 wad_oldnumwads = wad_numwads;
81
82 return (o > wad_numwads && i <= wad_removednum) ? i : i + 1;
83 }
84
WAD_WADByNum(wad_uint16_t num)85 wad_t *WAD_WADByNum(wad_uint16_t num)
86 {
87 return (wads[num]) ? (wad_t *)wads[num] : NULL;
88 }
89
WAD_WADByFileName(const char * filename)90 wad_t *WAD_WADByFileName(const char *filename)
91 {
92 wad_uint32_t i;
93 wad_t *w;
94
95 for (i = 0; i < wad_numwads; i = WAD_WADLoopAdvance(i))
96 {
97 w = WAD_WADByNum(i);
98 if (!strcmp(filename, w->filename))
99 return w;
100 }
101
102 return NULL;
103 }
104
105 //
106 // Allocate a wadfile, setup the lumpinfo (directory) and
107 // lumpcache, add the wadfile to the current active wadfiles
108 //
109 // 1: Reached the maximum WAD number.
110 // 2: Cannot open the file.
111 // 3: Cannot read the WAD file's header.
112 // 4: Invalid WAD ID.
113 // 5: WAD's lump directory is corrupt.
114 // 6: Corrupt compressed WAD.
115 //
WAD_OpenWAD(const char * filename)116 wad_t *WAD_OpenWAD(const char *filename)
117 {
118 size_t i;
119 // Pointer to the WAD.
120 wad_t *wad = NULL;
121 // WAD file structure information.
122 // wad->filename is the function's argument.
123 FILE *file; // wad->file
124 wadheader_t *header; // wad->header
125 lump_t *l, *lumps = NULL; // wad->lumps
126 // Temporary lump information.
127 wad_uint64_t position;
128 lumpdir_t *lumpdir;
129 lump_t *ln = NULL;
130
131 //
132 // Check if the limit of maximum WAD files has been
133 // reached or not.
134 //
135 if (wad_numwads >= MAX_WADFILES)
136 return NULL;
137
138 //
139 // Open the WAD file.
140 //
141 if (!(file = fopen(filename, "rb")))
142 return NULL;
143
144 //
145 // Read the WAD file's header.
146 //
147 header = malloc(sizeof(*header));
148 if (fread(header, 1, sizeof(*header), file) < sizeof(*header))
149 return NULL;
150
151 // Check if the ID is valid.
152 if (memcmp(header->id, "IWAD", 4) && memcmp(header->id, "PWAD", 4) && memcmp(header->id, "SDLL", 4))
153 {
154 WAD_FreeMemory(file, header, NULL, NULL, NULL);
155 return NULL;
156 }
157
158 // Check the rest of the header for type.
159 header->numlumps = WAD_INT32_Endianness(header->numlumps);
160 header->lumpdir = WAD_INT32_Endianness(header->lumpdir);
161
162 //
163 // Read the WAD file's lump directory.
164 //
165 i = header->numlumps * sizeof(*lumpdir);
166 lumpdir = malloc(i);
167
168 if (fseek(file, header->lumpdir, SEEK_SET) < 0 || fread(lumpdir, i, 1, file) < 1)
169 {
170 WAD_FreeMemory(file, header, lumpdir, NULL, NULL);
171 return NULL;
172 }
173
174 //
175 // Fill lump information structure.
176 //
177 for (i = 0; i < header->numlumps; i++, lumpdir++)
178 {
179 if (!(l = malloc(sizeof(*l))))
180 {
181 WAD_FreeMemory(file, header, lumpdir, lumps, NULL);
182 return NULL;
183 }
184
185 position = WAD_INT32_Endianness(lumpdir->position);
186
187 strncpy(l->name, lumpdir->name, 8);
188 l->size = l->disksize = WAD_INT32_Endianness(lumpdir->size);
189 l->compressed = false; // TODO: Compressed WAD support.
190
191 if (l->disksize)
192 {
193 fseek(file, position, SEEK_SET);
194 if (!(l->data = malloc(l->disksize)) || fread(l->data, l->disksize, 1, file) < 1)
195 {
196 WAD_FreeMemory(file, header, lumpdir, lumps, NULL);
197
198 if (!l->data)
199 return NULL;
200 else
201 return NULL;
202 }
203 }
204
205 l->num = i;
206 l->next = NULL;
207 if (!lumps)
208 {
209 l->prev = NULL;
210 ln = lumps = l;
211 }
212 else
213 {
214 l->prev = ln;
215 ln = ln->next = l;
216 }
217 }
218 //free(lumpdir);
219
220 //
221 // Allocate and fill WAD entry.
222 //
223 if (!(wad = malloc(sizeof(*wad))))
224 {
225 WAD_FreeMemory(file, header, NULL, lumps, NULL);
226 return NULL;
227 }
228 wad->filename = (char *)filename;
229
230 wad->header = header;
231 wad->compressed = false;
232
233 wad->lumps = lumps;
234
235 for (l = wad->lumps; l; l = l->next)
236 l->wad = wad;
237
238 fclose(file);
239
240 //
241 // Add the WAD to the wadfiles buffer and wad_numwads number.
242 //
243 WAD_CacheWAD(wad);
244
245 //
246 // Done, loaded the WAD file.
247 //
248 return wad;
249 }
250
251 // 1: Invalid wad number.
252 // 2: Unable to open file for writing.
253 // 3: Unable to write wad ID to the header.
254 // 4: Unable to write number of lumps to the header.
255 // 5: Unable to write temporary data to the header.
256 // 6: Unable to write lump data.
257 // 7: Unable to write position of lump data.
258 // 8: Unable to Write size of lump data.
259 // 9: Unable to write lump name.
260 // 9: Unable to write directory position.
WAD_SaveWAD(wad_t * wad)261 wad_int32_t WAD_SaveWAD(wad_t *wad)
262 {
263 lump_t *l;
264 FILE *file;
265 char *newfilename;
266 wad_int64_t diroffset, lumpoffset;
267
268 if (!wad)
269 return 1;
270
271 newfilename = WAD_VA("%s%s", wad->filename, ".bak");
272
273 remove(newfilename);
274 rename(wad->filename, newfilename);
275
276 if (!(file = fopen(wad->filename, "wb")))
277 return 2;
278
279 //
280 // Write four-character WAD ID.
281 //
282 if(fwrite(wad->header->id, 4, 1, file) < 1)
283 return 3;
284
285 //
286 // Write number of lumps.
287 //
288 if (fwrite(&wad->header->numlumps, 4, 1, file) < 1)
289 return 4;
290
291 //
292 // Offset of directory is not known yet. For now, write number of lumps
293 // again, just to fill the space.
294 //
295 if (fwrite(&wad->header->numlumps, 4, 1, file) < 1)
296 return 5;
297
298 //
299 // Loop through lump list, writing lump data.
300 //
301 for (l = wad->lumps; l; l = l->next)
302 {
303 // Don't write anything for the head of the lump list or for lumps of
304 // zero length.
305 if (!l->data)
306 continue;
307
308 // Write the data.
309 if (fwrite(l->data, l->disksize, 1, file) < 1)
310 return 6;
311 }
312
313 //
314 // Current position is where directory will start.
315 //
316 diroffset = ftell(file);
317
318 //
319 // Pointer to the offset for the first lump's data.
320 //
321 lumpoffset = LUMPOFFSET;
322
323 //
324 // Loop through lump list again, this time writing directory entries.
325 //
326 for (l = wad->lumps; l; l = l->next)
327 {
328 // Write position of lump data.
329 if (fwrite(&lumpoffset, 4, 1, file) < 1)
330 return 7;
331
332 // Write size of lump data.
333 if (fwrite(&(l->disksize), 4, 1, file) < 1)
334 return 8;
335
336 // Write lump name.
337 if (fwrite(&(l->name), 8, 1, file) < 1)
338 return 9;
339
340 // Increment lumpoffset variable as appropriate.
341 lumpoffset += l->disksize;
342 }
343
344 //
345 // Go back to header and write the proper directory position.
346 //
347 fseek(file, 8, SEEK_SET);
348 if (fwrite(&diroffset, 4, 1, file) < 1)
349 return 10;
350
351 //
352 // Done, saved the WAD file.
353 //
354 return 0;
355 }
356
WAD_SaveWADByNum(wad_uint16_t num)357 inline wad_int32_t WAD_SaveWADByNum(wad_uint16_t num)
358 {
359 return WAD_SaveWAD(WAD_WADByNum(num));
360 }
361
WAD_SaveWADByFileName(const char * filename)362 inline wad_int32_t WAD_SaveWADByFileName(const char *filename)
363 {
364 return WAD_SaveWAD(WAD_WADByFileName(filename));
365 }
366
WAD_SaveCloseWAD(wad_t * wad)367 inline wad_int32_t WAD_SaveCloseWAD(wad_t *wad)
368 {
369 wad_int32_t ret;
370
371 if (!(ret = WAD_SaveWAD(wad)))
372 return ret;
373
374 WAD_CloseWAD(wad);
375 return ret;
376 }
377
WAD_SaveCloseWADByNum(wad_uint16_t num)378 inline wad_int32_t WAD_SaveCloseWADByNum(wad_uint16_t num)
379 {
380 return WAD_SaveCloseWAD(WAD_WADByNum(num));
381 }
382
WAD_SaveCloseWADByFileName(const char * filename)383 inline wad_int32_t WAD_SaveCloseWADByFileName(const char *filename)
384 {
385 return WAD_SaveCloseWAD(WAD_WADByFileName(filename));
386 }
387
WAD_CloseWAD(wad_t * wad)388 void WAD_CloseWAD(wad_t *wad)
389 {
390 if (!wad)
391 return;
392
393 //
394 // Free memory used by the WAD.
395 //
396 WAD_FreeMemory(NULL, wad->header, NULL, wad->lumps, wad);
397
398 //
399 // Remove references to the WAD.
400 //
401 WAD_UncacheWAD(wad);
402 }
403
WAD_CloseWADByNum(wad_uint16_t num)404 inline void WAD_CloseWADByNum(wad_uint16_t num)
405 {
406 WAD_CloseWAD(WAD_WADByNum(num));
407 }
408
WAD_CloseWADByFileName(const char * name)409 inline void WAD_CloseWADByFileName(const char *name)
410 {
411 WAD_CloseWAD(WAD_WADByFileName(name));
412 }
413