allow a .map to be updated with a .ent file as input using replace-entities

This commit is contained in:
Jonathan 2022-11-25 04:51:00 -05:00
parent f7370db0be
commit 96e88cfab1
2 changed files with 192 additions and 18 deletions

View File

@ -36,6 +36,7 @@
#include <algorithm> // std::sort
#include <string>
#include <fstream>
#include <fmt/ostream.h>
/* FIXME - share header with qbsp, etc. */
struct wadinfo_t
@ -469,32 +470,136 @@ static void FindLeaf(const mbsp_t *bsp, const qvec3d &pos)
// TODO
settings::common_settings bsputil_options;
// map file stuff
struct map_entity_t {
entdict_t epairs;
parser_source_location location;
std::string map_brushes; // raw brush data
};
struct map_file_t {
std::vector<map_entity_t> entities;
};
static void ParseEpair(parser_t &parser, map_entity_t &entity)
{
std::string key = parser.token;
// trim whitespace from start/end
while (std::isspace(key.front())) {
key.erase(key.begin());
}
while (std::isspace(key.back())) {
key.erase(key.end() - 1);
}
parser.parse_token(PARSE_SAMELINE);
entity.epairs.set(key, parser.token);
}
bool ParseEntity(parser_t &parser, map_entity_t &entity)
{
entity.location = parser.location;
if (!parser.parse_token()) {
return false;
}
if (parser.token != "{") {
FError("{}: Invalid entity format, { not found", parser.location);
}
do {
if (!parser.parse_token())
FError("Unexpected EOF (no closing brace)");
if (parser.token == "}")
break;
else if (parser.token == "{") {
auto start = parser.pos - 1;
// skip until a }
do {
if (!parser.parse_token()) {
FError("Unexpected EOF (no closing brace)");
}
} while (parser.token != "}");
auto end = parser.pos;
entity.map_brushes += std::string(start, end) + "\n";
} else {
ParseEpair(parser, entity);
}
} while (1);
return true;
}
map_file_t LoadMapOrEntFile(const fs::path &source)
{
logging::funcheader();
auto file = fs::load(source);
map_file_t map;
if (!file) {
FError("Couldn't load map/entity file \"{}\".\n", source);
return map;
}
parser_t parser(file, { source.string() });
for (int i = 0;; i++) {
map_entity_t &entity = map.entities.emplace_back();
if (!ParseEntity(parser, entity)) {
break;
}
}
// Remove dummy entity inserted above
assert(!map.entities.back().epairs.size());
map.entities.pop_back();
return map;
}
int main(int argc, char **argv)
{
logging::preinitialize();
bspdata_t bspdata;
// FIXME: doesn't this get overwritten by ConvertBSPFormat below?
mbsp_t &bsp = bspdata.bsp.emplace<mbsp_t>();
fmt::print("---- bsputil / ericw-tools {} ----\n", ERICWTOOLS_VERSION);
if (argc == 1) {
printf(
"usage: bsputil [--replace-entities] [--extract-entities] [--extract-textures] [--convert bsp29|bsp2|bsp2rmq|q2bsp] [--check] [--modelinfo]\n"
"[--check] [--compare otherbsp] [--findfaces x y z nx ny nz] [--findleaf x y z] [--settexinfo facenum texinfonum]\n"
"[--decompile] [--decompile-geomonly] [--decompile-hull n] bspfile\n");
"[--decompile] [--decompile-geomonly] [--decompile-hull n] bspfile/mapfile\n");
exit(1);
}
fs::path source = DefaultExtension(argv[argc - 1], "bsp");
fs::path source = argv[argc - 1];
if (!fs::exists(source)) {
source = DefaultExtension(argv[argc - 1], "bsp");
}
printf("---------------------\n");
fmt::print("{}\n", source);
map_file_t map_file;
if (string_iequals(source.extension().string(), ".bsp")) {
LoadBSPFile(source, &bspdata);
bspdata.version->game->init_filesystem(source, bsputil_options);
ConvertBSPFormat(&bspdata, &bspver_generic);
} else {
map_file = LoadMapOrEntFile(source);
}
for (int32_t i = 0; i < argc - 1; i++) {
if (!strcmp(argv[i], "--replace-entities")) {
@ -504,17 +609,67 @@ int main(int argc, char **argv)
}
// Load the .ent
if (std::holds_alternative<mbsp_t>(bspdata.bsp)) {
fs::data ent = fs::load(argv[i]);
if (!ent) {
Error("couldn't load ent file {}", argv[i]);
}
std::get<mbsp_t>(bspdata.bsp).dentdata = std::string(reinterpret_cast<char *>(ent->data()), ent->size());
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
bsp.dentdata = std::string(reinterpret_cast<char *>(ent->data()), ent->size());
ConvertBSPFormat(&bspdata, bspdata.loadversion);
WriteBSPFile(source, &bspdata);
} else {
map_file_t ents = LoadMapOrEntFile(argv[i]);
ents.entities[0].map_brushes = std::move(map_file.entities[0].map_brushes);
// move brushes over from .map into the .ent
for (int32_t i1 = 0, b = 1; i1 < map_file.entities.size(); i1++) {
// skip worldspawn though
if (map_file.entities[i1].map_brushes.empty() || i1 == 0) {
continue;
}
for (int32_t i2 = 0, b2 = 1; i2 < ents.entities.size(); i2++) {
if (ents.entities[i2].epairs.get("model").empty() && ents.entities[i2].epairs.get("classname") != "func_areaportal") {
continue;
}
if (b2 == b) {
ents.entities[i2].map_brushes = std::move(map_file.entities[i1].map_brushes);
b++;
break;
}
b2++;
}
if (!map_file.entities[i1].map_brushes.empty()) {
Error("ent files' map brushes don't match\n");
}
}
// write out .replaced.map
fs::path output = fs::path(source).replace_extension(".replaced.map");
std::ofstream strm(output, std::ios::binary);
for (const auto &ent : ents.entities) {
strm << "{\n";
for (const auto &epair : ent.epairs) {
fmt::print(strm, "\"{}\" \"{}\"\n", epair.first, epair.second);
}
if (!ent.map_brushes.empty()) {
strm << ent.map_brushes;
}
strm << "}\n";
}
}
} else if (!strcmp(argv[i], "--compare")) {
i++;
if (i == argc - 1) {
@ -530,6 +685,8 @@ int main(int argc, char **argv)
fmt::print("comparing reference bsp {} with test bsp {}\n", refbspname, source);
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
CompareBSPFiles(std::get<mbsp_t>(refbspdata.bsp), bsp);
break;
@ -557,6 +714,9 @@ int main(int argc, char **argv)
WriteBSPFile(source.replace_filename(source.stem().string() + "-" + argv[i]), &bspdata);
} else if (!strcmp(argv[i], "--extract-entities")) {
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
uint32_t crc = CRC_Block((unsigned char *)bsp.dentdata.data(), bsp.dentdata.size() - 1);
source.replace_extension(".ent");
@ -575,6 +735,9 @@ int main(int argc, char **argv)
printf("done.\n");
} else if (!strcmp(argv[i], "--extract-textures")) {
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
source.replace_extension(".wad");
fmt::print("-> writing {}... ", source);
@ -588,10 +751,12 @@ int main(int argc, char **argv)
printf("done.\n");
} else if (!strcmp(argv[i], "--check")) {
printf("Beginning BSP data check...\n");
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
CheckBSPFile(&bsp);
CheckBSPFacesPlanar(&bsp);
printf("Done.\n");
} else if (!strcmp(argv[i], "--modelinfo")) {
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
PrintModelInfo(&bsp);
} else if (!strcmp(argv[i], "--findfaces")) {
// (i + 1) ... (i + 6) = x y z nx ny nz
@ -601,6 +766,8 @@ int main(int argc, char **argv)
Error("--findfaces requires 6 arguments");
}
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
try {
const qvec3d pos{std::stof(argv[i + 1]), std::stof(argv[i + 2]), std::stof(argv[i + 3])};
const qvec3d normal{std::stof(argv[i + 4]), std::stof(argv[i + 5]), std::stof(argv[i + 6])};
@ -617,6 +784,8 @@ int main(int argc, char **argv)
Error("--findleaf requires 3 arguments");
}
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
try {
const qvec3d pos{std::stof(argv[i + 1]), std::stof(argv[i + 2]), std::stof(argv[i + 3])};
FindLeaf(&bsp, pos);
@ -632,6 +801,8 @@ int main(int argc, char **argv)
Error("--settexinfo requires 2 arguments");
}
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
const int fnum = std::stoi(argv[i + 1]);
const int texinfonum = std::stoi(argv[i + 2]);
@ -671,6 +842,8 @@ int main(int argc, char **argv)
if (!f)
Error("couldn't open {} for writing\n", source);
mbsp_t &bsp = std::get<mbsp_t>(bspdata.bsp);
decomp_options options;
options.geometryOnly = geomOnly;
options.ignoreBrushes = ignoreBrushes;

View File

@ -847,8 +847,9 @@ faceextents_t::faceextents_t(const mface_t &face, const mbsp_t &bsp, world_units
const qvec3f &worldpoint = Face_PointAtIndex(&bsp, &face, i);
bounds += worldpoint;
auto lm = worldToLMMatrix * qvec4f(worldpoint, 1.0f);
#if 0
auto lm = worldToLMMatrix * qvec4f(worldpoint, 1.0f);
logging::print("testing world {} -> lm {}\n",
worldpoint,
lm);