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