Fix setinfo.
[xonotic/darkplaces.git] / wad.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21
22 #include "quakedef.h"
23 #include "image.h"
24 #include "wad.h"
25
26 typedef struct mwad_s
27 {
28         qfile_t *file;
29         int numlumps;
30         lumpinfo_t *lumps;
31 }
32 mwad_t;
33
34 typedef struct wadstate_s
35 {
36         unsigned char *gfx_base;
37         mwad_t gfx;
38         memexpandablearray_t hlwads;
39 }
40 wadstate_t;
41
42 static wadstate_t wad;
43
44 /*
45 ==================
46 W_CleanupName
47
48 Lowercases name and pads with spaces and a terminating 0 to the length of
49 lumpinfo_t->name.
50 Used so lumpname lookups can proceed rapidly by comparing 4 chars at a time
51 Space padding is so names can be printed nicely in tables.
52 Can safely be performed in place.
53 ==================
54 */
55 static void W_CleanupName (const char *in, char *out)
56 {
57         int             i;
58         int             c;
59
60         for (i=0 ; i<16 ; i++ )
61         {
62                 c = in[i];
63                 if (!c)
64                         break;
65
66                 if (c >= 'A' && c <= 'Z')
67                         c += ('a' - 'A');
68                 out[i] = c;
69         }
70
71         for ( ; i< 16 ; i++ )
72                 out[i] = 0;
73 }
74
75 static void W_SwapLumps(int numlumps, lumpinfo_t *lumps)
76 {
77         int i;
78         for (i = 0;i < numlumps;i++)
79         {
80                 lumps[i].filepos = LittleLong(lumps[i].filepos);
81                 lumps[i].disksize = LittleLong(lumps[i].disksize);
82                 lumps[i].size = LittleLong(lumps[i].size);
83                 W_CleanupName(lumps[i].name, lumps[i].name);
84         }
85 }
86
87 void W_UnloadAll(void)
88 {
89         unsigned int i;
90         mwad_t *w;
91         // free gfx.wad if it is loaded
92         if (wad.gfx_base)
93                 Mem_Free(wad.gfx_base);
94         wad.gfx_base = NULL;
95         // close all hlwad files and free their lumps data
96         for (i = 0;i < Mem_ExpandableArray_IndexRange(&wad.hlwads);i++)
97         {
98                 w = (mwad_t *) Mem_ExpandableArray_RecordAtIndex(&wad.hlwads, i);
99                 if (!w)
100                         continue;
101                 if (w->file)
102                         FS_Close(w->file);
103                 w->file = NULL;
104                 if (w->lumps)
105                         Mem_Free(w->lumps);
106                 w->lumps = NULL;
107         }
108         // free the hlwads array
109         Mem_ExpandableArray_FreeArray(&wad.hlwads);
110         // clear all state
111         memset(&wad, 0, sizeof(wad));
112 }
113
114 unsigned char *W_GetLumpName(const char *name, fs_offset_t *returnfilesize)
115 {
116         int i;
117         fs_offset_t filesize;
118         lumpinfo_t *lump;
119         char clean[16];
120         wadinfo_t *header;
121         int infotableofs;
122
123         W_CleanupName (name, clean);
124
125         if (!wad.gfx_base)
126         {
127                 if ((wad.gfx_base = FS_LoadFile ("gfx.wad", cls.permanentmempool, false, &filesize)))
128                 {
129                         if (memcmp(wad.gfx_base, "WAD2", 4))
130                         {
131                                 Con_Print("gfx.wad doesn't have WAD2 id\n");
132                                 Mem_Free(wad.gfx_base);
133                                 wad.gfx_base = NULL;
134                         }
135                         else
136                         {
137                                 header = (wadinfo_t *)wad.gfx_base;
138                                 wad.gfx.numlumps = LittleLong(header->numlumps);
139                                 infotableofs = LittleLong(header->infotableofs);
140                                 wad.gfx.lumps = (lumpinfo_t *)(wad.gfx_base + infotableofs);
141
142                                 // byteswap the gfx.wad lumps in place
143                                 W_SwapLumps(wad.gfx.numlumps, wad.gfx.lumps);
144                         }
145                 }
146         }
147
148         for (lump = wad.gfx.lumps, i = 0;i < wad.gfx.numlumps;i++, lump++)
149         {
150                 if (!strcmp(clean, lump->name))
151                 {
152                         if (returnfilesize)
153                                 *returnfilesize = lump->size;
154                         return (wad.gfx_base + lump->filepos);
155                 }
156         }
157         return NULL;
158 }
159
160 /*
161 ====================
162 W_LoadTextureWadFile
163 ====================
164 */
165 void W_LoadTextureWadFile (char *filename, int complain)
166 {
167         wadinfo_t               header;
168         int                             infotableofs;
169         qfile_t                 *file;
170         int                             numlumps;
171         mwad_t                  *w;
172
173         file = FS_OpenVirtualFile(filename, false);
174         if (!file)
175         {
176                 if (complain)
177                         Con_Printf("W_LoadTextureWadFile: couldn't find %s\n", filename);
178                 return;
179         }
180
181         if (FS_Read(file, &header, sizeof(wadinfo_t)) != sizeof(wadinfo_t))
182         {Con_Print("W_LoadTextureWadFile: unable to read wad header\n");FS_Close(file);file = NULL;return;}
183
184         if(memcmp(header.identification, "WAD3", 4))
185         {Con_Printf("W_LoadTextureWadFile: Wad file %s doesn't have WAD3 id\n",filename);FS_Close(file);file = NULL;return;}
186
187         numlumps = LittleLong(header.numlumps);
188         if (numlumps < 1 || numlumps > 65536)
189         {Con_Printf("W_LoadTextureWadFile: invalid number of lumps (%i)\n", numlumps);FS_Close(file);file = NULL;return;}
190         infotableofs = LittleLong(header.infotableofs);
191         if (FS_Seek (file, infotableofs, SEEK_SET))
192         {Con_Print("W_LoadTextureWadFile: unable to seek to lump table\n");FS_Close(file);file = NULL;return;}
193
194         if (!wad.hlwads.mempool)
195                 Mem_ExpandableArray_NewArray(&wad.hlwads, cls.permanentmempool, sizeof(mwad_t), 16);
196         w = (mwad_t *) Mem_ExpandableArray_AllocRecord(&wad.hlwads);
197         w->file = file;
198         w->numlumps = numlumps;
199         w->lumps = (lumpinfo_t *) Mem_Alloc(cls.permanentmempool, w->numlumps * sizeof(lumpinfo_t));
200
201         if (!w->lumps)
202         {
203                 Con_Print("W_LoadTextureWadFile: unable to allocate temporary memory for lump table\n");
204                 FS_Close(w->file);
205                 w->file = NULL;
206                 w->numlumps = 0;
207                 return;
208         }
209
210         if (FS_Read(file, w->lumps, sizeof(lumpinfo_t) * w->numlumps) != (fs_offset_t)sizeof(lumpinfo_t) * numlumps)
211         {
212                 Con_Print("W_LoadTextureWadFile: unable to read lump table\n");
213                 FS_Close(w->file);
214                 w->file = NULL;
215                 w->numlumps = 0;
216                 Mem_Free(w->lumps);
217                 w->lumps = NULL;
218                 return;
219         }
220
221         W_SwapLumps(w->numlumps, w->lumps);
222
223         // leaves the file open
224 }
225
226 unsigned char *W_ConvertWAD3TextureBGRA(sizebuf_t *sb)
227 {
228         unsigned char *in, *data, *out, *pal;
229         int d, p;
230         unsigned char name[16];
231         unsigned int mipoffset[4];
232
233         MSG_BeginReading(sb);
234         MSG_ReadBytes(sb, 16, name);
235         image_width = MSG_ReadLittleLong(sb);
236         image_height = MSG_ReadLittleLong(sb);
237         mipoffset[0] = MSG_ReadLittleLong(sb);
238         mipoffset[1] = MSG_ReadLittleLong(sb); // should be mipoffset[0] + image_width*image_height
239         mipoffset[2] = MSG_ReadLittleLong(sb); // should be mipoffset[1] + image_width*image_height/4
240         mipoffset[3] = MSG_ReadLittleLong(sb); // should be mipoffset[2] + image_width*image_height/16
241         pal = sb->data + mipoffset[3] + (image_width / 8 * image_height / 8) + 2;
242
243         // bail if any data looks wrong
244         if (image_width < 0
245          || image_width > 4096
246          || image_height < 0
247          || image_height > 4096
248          || mipoffset[0] != 40
249          || mipoffset[1] != mipoffset[0] + image_width * image_height
250          || mipoffset[2] != mipoffset[1] + image_width / 2 * image_height / 2
251          || mipoffset[3] != mipoffset[2] + image_width / 4 * image_height / 4
252          || (unsigned int)sb->cursize < (mipoffset[3] + image_width / 8 * image_height / 8 + 2 + 768))
253                 return NULL;
254
255         in = (unsigned char *)sb->data + mipoffset[0];
256         data = out = (unsigned char *)Mem_Alloc(tempmempool, image_width * image_height * 4);
257         if (!data)
258                 return NULL;
259         for (d = 0;d < image_width * image_height;d++)
260         {
261                 p = *in++;
262                 if (name[0] == '{' && p == 255)
263                         out[0] = out[1] = out[2] = out[3] = 0;
264                 else
265                 {
266                         p *= 3;
267                         out[2] = pal[p];
268                         out[1] = pal[p+1];
269                         out[0] = pal[p+2];
270                         out[3] = 255;
271                 }
272                 out += 4;
273         }
274         return data;
275 }
276
277 unsigned char *W_GetTextureBGRA(char *name)
278 {
279         unsigned int i, k;
280         sizebuf_t sb;
281         unsigned char *data;
282         mwad_t *w;
283         char texname[17];
284         size_t range;
285
286         texname[16] = 0;
287         W_CleanupName(name, texname);
288         if (!wad.hlwads.mempool)
289                 Mem_ExpandableArray_NewArray(&wad.hlwads, cls.permanentmempool, sizeof(mwad_t), 16);
290         range = Mem_ExpandableArray_IndexRange(&wad.hlwads);
291         for (k = 0;k < range;k++)
292         {
293                 w = (mwad_t *)Mem_ExpandableArray_RecordAtIndex(&wad.hlwads, k);
294                 if (!w)
295                         continue;
296                 for (i = 0;i < (unsigned int)w->numlumps;i++)
297                 {
298                         if (!strcmp(texname, w->lumps[i].name)) // found it
299                         {
300                                 if (FS_Seek(w->file, w->lumps[i].filepos, SEEK_SET))
301                                 {Con_Print("W_GetTexture: corrupt WAD3 file\n");return NULL;}
302
303                                 MSG_InitReadBuffer(&sb, (unsigned char *)Mem_Alloc(tempmempool, w->lumps[i].disksize), w->lumps[i].disksize);
304                                 if (!sb.data)
305                                         return NULL;
306                                 if (FS_Read(w->file, sb.data, w->lumps[i].size) < w->lumps[i].disksize)
307                                 {Con_Print("W_GetTexture: corrupt WAD3 file\n");return NULL;}
308
309                                 data = W_ConvertWAD3TextureBGRA(&sb);
310                                 Mem_Free(sb.data);
311                                 return data;
312                         }
313                 }
314         }
315         image_width = image_height = 0;
316         return NULL;
317 }
318