ref: a2c587e7d7a8b72a7ef9afcd119d8cfb20076b54
dir: /i_wad.c/
#include "quakedef.h" enum { WAD_VER2 = 'W'<<0|'A'<<8|'D'<<16|'2'<<24, WAD_VER3 = 'W'<<0|'A'<<8|'D'<<16|'3'<<24, TYP_QPIC = 0x42, TYP_MIPTEX = 0x43, /* it IS in Half-Life */ }; typedef struct Lmp Lmp; struct Lmp { char name[16]; int off; int sz; int type; }; struct Wad { byte *in; int sz; int ver; int numlumps; Lmp lumps[]; }; Wad * W_OpenWad(char *path) { Wad *w; Lmp *lmp; byte *in, *p; int sz, ver, off, n, i; if((in = loadhunklmp(path, &sz)) == nil) goto err; if(sz < 4+4+4){ werrstr("invalid size: %d", sz); goto err; } p = in; ver = le32(p); if(ver != WAD_VER2 && ver != WAD_VER3){ werrstr("unsupported version: %c%c%c%c", (char)in[0], (char)in[1], (char)in[2], (char)in[3]); goto err; } n = le32(p); off = le32(p); if(off < 0 || n < 0 || off+n*32 > sz){ werrstr("invalid wad: off=%d numlumps=%d sz=%d", off, n, sz); goto err; } w = Hunk_Alloc(sizeof(*w) + sizeof(*w->lumps)*n); w->in = in; w->sz = sz; w->ver = ver; w->numlumps = n; p = in + off; for(lmp = w->lumps; n-- > 0; lmp++){ lmp->off = le32(p); p += 4; /* disksize */ lmp->sz = le32(p); lmp->type = *p++; p += 1+2; /* compression + padding */ memmove(lmp->name, p, sizeof(lmp->name)); for(i = 0; i < nelem(lmp->name) && lmp->name[i]; i++) lmp->name[i] = tolower(lmp->name[i]); memset(lmp->name+i, 0, nelem(lmp->name)-i); p += nelem(lmp->name); } return w; err: werrstr("W_OpenWad: %s: %s", path, lerr()); return nil; } static Lmp * W_FindName(Wad *w, char *name) { int i; Lmp *lmp; char t[16]; for(i = 0; i < nelem(lmp->name) && name[i]; i++) t[i] = tolower(name[i]); memset(t+i, 0, sizeof(t)-i); for(i = 0, lmp = w->lumps; i < w->numlumps; i++, lmp++){ if(strncmp(lmp->name, t, nelem(lmp->name)) == 0) return lmp; } werrstr("%s: not found", name); return nil; } qpic_t * W_ReadQpic(Wad *wad, char *name, mem_user_t *c) { int i, n, w, h, palsz, j; Lmp *lmp; byte *p, *pal; qpic_t *q; mem_user_t dummy = {0}; if(c == nil){ memset(&dummy, 0, sizeof(dummy)); c = &dummy; } if((q = Cache_Check(c)) != nil) return q; if((lmp = W_FindName(wad, name)) == nil || lmp->type != TYP_QPIC) return nil; p = wad->in + lmp->off; w = le32(p); h = le32(p); n = w*h; if(w < 0 || h < 0){ werrstr("invalid size: %dx%d", w, h); goto err; } pal = nil; palsz = 0; if(wad->ver == WAD_VER2){ if(lmp->sz < 4+4+n){ werrstr("truncated: %d < %d", lmp->sz, 4+4+n); goto err; } }else if(wad->ver == WAD_VER3){ pal = p + n; palsz = le16(pal); if(palsz < 0 || palsz > 256 || lmp->sz < 4+4+n+2+palsz*3){ werrstr("invalid: palsz=%d, %d < %d", palsz, lmp->sz, 4+4+n+2+palsz*3); goto err; } } q = Cache_Alloc(c, sizeof(*q) + n*sizeof(pixel_t)); q->width = w; q->height = h; if(wad->ver == WAD_VER2){ for(i = 0; i < n; i++, p++) q->pixels[i] = q1palindexed[*p]; }else if(wad->ver == WAD_VER3 && palsz > 0){ for(i = 0; i < n; i++, p++){ j = *p*3; q->pixels[i] = j < palsz*3 ? (0xff<<24 | pal[j+0]<<16 | pal[j+1]<<8 | pal[j+2]) : 0; } } return q; err: werrstr("%.*s: %s", nelem(lmp->name), lmp->name, lerr()); return nil; } static int W_ReadPixelsAt(Wad *wad, char *name, int off, int sz, pixel_t *out, int num) { int n, palsz, x, fb; byte *t, *pal; num = min(num, sz); num = min(num, wad->sz-off); t = wad->in + off; if(wad->ver == WAD_VER2){ for(n = 0; n < num; n++) *out++ = q1pal[*t++]; }else if(wad->ver == WAD_VER3){ if(off+num+2 >= wad->sz){ werrstr("invalid lump: %d > %d", off+num+2, wad->sz); return -1; } pal = t + num; palsz = le16(pal); if(palsz <= 0 || palsz > 256 || off+num+2+palsz*3 > wad->sz){ werrstr("invalid palette: palsz=%d pal_end=%d wad_sz=%d", palsz, off+num+2+palsz*3, wad->sz); goto err; } palsz *= 3; fb = strchr(name, '~') != nil; for(n = 0; n < num; n++, t++, out++){ x = *t*3; if(x >= palsz || (name[0] == '{' && x >= palsz-3)) *out = 0; else *out = ((fb && x >= palsz-32*3) ? 0 : 0xff)<<24 | pal[x+0]<<16 | pal[x+1]<<8 | pal[x+2]; } } return num; err: return -1; } int W_ReadMipTex(Wad *wad, char *name, texture_t *t) { Lmp *lmp; byte *p; int i, w, h, n, off; if((lmp = W_FindName(wad, name)) == nil || lmp->type != TYP_MIPTEX) return -1; p = wad->in + lmp->off + 16; w = le32(p); h = le32(p); if(w != t->width || h != t->height){ werrstr("%s: size mismatch: (%d->%d)x(%d->%d)\n", name, w, t->width, h, t->height); return -1; } n = w*h*85/64; for(i = 0; i < nelem(t->offsets); i++) t->offsets[i] = le32(p) - (16+2*4+4*4); off = p - wad->in; if((n = W_ReadPixelsAt(wad, name, off, lmp->off+lmp->sz-off, t->pixels, n)) < 0) werrstr("%s: %s", name, lerr()); if(name[0] == '{'){ for(i = 1; i < MIPLEVELS; i++){ w /= 2; h /= 2; pixels_resize( t->pixels+t->offsets[0], t->pixels+t->offsets[i], t->width, t->height, w, h, false, true ); } } return n; } int W_ReadPixels(Wad *wad, char *name, pixel_t *out, int num) { Lmp *lmp; int n; if((lmp = W_FindName(wad, name)) == nil) return -1; if((n = W_ReadPixelsAt(wad, name, lmp->off, lmp->sz, out, num)) < 0) werrstr("%s: %s", name, lerr()); return n; } int W_ReadRaw(Wad *wad, char *name, byte *out, int num) { Lmp *lmp; if((lmp = W_FindName(wad, name)) == nil) return -1; num = min(num, lmp->sz); num = min(num, wad->sz-lmp->off); memmove(out, wad->in+lmp->off, num); return num; }