diff --git a/common/bspfile.cc b/common/bspfile.cc index 062c2452..2c02716a 100644 --- a/common/bspfile.cc +++ b/common/bspfile.cc @@ -1252,8 +1252,8 @@ static const gamedef_h2_t gamedef_h2; static const gamedef_hl_t gamedef_hl; static const gamedef_q2_t gamedef_q2; -const bspversion_t bspver_generic{NO_VERSION, NO_VERSION, "mbsp", "generic BSP", {}, &gamedef_generic}; -const bspversion_t bspver_q1{BSPVERSION, NO_VERSION, "bsp29", "Quake BSP", +const bspversion_t bspver_generic{MBSPIDENT, std::nullopt, "mbsp", "generic BSP", {}, &gamedef_generic}; +const bspversion_t bspver_q1{BSPVERSION, std::nullopt, "bsp29", "Quake BSP", { {"entities", sizeof(char)}, {"planes", sizeof(dplane_t)}, @@ -1272,7 +1272,7 @@ const bspversion_t bspver_q1{BSPVERSION, NO_VERSION, "bsp29", "Quake BSP", {"models", sizeof(dmodelq1_t)}, }, &gamedef_q1, &bspver_bsp2}; -const bspversion_t bspver_bsp2{BSP2VERSION, NO_VERSION, "bsp2", "Quake BSP2", +const bspversion_t bspver_bsp2{BSP2VERSION, std::nullopt, "bsp2", "Quake BSP2", { {"entities", sizeof(char)}, {"planes", sizeof(dplane_t)}, @@ -1291,7 +1291,7 @@ const bspversion_t bspver_bsp2{BSP2VERSION, NO_VERSION, "bsp2", "Quake BSP2", {"models", sizeof(dmodelq1_t)}, }, &gamedef_q1}; -const bspversion_t bspver_bsp2rmq{BSP2RMQVERSION, NO_VERSION, "bsp2rmq", "Quake BSP2-RMQ", +const bspversion_t bspver_bsp2rmq{BSP2RMQVERSION, std::nullopt, "bsp2rmq", "Quake BSP2-RMQ", { {"entities", sizeof(char)}, {"planes", sizeof(dplane_t)}, @@ -1311,7 +1311,7 @@ const bspversion_t bspver_bsp2rmq{BSP2RMQVERSION, NO_VERSION, "bsp2rmq", "Quake }, &gamedef_q1}; /* Hexen II doesn't use a separate version, but we can still use a separate tag/name for it */ -const bspversion_t bspver_h2{BSPVERSION, NO_VERSION, "hexen2", "Hexen II BSP", +const bspversion_t bspver_h2{BSPVERSION, std::nullopt, "hexen2", "Hexen II BSP", { {"entities", sizeof(char)}, {"planes", sizeof(dplane_t)}, @@ -1330,7 +1330,7 @@ const bspversion_t bspver_h2{BSPVERSION, NO_VERSION, "hexen2", "Hexen II BSP", {"models", sizeof(dmodelh2_t)}, }, &gamedef_h2, &bspver_h2bsp2}; -const bspversion_t bspver_h2bsp2{BSP2VERSION, NO_VERSION, "hexen2bsp2", "Hexen II BSP2", +const bspversion_t bspver_h2bsp2{BSP2VERSION, std::nullopt, "hexen2bsp2", "Hexen II BSP2", { {"entities", sizeof(char)}, {"planes", sizeof(dplane_t)}, @@ -1349,7 +1349,7 @@ const bspversion_t bspver_h2bsp2{BSP2VERSION, NO_VERSION, "hexen2bsp2", "Hexen I {"models", sizeof(dmodelh2_t)}, }, &gamedef_h2}; -const bspversion_t bspver_h2bsp2rmq{BSP2RMQVERSION, NO_VERSION, "hexen2bsp2rmq", "Hexen II BSP2-RMQ", +const bspversion_t bspver_h2bsp2rmq{BSP2RMQVERSION, std::nullopt, "hexen2bsp2rmq", "Hexen II BSP2-RMQ", { {"entities", sizeof(char)}, {"planes", sizeof(dplane_t)}, @@ -1368,7 +1368,7 @@ const bspversion_t bspver_h2bsp2rmq{BSP2RMQVERSION, NO_VERSION, "hexen2bsp2rmq", {"models", sizeof(dmodelh2_t)}, }, &gamedef_h2}; -const bspversion_t bspver_hl{BSPHLVERSION, NO_VERSION, "hl", "Half-Life BSP", bspver_q1.lumps, &gamedef_hl}; +const bspversion_t bspver_hl{BSPHLVERSION, std::nullopt, "hl", "Half-Life BSP", bspver_q1.lumps, &gamedef_hl}; const bspversion_t bspver_q2{Q2_BSPIDENT, Q2_BSPVERSION, "q2bsp", "Quake II BSP", { {"entities", sizeof(char)}, @@ -1529,7 +1529,7 @@ std::string contentflags_t::to_string(const gamedef_t *game) const return s; } -static bool BSPVersionSupported(int32_t ident, int32_t version, const bspversion_t **out_version) +static bool BSPVersionSupported(int32_t ident, std::optional version, const bspversion_t **out_version) { for (const bspversion_t *bspver : bspversions) { if (bspver->ident == ident && bspver->version == version) { @@ -1976,7 +1976,7 @@ void LoadBSPFile(fs::path &filename, bspdata_t *bspdata) dheader_t q1header; stream >= q1header; - temp_version.version = NO_VERSION; + temp_version.version = std::nullopt; std::copy(q1header.lumps.begin(), q1header.lumps.end(), std::back_inserter(lumps)); } @@ -2075,7 +2075,7 @@ private: const lumpspec_t &lumpspec = version->lumps.begin()[lump_num]; lump_t *lumps; - if (version->version != NO_VERSION) { + if (version->version.has_value()) { lumps = q2header.lumps.data(); } else { lumps = q1header.lumps.data(); @@ -2112,7 +2112,7 @@ private: Q_assert(lumpspec.size == 1); - if (version->version != NO_VERSION) { + if (version->version.has_value()) { lumps = q2header.lumps.data(); } else { lumps = q1header.lumps.data(); @@ -2144,7 +2144,7 @@ private: Q_assert(lumpspec.size == 1); - if (version->version != NO_VERSION) { + if (version->version.has_value()) { lumps = q2header.lumps.data(); } else { lumps = q1header.lumps.data(); @@ -2269,11 +2269,11 @@ void WriteBSPFile(const fs::path &filename, bspdata_t *bspdata) // headers are union'd, so this sets both bspfile.q2header.ident = bspfile.version->ident; - if (bspfile.version->version != NO_VERSION) { - bspfile.q2header.version = bspfile.version->version; + if (bspfile.version->version.has_value()) { + bspfile.q2header.version = bspfile.version->version.value(); } - logging::print("Writing {} as BSP version {}\n", filename, *bspdata->version); + logging::print("Writing {} as {}\n", filename, *bspdata->version); bspfile.stream.open(filename, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary); if (!bspfile.stream) @@ -2282,7 +2282,7 @@ void WriteBSPFile(const fs::path &filename, bspdata_t *bspdata) bspfile.stream << endianness; /* Save header space, updated after adding the lumps */ - if (bspfile.version->version != NO_VERSION) { + if (bspfile.version->version.has_value()) { bspfile.stream <= bspfile.q2header; } else { bspfile.stream <= bspfile.q1header; @@ -2296,7 +2296,7 @@ void WriteBSPFile(const fs::path &filename, bspdata_t *bspdata) bspfile.stream.seekp(0); // write the real header - if (bspfile.version->version != NO_VERSION) { + if (bspfile.version->version.has_value()) { bspfile.stream <= bspfile.q2header; } else { bspfile.stream <= bspfile.q1header; diff --git a/include/common/bspfile.hh b/include/common/bspfile.hh index 56f4b5ef..257e2380 100644 --- a/include/common/bspfile.hh +++ b/include/common/bspfile.hh @@ -41,6 +41,8 @@ struct lump_t auto stream_data() { return std::tie(fileofs, filelen); } }; +constexpr int32_t MBSPIDENT = -1; + constexpr int32_t BSPVERSION = 29; constexpr int32_t BSP2RMQVERSION = (('B' << 24) | ('S' << 16) | ('P' << 8) | '2'); constexpr int32_t BSP2VERSION = ('B' | ('S' << 8) | ('P' << 16) | ('2' << 24)); @@ -96,31 +98,6 @@ enum q2_lump_t Q2_HEADER_LUMPS }; -struct bspx_header_t -{ - std::array id = {'B', 'S', 'P', 'X'}; //'BSPX' - uint32_t numlumps; - - bspx_header_t(uint32_t numlumps) : numlumps(numlumps) { } - - auto stream_data() { return std::tie(id, numlumps); } -}; - -struct bspx_lump_t -{ - std::array lumpname{}; - uint32_t fileofs; - uint32_t filelen; - - auto stream_data() { return std::tie(lumpname, fileofs, filelen); } -}; - -struct lumpspec_t -{ - const char *name; - size_t size; -}; - // helper functions to quickly numerically cast mins/maxs // and floor/ceil them in the case of float -> integral template @@ -1673,43 +1650,7 @@ struct q2_dheader_t auto stream_data() { return std::tie(ident, version, lumps); } }; -/* ========================================================================= */ - -// BRUSHLIST BSPX lump -struct bspxbrushes_permodel -{ - int32_t ver; - int32_t modelnum; - int32_t numbrushes; - int32_t numfaces; - - auto stream_data() { return std::tie(ver, modelnum, numbrushes, numfaces); } -}; - -struct bspxbrushes_perbrush -{ - aabb3f bounds; - int16_t contents; - uint16_t numfaces; - - auto stream_data() { return std::tie(bounds, contents, numfaces); } -}; - -using bspxbrushes_perface = qplane3f; - -// BSPX data - -struct bspxentry_t -{ - std::unique_ptr lumpdata; - size_t lumpsize; - - // bspxentry_t takes ownership over the pointer and will - // free it automatically. - bspxentry_t(void *lumpdata, size_t lumpsize) : lumpdata(reinterpret_cast(lumpdata)), lumpsize(lumpsize) - { - } -}; +#include "bspxfile.hh" struct bspdata_t { @@ -1821,15 +1762,22 @@ struct gamedef_t virtual void print_content_stats(const std::any &stats, const char *what) const = 0; }; -constexpr int32_t NO_VERSION = -1; +// Lump specification; stores the name and size +// of an individual entry in the lump. Count is +// calculated as (lump_size / size) +struct lumpspec_t +{ + const char *name; + size_t size; +}; // BSP version struct & instances struct bspversion_t { /* identifier value, the first int32_t in the header */ int32_t ident; - /* version value, if supported; use NO_VERSION if a version is not required */ - int32_t version; + /* version value, if supported */ + std::optional version; /* short name used for command line args, etc */ const char *short_name; /* full display name for printing */ @@ -1852,14 +1800,17 @@ struct fmt::formatter auto format(const bspversion_t &v, FormatContext &ctx) -> decltype(ctx.out()) { if (v.name) { - return format_to(ctx.out(), "{}", v.name); + format_to(ctx.out(), "{} ", v.name); } - if (v.version != NO_VERSION) { - return format_to(ctx.out(), "{}:{}", v.version, v.ident); + // Q2-esque BSPs are printed as, ex, IBSP:38 + if (v.version.has_value()) { + char ident[5] = { (char) (v.ident & 0xFF), (char) ((v.ident >> 8) & 0xFF), (char) ((v.ident >> 16) & 0xFF), (char) ((v.ident >> 24) & 0xFF), '\0' }; + return format_to(ctx.out(), "{}:{}", ident, v.version.value()); } - return format_to(ctx.out(), "{}", v.version, v.ident); + // Q1-esque BSPs are printed as, ex, 29 + return format_to(ctx.out(), "{}", v.version.value()); } }; diff --git a/include/common/bspxfile.hh b/include/common/bspxfile.hh new file mode 100644 index 00000000..1c158175 --- /dev/null +++ b/include/common/bspxfile.hh @@ -0,0 +1,82 @@ +/* Copyright (C) 1996-1997 Id Software, Inc. + + 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. +*/ + +#pragma once + +#include +#include +#include +#include + +/* ========================================================================= */ + +struct bspx_header_t +{ + std::array id = {'B', 'S', 'P', 'X'}; //'BSPX' + uint32_t numlumps; + + constexpr bspx_header_t(uint32_t numlumps) : numlumps(numlumps) { } + + auto stream_data() { return std::tie(id, numlumps); } +}; + +struct bspx_lump_t +{ + std::array lumpname{}; + uint32_t fileofs; + uint32_t filelen; + + auto stream_data() { return std::tie(lumpname, fileofs, filelen); } +}; + +// BRUSHLIST BSPX lump +struct bspxbrushes_permodel +{ + int32_t ver; + int32_t modelnum; + int32_t numbrushes; + int32_t numfaces; + + auto stream_data() { return std::tie(ver, modelnum, numbrushes, numfaces); } +}; + +struct bspxbrushes_perbrush +{ + aabb3f bounds; + int16_t contents; + uint16_t numfaces; + + auto stream_data() { return std::tie(bounds, contents, numfaces); } +}; + +using bspxbrushes_perface = qplane3f; + +// BSPX data + +struct bspxentry_t +{ + std::unique_ptr lumpdata; + size_t lumpsize; + + // bspxentry_t takes ownership over the pointer and will + // free it automatically. + inline bspxentry_t(void *lumpdata, size_t lumpsize) : lumpdata(reinterpret_cast(lumpdata)), lumpsize(lumpsize) + { + } +}; \ No newline at end of file