ericw-tools/qbsp/wad.cc

341 lines
12 KiB
C++

/*
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 1997 Greg Lewis
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
See file, 'COPYING', for details.
*/
#include <cstring>
#include <string>
#include <qbsp/qbsp.hh>
#include <qbsp/wad.hh>
uint8_t thepalette[768] = // Quake palette
{0, 0, 0, 15, 15, 15, 31, 31, 31, 47, 47, 47, 63, 63, 63, 75, 75, 75, 91, 91, 91, 107, 107, 107, 123, 123, 123, 139,
139, 139, 155, 155, 155, 171, 171, 171, 187, 187, 187, 203, 203, 203, 219, 219, 219, 235, 235, 235, 15, 11, 7,
23, 15, 11, 31, 23, 11, 39, 27, 15, 47, 35, 19, 55, 43, 23, 63, 47, 23, 75, 55, 27, 83, 59, 27, 91, 67, 31, 99,
75, 31, 107, 83, 31, 115, 87, 31, 123, 95, 35, 131, 103, 35, 143, 111, 35, 11, 11, 15, 19, 19, 27, 27, 27, 39,
39, 39, 51, 47, 47, 63, 55, 55, 75, 63, 63, 87, 71, 71, 103, 79, 79, 115, 91, 91, 127, 99, 99, 139, 107, 107,
151, 115, 115, 163, 123, 123, 175, 131, 131, 187, 139, 139, 203, 0, 0, 0, 7, 7, 0, 11, 11, 0, 19, 19, 0, 27, 27,
0, 35, 35, 0, 43, 43, 7, 47, 47, 7, 55, 55, 7, 63, 63, 7, 71, 71, 7, 75, 75, 11, 83, 83, 11, 91, 91, 11, 99, 99,
11, 107, 107, 15, 7, 0, 0, 15, 0, 0, 23, 0, 0, 31, 0, 0, 39, 0, 0, 47, 0, 0, 55, 0, 0, 63, 0, 0, 71, 0, 0, 79,
0, 0, 87, 0, 0, 95, 0, 0, 103, 0, 0, 111, 0, 0, 119, 0, 0, 127, 0, 0, 19, 19, 0, 27, 27, 0, 35, 35, 0, 47, 43,
0, 55, 47, 0, 67, 55, 0, 75, 59, 7, 87, 67, 7, 95, 71, 7, 107, 75, 11, 119, 83, 15, 131, 87, 19, 139, 91, 19,
151, 95, 27, 163, 99, 31, 175, 103, 35, 35, 19, 7, 47, 23, 11, 59, 31, 15, 75, 35, 19, 87, 43, 23, 99, 47, 31,
115, 55, 35, 127, 59, 43, 143, 67, 51, 159, 79, 51, 175, 99, 47, 191, 119, 47, 207, 143, 43, 223, 171, 39, 239,
203, 31, 255, 243, 27, 11, 7, 0, 27, 19, 0, 43, 35, 15, 55, 43, 19, 71, 51, 27, 83, 55, 35, 99, 63, 43, 111, 71,
51, 127, 83, 63, 139, 95, 71, 155, 107, 83, 167, 123, 95, 183, 135, 107, 195, 147, 123, 211, 163, 139, 227, 179,
151, 171, 139, 163, 159, 127, 151, 147, 115, 135, 139, 103, 123, 127, 91, 111, 119, 83, 99, 107, 75, 87, 95, 63,
75, 87, 55, 67, 75, 47, 55, 67, 39, 47, 55, 31, 35, 43, 23, 27, 35, 19, 19, 23, 11, 11, 15, 7, 7, 187, 115, 159,
175, 107, 143, 163, 95, 131, 151, 87, 119, 139, 79, 107, 127, 75, 95, 115, 67, 83, 107, 59, 75, 95, 51, 63, 83,
43, 55, 71, 35, 43, 59, 31, 35, 47, 23, 27, 35, 19, 19, 23, 11, 11, 15, 7, 7, 219, 195, 187, 203, 179, 167, 191,
163, 155, 175, 151, 139, 163, 135, 123, 151, 123, 111, 135, 111, 95, 123, 99, 83, 107, 87, 71, 95, 75, 59, 83,
63, 51, 67, 51, 39, 55, 43, 31, 39, 31, 23, 27, 19, 15, 15, 11, 7, 111, 131, 123, 103, 123, 111, 95, 115, 103,
87, 107, 95, 79, 99, 87, 71, 91, 79, 63, 83, 71, 55, 75, 63, 47, 67, 55, 43, 59, 47, 35, 51, 39, 31, 43, 31, 23,
35, 23, 15, 27, 19, 11, 19, 11, 7, 11, 7, 255, 243, 27, 239, 223, 23, 219, 203, 19, 203, 183, 15, 187, 167, 15,
171, 151, 11, 155, 131, 7, 139, 115, 7, 123, 99, 7, 107, 83, 0, 91, 71, 0, 75, 55, 0, 59, 43, 0, 43, 31, 0, 27,
15, 0, 11, 7, 0, 0, 0, 255, 11, 11, 239, 19, 19, 223, 27, 27, 207, 35, 35, 191, 43, 43, 175, 47, 47, 159, 47,
47, 143, 47, 47, 127, 47, 47, 111, 47, 47, 95, 43, 43, 79, 35, 35, 63, 27, 27, 47, 19, 19, 31, 11, 11, 15, 43,
0, 0, 59, 0, 0, 75, 7, 0, 95, 7, 0, 111, 15, 0, 127, 23, 7, 147, 31, 7, 163, 39, 11, 183, 51, 15, 195, 75, 27,
207, 99, 43, 219, 127, 59, 227, 151, 79, 231, 171, 95, 239, 191, 119, 247, 211, 139, 167, 123, 59, 183, 155, 55,
199, 195, 55, 231, 227, 87, 127, 191, 255, 171, 231, 255, 215, 255, 255, 103, 0, 0, 139, 0, 0, 179, 0, 0, 215,
0, 0, 255, 0, 0, 255, 243, 147, 255, 247, 199, 255, 255, 255, 159, 91, 83};
static bool WAD_LoadInfo(wad_t &wad, bool external)
{
wadinfo_t *hdr = &wad.header;
int i, len;
dmiptex_t miptex;
external |= options.fNoTextures;
len = SafeRead(wad.file, hdr, sizeof(wadinfo_t));
if (len != sizeof(wadinfo_t))
return false;
wad.version = 0;
if (!strncmp(hdr->identification, "WAD2", 4))
wad.version = 2;
else if (!strncmp(hdr->identification, "WAD3", 4))
wad.version = 3;
if (!wad.version)
return false;
SafeSeek(wad.file, hdr->infotableofs, SEEK_SET);
wad.lumps.reserve(wad.header.numlumps);
/* Get the dimensions and make a texture_t */
for (i = 0; i < wad.header.numlumps; i++) {
lumpinfo_t lump;
len = SafeRead(wad.file, &lump, sizeof(lump));
if (len != sizeof(lump))
return false;
auto restore_pos = SafeTell(wad.file);
SafeSeek(wad.file, lump.filepos, SEEK_SET);
len = SafeRead(wad.file, &miptex, sizeof(miptex));
if (len == sizeof(miptex)) {
int w = LittleLong(miptex.width);
int h = LittleLong(miptex.height);
lump.size =
sizeof(miptex) + (w >> 0) * (h >> 0) + (w >> 1) * (h >> 1) + (w >> 2) * (h >> 2) + (w >> 3) * (h >> 3);
if (options.target_game->id == GAME_HALF_LIFE)
lump.size += 2 + 3 * 256; // palette size+palette data
lump.size = (lump.size + 3) & ~3; // keep things aligned if we can.
texture_t tex;
memcpy(tex.name, miptex.name.data(), 16);
tex.name[15] = '\0';
tex.width = miptex.width;
tex.height = miptex.height;
wad.textures.insert({tex.name, tex});
// if we're not going to embed it into the bsp, set its size now so we know how much to actually store.
if (external)
lump.size = lump.disksize = sizeof(dmiptex_t);
// fmt::print("Created texture_t {} {} {}\n", tex->name, tex->width, tex->height);
wad.lumps.insert({tex.name, lump});
} else {
lump.size = 0;
wad.lumps.insert({lump.name, lump});
}
SafeSeek(wad.file, restore_pos, SEEK_SET);
}
return true;
}
static void WADList_OpenWad(const std::filesystem::path &fpath, bool external)
{
wad_t wad;
wad.file = SafeOpenRead(fpath);
if (wad.file) {
if (options.fVerbose)
LogPrint("Opened WAD: {}\n", fpath);
if (WAD_LoadInfo(wad, external)) {
wadlist.emplace_front(std::move(wad));
return;
}
LogPrint("WARNING: {} isn't a wadfile\n", fpath);
wad.file.reset();
} else {
// Message?
}
}
void WADList_Init(const char *wadstring)
{
if (!wadstring || !wadstring[0])
return;
const int len = strlen(wadstring);
const char *pos = wadstring;
while (pos - wadstring < len) {
// split string by ';' and copy the current component into fpath
const char *const fname = pos;
while (*pos && *pos != ';')
pos++;
const size_t fpathLen = pos - fname;
std::string fpathstr;
fpathstr.resize(fpathLen);
memcpy(&fpathstr[0], fname, fpathLen);
std::filesystem::path fpath = fpathstr;
if (options.wadPathsVec.empty() || fpath.is_absolute()) {
WADList_OpenWad(fpath, false);
} else {
for (const options_t::wadpath &wadpath : options.wadPathsVec) {
std::filesystem::path fullPath = wadpath.path / fpath;
WADList_OpenWad(fullPath, wadpath.external);
}
}
pos++;
}
}
static const lumpinfo_t *WADList_FindTexture(const std::string &name)
{
for (auto &wad : wadlist) {
auto it = wad.lumps.find(name);
if (it == wad.lumps.end()) {
continue;
}
return &it->second;
}
return NULL;
}
static bool WAD_LoadLump(const wad_t &wad, const char *name, miptexhl_t &dest)
{
auto it = wad.lumps.find(name);
if (it == wad.lumps.end()) {
it = wad.lumps.find("*slime0");
return false;
}
auto &lump = it->second;
SafeSeek(wad.file, lump.filepos, SEEK_SET);
if (lump.disksize < sizeof(dmiptex_t)) {
LogPrint("Wad texture {} is invalid", name);
return false;
}
if (lump.size != lump.disksize) {
LogPrint("Texture {} is {} bytes in wad, packed to {} bytes in bsp\n", name, lump.disksize, lump.size);
}
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(lump.disksize);
size_t size = SafeRead(wad.file, buffer.get(), lump.disksize);
if (size != lump.disksize)
FError("Failure reading from file");
memstream stream(buffer.get(), size);
stream >> endianness<std::endian::little>;
dmiptex_t header;
stream >= header;
dest.name = header.name.data();
dest.width = header.width;
dest.height = header.height;
// only header, no actual texture data
if (lump.disksize == sizeof(dmiptex_t)) {
return true;
}
if (lump.size != lump.disksize) {
LogPrint("Texture {} is {} bytes in wad, packed to {} bytes in bsp\n", name, lump.disksize, lump.size);
}
for (size_t i = 0; i < MIPLEVELS; i++) {
stream.seekg(header.offsets[i]);
size_t mipsize = (header.width >> i) * (header.height >> i);
dest.data[i] = std::make_unique<uint8_t[]>(mipsize);
stream.read(reinterpret_cast<char *>(dest.data[i].get()), mipsize);
}
// we're right after offsets[3] now, so see if palette is here
uint16_t palette_size;
if (stream >= palette_size) {
dest.palette.resize(palette_size);
if (!stream.read(reinterpret_cast<char *>(dest.palette.data()), palette_size * 3)) {
FError("Invalid palette in wad texture {}\n", name);
}
} else {
dest.palette.resize(sizeof(thepalette));
memcpy(dest.palette.data(), thepalette, sizeof(thepalette));
}
return true;
}
static void WADList_LoadTextures()
{
for (size_t i = 0; i < map.nummiptex(); i++) {
// already loaded?
if (map.bsp.dtex.textures[i].data[0])
continue;
for (auto &wad : wadlist) {
if (WAD_LoadLump(wad, map.miptexTextureName(i).c_str(), map.bsp.dtex.textures[i]))
break;
}
}
}
static void WADList_AddAnimationFrames()
{
size_t oldcount = map.nummiptex();
for (size_t i = 0; i < oldcount; i++) {
const std::string &existing_name = map.miptexTextureName(i);
if (existing_name[0] != '+' && (options.target_game->id != GAME_HALF_LIFE || existing_name[0] != '-'))
continue;
std::string name = map.miptexTextureName(i);
/* Search for all animations (0-9) and alt-animations (A-J) */
for (size_t j = 0; j < 20; j++) {
name[1] = (j < 10) ? '0' + j : 'a' + j - 10;
if (WADList_FindTexture(name))
FindMiptex(name.c_str());
}
}
LogPrint(LOG_STAT, " {:8} texture frames added\n", map.nummiptex() - oldcount);
}
void WADList_Process()
{
WADList_AddAnimationFrames();
// Q2 doesn't use texdata
if (options.target_game->id == GAME_QUAKE_II) {
return;
}
/* Default texture data to store in worldmodel */
map.bsp.dtex.textures.resize(map.nummiptex());
WADList_LoadTextures();
/* Last pass, mark unfound textures as such */
for (size_t i = 0; i < map.nummiptex(); i++) {
if (!map.bsp.dtex.textures[i].data[0]) {
LogPrint("WARNING: Texture {} not found\n", map.miptexTextureName(i));
}
}
}
const texture_t *WADList_GetTexture(const char *name)
{
for (auto &wad : wadlist) {
auto it = wad.textures.find(name);
if (it == wad.textures.end()) {
return nullptr;
}
return &it->second;
}
return nullptr;
}
std::list<wad_t> wadlist;