From 292d6851440f187b3ec0ddfd52b8c11ae2c8f031 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Fri, 15 May 2026 12:21:43 -0300 Subject: [PATCH 01/10] dummy commit --- CMakeLists.txt | 1 - src/blockmap.cpp | 71 +--- src/bsp.cpp | 203 ++++------ src/bsp.hpp | 210 ++++++++++ src/core.hpp | 622 +----------------------------- src/level.cpp | 720 +++++------------------------------ src/local.hpp | 65 +--- src/main.cpp | 49 +-- src/parse.cpp | 1 + src/parse.hpp | 85 +++++ src/reject.cpp | 17 +- src/wad.cpp | 973 ++++++++--------------------------------------- src/wad.hpp | 101 +++++ 13 files changed, 784 insertions(+), 2334 deletions(-) create mode 100644 src/bsp.hpp create mode 100644 src/parse.hpp create mode 100644 src/wad.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 320531b..e84224c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,7 +177,6 @@ target_compile_options(${PROJECT_NAME} PRIVATE ) target_compile_options(${PROJECT_NAME} PRIVATE - $<$:-fno-exceptions> $<$:-fno-rtti> $<$:-Wold-style-cast> ) diff --git a/src/blockmap.cpp b/src/blockmap.cpp index bcfb698..6fa4842 100644 --- a/src/blockmap.cpp +++ b/src/blockmap.cpp @@ -348,59 +348,21 @@ static void CompressBlockmap(level_t &level) level.block_compression = std::max(0.0, level.block_compression); } -// compute size of final BLOCKMAP lump. -static size_t CalcBlockmapSize(level_t &level, const char *Prefix, size_t PrefixSize, size_t NumSize, size_t RawHeaderSize) -{ - size_t size = PrefixSize; - size += RawHeaderSize; - size += EXTRA_LINES * NumSize; // null block - size += level.block_count * NumSize; // the pointers (indexes to the line lists) - - // add size of each block - for (size_t i = 0; i < level.block_count; i++) - { - size_t blk_num = level.block_duplicates[i]; - if (blk_num == NO_INDEX) continue; // ignore duplicate or empty blocks - const auto &blk = level.block_lines[blk_num]; - size += (blk.lines.size() + EXTRA_LINES) * NumSize; - } - - if (HAS_BIT(config.debug, DEBUG_BLOCKMAP)) - { - PrintLine(LOG_DEBUG, "[%s] Lump prefix header \'%s\', num type size of %zu, total size of %zu", __func__, Prefix, NumSize, - size); - } - - return size; -} - template -static void WriteBlockmap(level_t &level) +static void WriteBlockmap(WadIO &io, level_t &level) { constexpr size_t NumSize = sizeof(NumType); - size_t max_size = 0; if constexpr (format == BMAP_XBM1) { - max_size = CalcBlockmapSize(level, "XBM1", 8, NumSize, sizeof(raw_blockmap_xbm1_header_t)); - } - else - { - max_size = CalcBlockmapSize(level, "", 0, NumSize, sizeof(raw_blockmap_header_t)); - } - - Lump_c *lump = CreateLevelLump(level, "BLOCKMAP", max_size); - - if constexpr (format == BMAP_XBM1) - { - lump->Write("XBM1\0\0\0\0", 8); + io.AddToLump("XBM1\0\0\0\0", 8); raw_blockmap_xbm1_header_t xbm1_header; xbm1_header.x_origin = GetLittleEndian(IntToFixed(level.block_x)); xbm1_header.y_origin = GetLittleEndian(IntToFixed(level.block_y)); xbm1_header.x_blocks = GetLittleEndian(IndexToInt(level.block_w)); xbm1_header.y_blocks = GetLittleEndian(IndexToInt(level.block_h)); - lump->Write(&xbm1_header, sizeof(xbm1_header)); + io.AddToLump(&xbm1_header, sizeof(xbm1_header)); } else { @@ -409,7 +371,7 @@ static void WriteBlockmap(level_t &level) header.y_origin = GetLittleEndian(level.block_y); header.x_blocks = GetLittleEndian(IndexToShort(level.block_w)); header.y_blocks = GetLittleEndian(IndexToShort(level.block_h)); - lump->Write(&header, sizeof(header)); + io.AddToLump(&header, sizeof(header)); } // handle pointers @@ -420,12 +382,12 @@ static void WriteBlockmap(level_t &level) { PrintLine(LOG_ERROR, "ERROR: WriteBlockmap: offset %zu not set.", i); } - lump->Write(&ptr, NumSize); + io.AddToLump(&ptr, NumSize); } // add the null block which *all* empty blocks will use - lump->Write(&m_zero, NumSize); - lump->Write(&m_neg1, NumSize); + io.AddToLump(&m_zero, NumSize); + io.AddToLump(&m_neg1, NumSize); // handle each block list for (size_t i = 0; i < level.block_count; i++) @@ -437,26 +399,24 @@ static void WriteBlockmap(level_t &level) const auto &blk = level.block_lines[blk_num]; - lump->Write(&m_zero, NumSize); + io.AddToLump(&m_zero, NumSize); for (size_t line : blk.lines) { NumType le_line = GetLittleEndian(static_cast(line)); - lump->Write(&le_line, NumSize); + io.AddToLump(&le_line, NumSize); } - lump->Write(&m_neg1, NumSize); + io.AddToLump(&m_neg1, NumSize); } - - lump->Finish(); } -void PutBlockmap(level_t &level) +void PutBlockmap(WadIO &io, level_t &level) { auto mark = Benchmarker(__func__); + io.StartWritingLump("BLOCKMAP"); - // just create an empty blockmap lump + // just leave an empty blockmap lump if (level.linedefs.size() == 0) { - CreateLevelLump(level, "BLOCKMAP")->Finish(); return; } @@ -468,15 +428,14 @@ void PutBlockmap(level_t &level) switch (level.bmap_format) { case BMAP_DoomBSP: - WriteBlockmap(level); + WriteBlockmap(io, level); break; case BMAP_XBM1: - WriteBlockmap(level); + WriteBlockmap(io, level); break; default: // how did we get here // leave an empty blockmap lump - CreateLevelLump(level, "BLOCKMAP")->Finish(); PrintLine(LOG_NORMAL, "WARNING: Blockmap overflowed (lump will be empty)"); config.total_warnings++; break; diff --git a/src/bsp.cpp b/src/bsp.cpp index 417d3ba..f04aaef 100644 --- a/src/bsp.cpp +++ b/src/bsp.cpp @@ -21,14 +21,16 @@ // //------------------------------------------------------------------------------ -#include - #include "core.hpp" #include "local.hpp" +#include "bsp.hpp" + +#include +#include // Upper-most bit is used for distinguishing sub-sectors, i.e tree leaves -constexpr uint16_t NF_SUBSECTOR_VANILLA = UINT16_C(0x8000); -constexpr uint32_t NF_SUBSECTOR = UINT32_C(0x80000000); +constexpr uint16_t NF_SUBSECTOR_VANILLA = BIT(15); +constexpr uint32_t NF_SUBSECTOR = BIT(31); // // Utility @@ -99,13 +101,11 @@ static inline short_angle_t VanillaSegAngle(const seg_t *seg) return result; } -static void PutVertices_Doom(level_t &level) +static void PutVertices_Doom(WadIO &io, level_t &level) { - // this size is worst-case scenario - size_t size = level.vertices.size() * sizeof(raw_vertex_t); - Lump_c *lump = CreateLevelLump(level, "VERTEXES", size); - size_t count = 0; + io.StartWritingLump("VERTEXES"); + for (size_t i = 0; i < level.vertices.size(); i++) { raw_vertex_t raw; @@ -121,24 +121,22 @@ static void PutVertices_Doom(level_t &level) raw.x = GetLittleEndian(FloatToShort(floor(vert->x))); raw.y = GetLittleEndian(FloatToShort(floor(vert->y))); - lump->Write(&raw, sizeof(raw_vertex_t)); - count++; + io.AddToLump(&raw, sizeof(raw_vertex_t)); } - lump->Finish(); - + // sanity count + // double check that bsp-vertices are written out in the correct formats if (count != level.num_old_vert) { PrintLine(LOG_ERROR, "ERROR: PutVertices miscounted (%zu != %zu)", count, level.num_old_vert); } } -static void PutVertices_Doom64(level_t &level) +static void PutVertices_Doom64(WadIO &io, level_t &level) { - // this size is worst-case scenario - size_t size = level.vertices.size() * sizeof(raw_vertex_doom64_t); - Lump_c *lump = CreateLevelLump(level, "VERTEXES", size); + size_t count = 0; + io.StartWritingLump("VERTEXES"); for (size_t i = 0; i < level.vertices.size(); i++) { @@ -146,28 +144,33 @@ static void PutVertices_Doom64(level_t &level) const vertex_t *vert = level.vertices[i]; - // do not ignore vertex_t::is_new - // it is required for leafs + // see: RoundOffVertices() + if (vert->is_new) + { + continue; + } raw.x = GetLittleEndian(FloatToFixed(vert->x)); raw.y = GetLittleEndian(FloatToFixed(vert->y)); - lump->Write(&raw, sizeof(raw_vertex_doom64_t)); + io.AddToLump(&raw, sizeof(raw_vertex_doom64_t)); } - lump->Finish(); + // sanity count + // double check that bsp-vertices are written out in the correct formats + if (count != level.num_old_vert) + { + PrintLine(LOG_ERROR, "ERROR: PutVertices miscounted (%zu != %zu)", count, level.num_old_vert); + } } // // Vanilla format // -static void PutSegs_Vanilla(level_t &level) +static void PutSegs_Vanilla(WadIO &io, level_t &level) { - // this size is worst-case scenario - size_t size = level.segs.size() * sizeof(raw_seg_vanilla_t); - - Lump_c *lump = CreateLevelLump(level, "SEGS", size); + io.StartWritingLump("SEGS"); for (size_t i = 0; i < level.segs.size(); i++) { @@ -182,24 +185,20 @@ static void PutSegs_Vanilla(level_t &level) raw.flip = GetLittleEndian(seg->side); raw.dist = GetLittleEndian(VanillaSegDist(seg)); - lump->Write(&raw, sizeof(raw_seg_vanilla_t)); - if (HAS_BIT(config.debug, DEBUG_BSP)) { PrintLine(LOG_DEBUG, "[%s] %zu Vert %04X->%04X Line %04X %s Angle %04X (%1.1f,%1.1f) -> (%1.1f,%1.1f)", __func__, seg->index, GetLittleEndian(raw.start), GetLittleEndian(raw.end), GetLittleEndian(raw.linedef), seg->side ? "L" : "R", GetLittleEndian(raw.angle), seg->start->x, seg->start->y, seg->end->x, seg->end->y); } - } - lump->Finish(); + io.AddToLump(&raw, sizeof(raw_seg_vanilla_t)); + } } -static void PutSubsecs_Vanilla(level_t &level) +static void PutSubsecs_Vanilla(WadIO &io, level_t &level) { - size_t size = level.subsecs.size() * sizeof(raw_subsec_vanilla_t); - - Lump_c *lump = CreateLevelLump(level, "SSECTORS", size); + io.StartWritingLump("SSECTORS"); for (size_t i = 0; i < level.subsecs.size(); i++) { @@ -210,7 +209,7 @@ static void PutSubsecs_Vanilla(level_t &level) raw.first = GetLittleEndian(IndexToShort(sub->seg_list->index)); raw.num = GetLittleEndian(IndexToShort(sub->seg_count)); - lump->Write(&raw, sizeof(raw_subsec_vanilla_t)); + io.AddToLump(&raw, sizeof(raw_subsec_vanilla_t)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -218,11 +217,9 @@ static void PutSubsecs_Vanilla(level_t &level) GetLittleEndian(raw.num)); } } - - lump->Finish(); } -static void PutOneNode_Vanilla(Lump_c *lump, node_t *node, size_t &node_cur_index) +static void PutOneNode_Vanilla(node_t *node, size_t &node_cur_index) { if (node->r.node) { @@ -279,7 +276,6 @@ static void PutOneNode_Vanilla(Lump_c *lump, node_t *node, size_t &node_cur_inde PrintLine(LOG_ERROR, "ERROR: Bad left child in node %zu", node->index); } - lump->Write(&raw, sizeof(raw_node_vanilla_t)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -288,19 +284,17 @@ static void PutOneNode_Vanilla(Lump_c *lump, node_t *node, size_t &node_cur_inde } } -static void PutNodes_Vanilla(level_t &level, node_t *root_node) +static void PutNodes_Vanilla(WadIO &io, level_t &level, node_t *root_node) { // this can be bigger than the actual size, but never smaller size_t max_size = (level.nodes.size() + 1) * sizeof(raw_node_vanilla_t); size_t node_cur_index = 0; - Lump_c *lump = CreateLevelLump(level, "NODES", max_size); if (root_node != nullptr) { PutOneNode_Vanilla(lump, root_node, node_cur_index); } - lump->Finish(); if (node_cur_index != level.nodes.size()) { @@ -308,9 +302,8 @@ static void PutNodes_Vanilla(level_t &level, node_t *root_node) } } -static void PutLeafs_Vanilla(level_t &level) +static void PutLeafs_Vanilla(WadIO &io, level_t &level) { - Lump_c *lump = CreateLevelLump(level, "LEAFS"); uint16_t actual_seg_index = 0; for (size_t i = 0; i < level.subsecs.size(); i++) @@ -319,7 +312,7 @@ static void PutLeafs_Vanilla(level_t &level) seg_t *seg = subsec->seg_list; size_t seg_count = subsec->seg_count; - lump->Write(&seg_count, sizeof(uint16_t)); + if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -350,7 +343,7 @@ static void PutLeafs_Vanilla(level_t &level) raw.seg = NO_INDEX_INT16; } - lump->Write(&raw, sizeof(raw)); + seg = seg->next; if (HAS_BIT(config.debug, DEBUG_BSP)) @@ -365,11 +358,10 @@ static void PutLeafs_Vanilla(level_t &level) // DeePBSPV4 format // -static void PutSegs_DeePBSPV4(level_t &level) +static void PutSegs_DeePBSPV4(WadIO &io, level_t &level) { // this size is worst-case scenario size_t size = level.segs.size() * sizeof(raw_seg_deepbspv4_t); - Lump_c *lump = CreateLevelLump(level, "SEGS", size); for (size_t i = 0; i < level.segs.size(); i++) { @@ -384,7 +376,7 @@ static void PutSegs_DeePBSPV4(level_t &level) raw.flip = GetLittleEndian(seg->side); raw.dist = GetLittleEndian(VanillaSegDist(seg)); - lump->Write(&raw, sizeof(raw_seg_deepbspv4_t)); + if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -394,14 +386,12 @@ static void PutSegs_DeePBSPV4(level_t &level) } } - lump->Finish(); } -static void PutSubsecs_DeePBSPV4(level_t &level) +static void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level) { size_t size = level.subsecs.size() * sizeof(raw_subsec_deepbspv4_t); - Lump_c *lump = CreateLevelLump(level, "SSECTORS", size); for (size_t i = 0; i < level.subsecs.size(); i++) { @@ -412,7 +402,7 @@ static void PutSubsecs_DeePBSPV4(level_t &level) raw.first = GetLittleEndian(IndexToInt(sub->seg_list->index)); raw.num = GetLittleEndian(IndexToShort(sub->seg_count)); - lump->Write(&raw, sizeof(raw_subsec_deepbspv4_t)); + if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -421,10 +411,9 @@ static void PutSubsecs_DeePBSPV4(level_t &level) } } - lump->Finish(); } -static void PutOneNode_DeePBSPV4(Lump_c *lump, node_t *node, size_t &node_cur_index) +static void PutOneNode_DeePBSPV4(node_t *node, size_t &node_cur_index) { if (node->r.node) { @@ -481,7 +470,6 @@ static void PutOneNode_DeePBSPV4(Lump_c *lump, node_t *node, size_t &node_cur_in PrintLine(LOG_ERROR, "ERROR: Bad left child in node %zu", node->index); } - lump->Write(&raw, sizeof(raw_node_deepbspv4_t)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -490,22 +478,19 @@ static void PutOneNode_DeePBSPV4(Lump_c *lump, node_t *node, size_t &node_cur_in } } -static void PutNodes_DeePBSPV4(level_t &level, node_t *root_node) +static void PutNodes_DeePBSPV4(WadIO &io, level_t &level, node_t *root_node) { // this can be bigger than the actual size, but never smaller // 8 bytes for BSP_MAGIC_DEEPBSPV4 header // size_t max_size = 8 + (level.nodes.size() + 1) * sizeof(raw_node_deepbspv4_t); size_t node_cur_index = 0; - Lump_c *lump = CreateLevelLump(level, "NODES"); - lump->Write("xNd4\0\0\0\0", 8); if (root_node != nullptr) { PutOneNode_DeePBSPV4(lump, root_node, node_cur_index); } - lump->Finish(); if (node_cur_index != level.nodes.size()) { @@ -513,9 +498,8 @@ static void PutNodes_DeePBSPV4(level_t &level, node_t *root_node) } } -static void PutLeafs_DeePBSPV4(level_t &level) +static void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) { - Lump_c *lump = CreateLevelLump(level, "LEAFS"); uint32_t actual_seg_index = 0; for (size_t i = 0; i < level.subsecs.size(); i++) @@ -524,7 +508,7 @@ static void PutLeafs_DeePBSPV4(level_t &level) seg_t *seg = subsec->seg_list; size_t seg_count = subsec->seg_count; - lump->Write(&seg_count, sizeof(uint32_t)); + if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -555,7 +539,7 @@ static void PutLeafs_DeePBSPV4(level_t &level) raw.seg = NO_INDEX_INT32; } - lump->Write(&raw, sizeof(raw)); + seg = seg->next; if (HAS_BIT(config.debug, DEBUG_BSP)) @@ -580,13 +564,11 @@ static inline uint32_t VertexIndex_XNOD(level_t &level, const vertex_t *v) return IndexToInt(v->index); } -static void PutVertices_Xnod(level_t &level, Lump_c *lump) +static void PutVertices_Xnod(WadIO &io, level_t &level) { size_t orgverts = GetLittleEndian(level.num_old_vert); size_t newverts = GetLittleEndian(level.num_new_vert); - lump->WriteZ(&orgverts, 4); - lump->WriteZ(&newverts, 4); size_t count = 0; for (size_t i = 0; i < level.vertices.size(); i++) @@ -603,7 +585,7 @@ static void PutVertices_Xnod(level_t &level, Lump_c *lump) raw.x = GetLittleEndian(FloatToFixed(vert->x)); raw.y = GetLittleEndian(FloatToFixed(vert->y)); - lump->WriteZ(&raw, sizeof(raw_vertex_xnod_t)); + count++; } @@ -614,10 +596,9 @@ static void PutVertices_Xnod(level_t &level, Lump_c *lump) } } -static void PutSubsecs_Xnod(level_t &level, Lump_c *lump) +static void PutSubsecs_Xnod(WadIO &io, level_t &level) { size_t raw_num = GetLittleEndian(level.subsecs.size()); - lump->WriteZ(&raw_num, 4); size_t cur_seg_index = 0; for (size_t i = 0; i < level.subsecs.size(); i++) @@ -625,7 +606,7 @@ static void PutSubsecs_Xnod(level_t &level, Lump_c *lump) const subsec_t *sub = level.subsecs[i]; raw_num = GetLittleEndian(sub->seg_count); - lump->WriteZ(&raw_num, 4); + // sanity check the seg index values size_t count = 0; @@ -651,10 +632,9 @@ static void PutSubsecs_Xnod(level_t &level, Lump_c *lump) } } -static void PutSegs_Xnod(level_t &level, Lump_c *lump) +static void PutSegs_Xnod(WadIO &io, level_t &level) { size_t raw_num = GetLittleEndian(level.segs.size()); - lump->WriteZ(&raw_num, 4); for (size_t i = 0; i < level.segs.size(); i++) { @@ -671,11 +651,11 @@ static void PutSegs_Xnod(level_t &level, Lump_c *lump) raw.end = GetLittleEndian(VertexIndex_XNOD(level, seg->end)); raw.linedef = GetLittleEndian(IndexToShort(seg->linedef->index)); raw.side = seg->side; - lump->WriteZ(&raw, sizeof(raw_seg_xnod_t)); + } } -static void PutOneNode_Xnod(Lump_c *lump, node_t *node, size_t &node_cur_index) +static void PutOneNode_Xnod(node_t *node, size_t &node_cur_index) { raw_node_xnod_t raw; @@ -732,7 +712,6 @@ static void PutOneNode_Xnod(Lump_c *lump, node_t *node, size_t &node_cur_index) PrintLine(LOG_ERROR, "ERROR: Bad left child in ZDoom node %zu", node->index); } - lump->WriteZ(&raw, sizeof(raw_node_xnod_t)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -741,11 +720,10 @@ static void PutOneNode_Xnod(Lump_c *lump, node_t *node, size_t &node_cur_index) } } -static void PutNodes_Xnod(level_t &level, Lump_c *lump, node_t *root) +static void PutNodes_Xnod(WadIO &io, level_t &level, node_t *root) { size_t node_cur_index = 0; size_t raw_num = GetLittleEndian(level.nodes.size()); - lump->WriteZ(&raw_num, 4); if (root) { @@ -758,7 +736,7 @@ static void PutNodes_Xnod(level_t &level, Lump_c *lump, node_t *root) } } -static size_t CalcXnodNodesSize(level_t &level) +static size_t CalcXnodNodesSize(WadIO &io, level_t &level) { // compute size of the ZDoom format nodes. // it does not need to be exact, but it *does* need to be bigger @@ -778,10 +756,9 @@ static size_t CalcXnodNodesSize(level_t &level) // ZDoom format -- XGLN, XGL2, XGL3 // -static void PutSegs_Xgln(level_t &level, Lump_c *lump) +static void PutSegs_Xgln(WadIO &io, level_t &level) { size_t raw_num = GetLittleEndian(level.segs.size()); - lump->WriteZ(&raw_num, 4); for (size_t i = 0; i < level.segs.size(); i++) { @@ -799,7 +776,7 @@ static void PutSegs_Xgln(level_t &level, Lump_c *lump) raw.linedef = GetLittleEndian(IndexToShort(seg->linedef ? seg->linedef->index : NO_INDEX)); raw.side = seg->side; - lump->WriteZ(&raw, sizeof(raw_seg_xgln_t)); + if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -809,10 +786,9 @@ static void PutSegs_Xgln(level_t &level, Lump_c *lump) } } -static void PutSegs_Xgl2(level_t &level, Lump_c *lump) +static void PutSegs_Xgl2(WadIO &io, level_t &level) { size_t raw_num = GetLittleEndian(level.segs.size()); - lump->WriteZ(&raw_num, 4); for (size_t i = 0; i < level.segs.size(); i++) { @@ -830,7 +806,7 @@ static void PutSegs_Xgl2(level_t &level, Lump_c *lump) raw.linedef = GetLittleEndian(IndexToInt(seg->linedef ? seg->linedef->index : NO_INDEX)); raw.side = seg->side; - lump->WriteZ(&raw, sizeof(raw_seg_xgl2_t)); + if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -840,7 +816,7 @@ static void PutSegs_Xgl2(level_t &level, Lump_c *lump) } } -static void PutOneNode_Xgl3(Lump_c *lump, node_t *node, size_t &node_cur_index) +static void PutOneNode_Xgl3(node_t *node, size_t &node_cur_index) { raw_node_xgl3_t raw; @@ -897,7 +873,6 @@ static void PutOneNode_Xgl3(Lump_c *lump, node_t *node, size_t &node_cur_index) PrintLine(LOG_ERROR, "ERROR: Bad left child in ZDoom node %zu", node->index); } - lump->WriteZ(&raw, sizeof(raw_node_xgl3_t)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -906,11 +881,10 @@ static void PutOneNode_Xgl3(Lump_c *lump, node_t *node, size_t &node_cur_index) } } -static void PutNodes_Xgl3(level_t &level, Lump_c *lump, node_t *root) +static void PutNodes_Xgl3(WadIO &io, level_t &level, node_t *root) { size_t node_cur_index = 0; size_t raw_num = GetLittleEndian(level.nodes.size()); - lump->WriteZ(&raw_num, 4); if (root) { @@ -927,7 +901,7 @@ static void PutNodes_Xgl3(level_t &level, Lump_c *lump, node_t *root) // Lump writing procedures // -void SaveDoom_DoomBSP(level_t &level, node_t *root_node) +void SaveDoom_DoomBSP(WadIO &io, level_t &level, node_t *root_node) { auto mark = Benchmarker(__func__); // remove all the minisegs from subsectors @@ -943,7 +917,7 @@ void SaveDoom_DoomBSP(level_t &level, node_t *root_node) PutNodes_Vanilla(level, root_node); } -void SaveDoom_DeePBSPV4(level_t &level, node_t *root_node) +void SaveDoom_DeePBSPV4(WadIO &io, level_t &level, node_t *root_node) { auto mark = Benchmarker(__func__); // remove all the minisegs from subsectors @@ -959,7 +933,7 @@ void SaveDoom_DeePBSPV4(level_t &level, node_t *root_node) PutNodes_DeePBSPV4(level, root_node); } -void SaveDoom_XNOD(level_t &level, node_t *root_node) +void SaveDoom_XNOD(WadIO &io, level_t &level, node_t *root_node) { auto mark = Benchmarker(__func__); CreateLevelLump(level, "SEGS")->Finish(); @@ -968,23 +942,20 @@ void SaveDoom_XNOD(level_t &level, node_t *root_node) NormaliseBspTree(level); SortSegs(level); - Lump_c *lump = CreateLevelLump(level, "NODES", CalcXnodNodesSize(level)); - if (level.bsp_compress) lump->Begin_Zlib(); + if (level.bsp_compress - lump->Write(level.bsp_compress ? "ZNOD" : "XNOD", 4); PutVertices_Xnod(level, lump); PutSubsecs_Xnod(level, lump); PutSegs_Xnod(level, lump); PutNodes_Xnod(level, lump, root_node); - if (level.bsp_compress) lump->Finish_Zlib(); + if (level.bsp_compress - lump->Finish(); lump = nullptr; } -void SaveDoom_XGLN(level_t &level, node_t *root_node) +void SaveDoom_XGLN(WadIO &io, level_t &level, node_t *root_node) { auto mark = Benchmarker(__func__); // leave SEGS empty @@ -992,26 +963,23 @@ void SaveDoom_XGLN(level_t &level, node_t *root_node) SortSegs(level); - Lump_c *lump = CreateLevelLump(level, "SSECTORS", CalcXnodNodesSize(level)); - if (level.bsp_compress) lump->Begin_Zlib(); + if (level.bsp_compress - lump->Write(level.bsp_compress ? "ZGLN" : "XGLN", 4); PutVertices_Xnod(level, lump); PutSubsecs_Xnod(level, lump); PutSegs_Xgln(level, lump); PutNodes_Xnod(level, lump, root_node); - if (level.bsp_compress) lump->Finish_Zlib(); + if (level.bsp_compress - lump->Finish(); lump = nullptr; // leave NODES empty CreateLevelLump(level, "NODES")->Finish(); } -void SaveDoom_XGL2(level_t &level, node_t *root_node) +void SaveDoom_XGL2(WadIO &io, level_t &level, node_t *root_node) { auto mark = Benchmarker(__func__); // leave SEGS empty @@ -1019,26 +987,23 @@ void SaveDoom_XGL2(level_t &level, node_t *root_node) SortSegs(level); - Lump_c *lump = CreateLevelLump(level, "SSECTORS", CalcXnodNodesSize(level)); - if (level.bsp_compress) lump->Begin_Zlib(); + if (level.bsp_compress - lump->Write(level.bsp_compress ? "ZGL2" : "XGL2", 4); PutVertices_Xnod(level, lump); PutSubsecs_Xnod(level, lump); PutSegs_Xgl2(level, lump); PutNodes_Xnod(level, lump, root_node); - if (level.bsp_compress) lump->Finish_Zlib(); + if (level.bsp_compress - lump->Finish(); lump = nullptr; // leave NODES empty CreateLevelLump(level, "NODES")->Finish(); } -void SaveDoom_XGL3(level_t &level, node_t *root_node) +void SaveDoom_XGL3(WadIO &io, level_t &level, node_t *root_node) { auto mark = Benchmarker(__func__); // leave SEGS empty @@ -1046,19 +1011,16 @@ void SaveDoom_XGL3(level_t &level, node_t *root_node) SortSegs(level); - Lump_c *lump = CreateLevelLump(level, "SSECTORS", CalcXnodNodesSize(level)); - if (level.bsp_compress) lump->Begin_Zlib(); + if (level.bsp_compress - lump->Write(level.bsp_compress ? "ZGL3" : "XGL3", 4); PutVertices_Xnod(level, lump); PutSubsecs_Xnod(level, lump); PutSegs_Xgl2(level, lump); PutNodes_Xgl3(level, lump, root_node); - if (level.bsp_compress) lump->Finish_Zlib(); + if (level.bsp_compress - lump->Finish(); lump = nullptr; // leave NODES empty @@ -1070,7 +1032,7 @@ void SaveDoom_XGL3(level_t &level, node_t *root_node) // This could also be shared with PSX Doom and PSX Final Doom, but we don't support those // -void SaveDoom64_DoomBSP(level_t &level, node_t *root_node) +void SaveDoom64_DoomBSP(WadIO &io, level_t &level, node_t *root_node) { // Needed for LEAFS RoundOffVertices(level); @@ -1085,7 +1047,7 @@ void SaveDoom64_DoomBSP(level_t &level, node_t *root_node) PutNodes_Vanilla(level, root_node); } -void SaveDoom64_DeePBSPV4(level_t &level, node_t *root_node) +void SaveDoom64_DeePBSPV4(WadIO &io, level_t &level, node_t *root_node) { // Needed for LEAFS RoundOffVertices(level); @@ -1104,21 +1066,16 @@ void SaveDoom64_DeePBSPV4(level_t &level, node_t *root_node) // Unlike the the Doom and Hexen map formats, UDMF has a tight requirement for fractional coordinates. // Always use the latest high-precision BSP format we support. // -void SaveTextmap_ZNODES(level_t &level, node_t *root_node) +void SaveTextmap_ZNODES(WadIO &io, level_t &level, node_t *root_node) { auto mark = Benchmarker(__func__); SortSegs(level); - Lump_c *lump = CreateLevelLump(level, "ZNODES", CalcXnodNodesSize(level)); - lump->Begin_Zlib(); - lump->Write("ZGL3", 4); PutVertices_Xnod(level, lump); PutSubsecs_Xnod(level, lump); PutSegs_Xgl2(level, lump); PutNodes_Xgl3(level, lump, root_node); - lump->Finish_Zlib(); - lump->Finish(); lump = nullptr; } diff --git a/src/bsp.hpp b/src/bsp.hpp new file mode 100644 index 0000000..f23c9df --- /dev/null +++ b/src/bsp.hpp @@ -0,0 +1,210 @@ +//------------------------------------------------------------------------------ +// +// ELFBSP +// +//------------------------------------------------------------------------------ +// +// Copyright 2025-2026 Guilherme Miranda +// Copyright 2000-2018 Andrew Apted, et al +// Copyright 1994-1998 Colin Reed +// Copyright 1997-1998 Lee Killough +// +// 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. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include "core.hpp" +#include "local.hpp" + +// +// Vanilla BSP +// +using raw_node_vanilla_t = struct raw_node_vanilla_s +{ + int16_t x, y; // starting point + int16_t dx, dy; // offset to ending point + raw_bbox_t b1, b2; // bounding rectangles + uint16_t right, left; // children: Node or SSector (if high bit is set) +} PACKEDATTR; + +using raw_subsec_vanilla_t = struct raw_subsec_vanilla_s +{ + uint16_t num; // number of Segs in this Sub-Sector + uint16_t first; // first Seg +} PACKEDATTR; + +using raw_seg_vanilla_t = struct raw_seg_vanilla_s +{ + uint16_t start; // from this vertex... + uint16_t end; // ... to this vertex + uint16_t angle; // angle (0 = east, 16384 = north, ...) + uint16_t linedef; // linedef that this seg goes along + uint16_t flip; // true if not the same direction as linedef + int16_t dist; // distance from starting point +} PACKEDATTR; + +using raw_leaf_vanilla_t = struct raw_leaf_vanilla_s +{ + uint16_t vertex; + uint16_t seg; +} PACKEDATTR; + +// +// DeepSea BSP +// * compared to vanilla, some types were raise to 32bit +// +using raw_node_deepbspv4_t = struct raw_node_deepbspv4_s +{ + int16_t x, y; // starting point + int16_t dx, dy; // offset to ending point + raw_bbox_t b1, b2; // bounding rectangles + uint32_t right, left; // children: Node or SSector (if high bit is set) +} PACKEDATTR; + +using raw_subsec_deepbspv4_t = struct raw_subsec_deepbspv4_s +{ + uint16_t num; // number of Segs in this Sub-Sector + uint32_t first; // first Seg +} PACKEDATTR; + +using raw_seg_deepbspv4_t = struct raw_seg_deepbspv4_s +{ + uint32_t start; // from this vertex... + uint32_t end; // ... to this vertex + uint16_t angle; // angle (0 = east, 16384 = north, ...) + uint16_t linedef; // linedef that this seg goes along + uint16_t flip; // true if not the same direction as linedef + int16_t dist; // distance from starting point +} PACKEDATTR; + +using raw_leaf_deepbspv4_t = struct raw_leaf_deepbspv4_s +{ + uint32_t vertex; + uint32_t seg; +} PACKEDATTR; + +// +// ZDoom BSP +// * compared to vanilla, some types were raise to 32bit +// * each version (XNOD->XGLN->XGL2->XGL3) builds on top of the previous +// +using raw_vertex_xnod_t = struct raw_vertex_xnod_s +{ + int32_t x; + int32_t y; +} PACKEDATTR; + +using raw_node_xnod_t = struct raw_node_xnod_s +{ + int16_t x, y; // starting point + int16_t dx, dy; // offset to ending point + raw_bbox_t b1, b2; // bounding rectangles + uint32_t right, left; // children: Node or SSector (if high bit is set) +} PACKEDATTR; + +using raw_subsec_xnod_t = struct raw_subsec_xnod_s +{ + uint32_t segnum; + // NOTE : no "first" value, segs must be contiguous and appear + // in an order dictated by the subsector list, e.g. all + // segs of the second subsector must appear directly after + // all segs of the first subsector. +} PACKEDATTR; + +using raw_seg_xnod_t = struct raw_seg_xnod_s +{ + uint32_t start; // from this vertex... + uint32_t end; // ... to this vertex + uint16_t linedef; // linedef that this seg goes along, or NO_INDEX + uint8_t side; // 0 if on right of linedef, 1 if on left +} PACKEDATTR; + +using raw_seg_xgln_t = struct raw_seg_xgln_s +{ + uint32_t vertex; // from this vertex ... to the next + uint32_t partner; // partner seg, unused by most ports outside of U/G/ZDoom + uint16_t linedef; // linedef that this seg goes along, or NO_INDEX + uint8_t side; // 0 if on right of linedef, 1 if on left +} PACKEDATTR; + +using raw_seg_xgl2_t = struct raw_seg_xgl2_s +{ + uint32_t vertex; // from this vertex ... to the next + uint32_t partner; // partner seg, unused by most ports outside of U/G/ZDoom + uint32_t linedef; // linedef that this seg goes along, or NO_INDEX + uint8_t side; // 0 if on right of linedef, 1 if on left +} PACKEDATTR; + +using raw_node_xgl3_t = struct raw_node_xgl3_s +{ + int32_t x, y; // starting point + int32_t dx, dy; // offset to ending point + raw_bbox_t b1, b2; // bounding rectangles + uint32_t right, left; // children: Node or SSector (if high bit is set) +} PACKEDATTR; + +static_size(raw_node_vanilla_t, 28); +static_size(raw_subsec_vanilla_t, 4); +static_size(raw_seg_vanilla_t, 12); +static_size(raw_node_deepbspv4_t, 32); +static_size(raw_subsec_deepbspv4_t, 6); +static_size(raw_seg_deepbspv4_t, 16); +static_size(raw_vertex_xnod_t, 8); +static_size(raw_node_xnod_t, 32); +static_size(raw_subsec_xnod_t, 4); +static_size(raw_seg_xnod_t, 11); +static_size(raw_seg_xgln_t, 11); +static_size(raw_seg_xgl2_t, 13); +static_size(raw_node_xgl3_t, 40); + +// put all the segs in each subsector into clockwise order, and renumber +// the seg indices. +// +// [ This cannot be done DURING BuildNodes() since splitting a seg with +// a partner will insert another seg into that partner's list, usually +// in the wrong place order-wise. ] +void ClockwiseBspTree(level_t &level); + +// traverse the BSP tree and do whatever is necessary to convert the +// node information from GL standard to normal standard (for example, +// removing minisegs). +void NormaliseBspTree(level_t &level); + +// Mark new vertices as old for writing into the VERTEXES lump +// Needed for RoundOffBspTree and saving to Doom 64 map formats directly +void RoundOffVertices(level_t &level); + +// traverse the BSP tree, doing whatever is necessary to round +// vertices to integer coordinates (for example, removing segs whose +// rounded coordinates degenerate to the same point). +void RoundOffBspTree(level_t &level); + +// both BLOCKAMP and REJECT exist as single lumps on all supported map formats +void InitBlockmap(level_t &level); +void PutBlockmap(level_t &level); +void PutReject(level_t &level); + +// the BSP tree lumps differ notably on each map format -- Doom/Hexen/Doom64 +// have NODES, SSECTORS & SEGS, but UDMF is generally only ZNODES, and there's +// some format overlap between them +void SaveDoom_DoomBSP(level_t &level, node_t *root_node); +void SaveDoom_DeePBSPV4(level_t &level, node_t *root_node); +void SaveDoom_XNOD(level_t &level, node_t *root_node); +void SaveDoom_XGLN(level_t &level, node_t *root_node); +void SaveDoom_XGL2(level_t &level, node_t *root_node); +void SaveDoom_XGL3(level_t &level, node_t *root_node); + +void SaveDoom64_DoomBSP(level_t &level, node_t *root_node); +void SaveDoom64_DeePBSPV4(level_t &level, node_t *root_node); + +void SaveTextmap_ZNODES(level_t &level, node_t *root_node); diff --git a/src/core.hpp b/src/core.hpp index 3034d48..ad54ec2 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -538,32 +538,6 @@ inline bool HasExtension(const char *filename) return false; } -// -// MatchExtension -// -// When ext is nullptr, checks if the file has no extension. -// -inline bool MatchExtension(const char *filename, const char *ext) -{ - if (!ext || !*ext) - { - return !HasExtension(filename); - } - - size_t A = strlen(filename); - size_t B = strlen(ext); - - while (A-- > 0 && B-- > 0) - { - if (toupper(static_cast(filename[A])) != toupper(static_cast(ext[B]))) - { - return false; - } - } - - return (B == NO_INDEX) && filename[A] == '.'; -} - // // FindExtension // @@ -624,26 +598,6 @@ inline short_angle_t ComputeAngle_BAM(double dx, double dy) return static_cast(floor(angle * FRACFACTOR / 360.0 + 0.5)); } -//------------------------------------------------------------------------ -// WAD STRUCTURES -//------------------------------------------------------------------------ - -// wad header -using raw_wad_header_t = struct raw_wad_header_s -{ - char ident[4]; - uint32_t num_entries; - uint32_t dir_start; -} PACKEDATTR; - -// directory entry -using raw_wad_entry_t = struct raw_wad_entry_s -{ - uint32_t pos; - uint32_t size; - char name[8]; -} PACKEDATTR; - //------------------------------------------------------------------------ // LEVEL STRUCTURES //------------------------------------------------------------------------ @@ -899,196 +853,10 @@ using raw_blockmap_xbm1_header_t = struct raw_blockmap_xbm1_header_s uint32_t x_blocks, y_blocks; } PACKEDATTR; -// -// Vanilla BSP -// -using raw_node_vanilla_t = struct raw_node_vanilla_s -{ - int16_t x, y; // starting point - int16_t dx, dy; // offset to ending point - raw_bbox_t b1, b2; // bounding rectangles - uint16_t right, left; // children: Node or SSector (if high bit is set) -} PACKEDATTR; - -using raw_subsec_vanilla_t = struct raw_subsec_vanilla_s -{ - uint16_t num; // number of Segs in this Sub-Sector - uint16_t first; // first Seg -} PACKEDATTR; - -using raw_seg_vanilla_t = struct raw_seg_vanilla_s -{ - uint16_t start; // from this vertex... - uint16_t end; // ... to this vertex - uint16_t angle; // angle (0 = east, 16384 = north, ...) - uint16_t linedef; // linedef that this seg goes along - uint16_t flip; // true if not the same direction as linedef - int16_t dist; // distance from starting point -} PACKEDATTR; - -using raw_leaf_vanilla_t = struct raw_leaf_vanilla_s -{ - uint16_t vertex; - uint16_t seg; -} PACKEDATTR; - -// -// DeepSea BSP -// * compared to vanilla, some types were raise to 32bit -// -using raw_node_deepbspv4_t = struct raw_node_deepbspv4_s -{ - int16_t x, y; // starting point - int16_t dx, dy; // offset to ending point - raw_bbox_t b1, b2; // bounding rectangles - uint32_t right, left; // children: Node or SSector (if high bit is set) -} PACKEDATTR; - -using raw_subsec_deepbspv4_t = struct raw_subsec_deepbspv4_s -{ - uint16_t num; // number of Segs in this Sub-Sector - uint32_t first; // first Seg -} PACKEDATTR; - -using raw_seg_deepbspv4_t = struct raw_seg_deepbspv4_s -{ - uint32_t start; // from this vertex... - uint32_t end; // ... to this vertex - uint16_t angle; // angle (0 = east, 16384 = north, ...) - uint16_t linedef; // linedef that this seg goes along - uint16_t flip; // true if not the same direction as linedef - int16_t dist; // distance from starting point -} PACKEDATTR; - -using raw_leaf_deepbspv4_t = struct raw_leaf_deepbspv4_s -{ - uint32_t vertex; - uint32_t seg; -} PACKEDATTR; - -// -// ZDoom BSP -// * compared to vanilla, some types were raise to 32bit -// * each version (XNOD->XGLN->XGL2->XGL3) builds on top of the previous -// -using raw_vertex_xnod_t = struct raw_vertex_xnod_s -{ - int32_t x; - int32_t y; -} PACKEDATTR; - -using raw_node_xnod_t = struct raw_node_xnod_s -{ - int16_t x, y; // starting point - int16_t dx, dy; // offset to ending point - raw_bbox_t b1, b2; // bounding rectangles - uint32_t right, left; // children: Node or SSector (if high bit is set) -} PACKEDATTR; - -using raw_subsec_xnod_t = struct raw_subsec_xnod_s -{ - uint32_t segnum; - // NOTE : no "first" value, segs must be contiguous and appear - // in an order dictated by the subsector list, e.g. all - // segs of the second subsector must appear directly after - // all segs of the first subsector. -} PACKEDATTR; - -using raw_seg_xnod_t = struct raw_seg_xnod_s -{ - uint32_t start; // from this vertex... - uint32_t end; // ... to this vertex - uint16_t linedef; // linedef that this seg goes along, or NO_INDEX - uint8_t side; // 0 if on right of linedef, 1 if on left -} PACKEDATTR; - -using raw_seg_xgln_t = struct raw_seg_xgln_s -{ - uint32_t vertex; // from this vertex ... to the next - uint32_t partner; // partner seg, unused by most ports outside of U/G/ZDoom - uint16_t linedef; // linedef that this seg goes along, or NO_INDEX - uint8_t side; // 0 if on right of linedef, 1 if on left -} PACKEDATTR; - -using raw_seg_xgl2_t = struct raw_seg_xgl2_s -{ - uint32_t vertex; // from this vertex ... to the next - uint32_t partner; // partner seg, unused by most ports outside of U/G/ZDoom - uint32_t linedef; // linedef that this seg goes along, or NO_INDEX - uint8_t side; // 0 if on right of linedef, 1 if on left -} PACKEDATTR; - -using raw_node_xgl3_t = struct raw_node_xgl3_s -{ - int32_t x, y; // starting point - int32_t dx, dy; // offset to ending point - raw_bbox_t b1, b2; // bounding rectangles - uint32_t right, left; // children: Node or SSector (if high bit is set) -} PACKEDATTR; - -/* ----- Graphical structures ---------------------- */ - -using raw_patchdef_t = struct raw_patchdef_s -{ - int16_t x_origin; - int16_t y_origin; - uint16_t pname; // index into PNAMES - uint16_t stepdir; // NOT USED - uint16_t colormap; // NOT USED -} PACKEDATTR; - -using raw_strife_patchdef_t = struct raw_strife_patchdef_s -{ - int16_t x_origin; - int16_t y_origin; - uint16_t pname; // index into PNAMES -} PACKEDATTR; - -// Texture definition. -// -// Each texture is composed of one or more patches, -// with patches being lumps stored in the WAD. -// -using raw_texture_t = struct raw_texture_s -{ - char name[8]; - uint32_t masked; // NOT USED - uint16_t width; - uint16_t height; - uint16_t column_dir[2]; // NOT USED - uint16_t patch_count; - raw_patchdef_t patches[1]; -} PACKEDATTR; - -using raw_strife_texture_t = struct raw_strife_texture_s -{ - char name[8]; - uint32_t masked; // NOT USED - uint16_t width; - uint16_t height; - uint16_t patch_count; - raw_strife_patchdef_t patches[1]; -} PACKEDATTR; - -// Patches. -// -// A patch holds one or more columns. -// Patches are used for sprites and all masked pictures, -// and we compose textures from the TEXTURE1/2 lists -// of patches. -// -using raw_patch_t = struct patch_s -{ - int16_t width; // bounding box size - int16_t height; // - int16_t leftoffset; // pixels to the left of origin - int16_t topoffset; // pixels below the origin - uint32_t columnofs[1]; // only [width] used -} PACKEDATTR; - // Fail way earlier -static_assert(sizeof(raw_wad_header_t) == 12, "Size mismatch for 'raw_wad_header_t'. Should be 12."); -static_assert(sizeof(raw_wad_entry_t) == 16, "Size mismatch for 'raw_wad_entry_t'. Should be 16."); +#define static_size(x, y) \ + static_assert(sizeof(x) == y, "Size mismatch for '" #x "'. Should be " #y ".") + static_assert(sizeof(raw_vertex_t) == 4, "Size mismatch for 'raw_vertex_t'. Should be 4."); static_assert(sizeof(raw_linedef_doom_t) == 14, "Size mismatch for 'raw_linedef_doom_t'. Should be 14."); static_assert(sizeof(raw_sidedef_doom_t) == 30, "Size mismatch for 'raw_sidedef_doom_t'. Should be 30."); @@ -1104,24 +872,6 @@ static_assert(sizeof(raw_sector_doom64_t) == 24, "Size mismatch for 'raw_sector_ static_assert(sizeof(raw_bbox_t) == 8, "Size mismatch for 'raw_bbox_t'. Should be 8."); static_assert(sizeof(raw_blockmap_header_t) == 8, "Size mismatch for 'raw_blockmap_header_t'. Should be 8."); static_assert(sizeof(raw_blockmap_xbm1_header_t) == 16, "Size mismatch for 'raw_blockmap_xbm1_header_t'. Should be 16."); -static_assert(sizeof(raw_node_vanilla_t) == 28, "Size mismatch for 'raw_node_vanilla_t'. Should be 28."); -static_assert(sizeof(raw_subsec_vanilla_t) == 4, "Size mismatch for 'raw_subsec_vanilla_t'. Should be 4."); -static_assert(sizeof(raw_seg_vanilla_t) == 12, "Size mismatch for 'raw_seg_vanilla_t'. Should be 12."); -static_assert(sizeof(raw_node_deepbspv4_t) == 32, "Size mismatch for 'raw_node_deepbspv4_t'. Should be 32."); -static_assert(sizeof(raw_subsec_deepbspv4_t) == 6, "Size mismatch for 'raw_subsec_deepbspv4_t'. Should be 6."); -static_assert(sizeof(raw_seg_deepbspv4_t) == 16, "Size mismatch for 'raw_seg_deepbspv4_t'. Should be 16."); -static_assert(sizeof(raw_vertex_xnod_t) == 8, "Size mismatch for 'raw_vertex_xnod_t'. Should be 8."); -static_assert(sizeof(raw_node_xnod_t) == 32, "Size mismatch for 'raw_node_xnod_t'. Should be 32."); -static_assert(sizeof(raw_subsec_xnod_t) == 4, "Size mismatch for 'raw_subsec_xnod_t'. Should be 4."); -static_assert(sizeof(raw_seg_xnod_t) == 11, "Size mismatch for 'raw_seg_xnod_t'. Should be 11."); -static_assert(sizeof(raw_seg_xgln_t) == 11, "Size mismatch for 'raw_seg_xgln_t'. Should be 11."); -static_assert(sizeof(raw_seg_xgl2_t) == 13, "Size mismatch for 'raw_seg_xgl2_t'. Should be 13."); -static_assert(sizeof(raw_node_xgl3_t) == 40, "Size mismatch for 'raw_node_xgl3_t'. Should be 40."); -static_assert(sizeof(raw_patchdef_t) == 10, "Size mismatch for 'raw_patchdef_t'. Should be 10."); -static_assert(sizeof(raw_strife_patchdef_t) == 6, "Size mismatch for 'raw_strife_patchdef_t'. Should be 6."); -static_assert(sizeof(raw_texture_t) == 32, "Size mismatch for 'raw_texture_t'. Should be 32."); -static_assert(sizeof(raw_strife_texture_t) == 24, "Size mismatch for 'raw_strife_texture_t'. Should be 24."); -static_assert(sizeof(raw_patch_t) == 12, "Size mismatch for 'raw_patch_t'. Should be 12."); // // LineDef attributes. @@ -1360,372 +1110,6 @@ using doomednum_t = enum doomednum_e : int16_t Hexen_PolyObj_SpawnHurt = 3003, // does any port actually handle this? }; -// -// File handling -// - -struct Lump_c; - -struct Wad_file -{ - char mode; // mode value passed to ::Open() - - FILE *fp; - - char kind; // 'P' for PWAD, 'I' for IWAD - - // zero means "currently unknown", which only occurs after a - // call to BeginWrite() and before any call to AddLump() or - // the finalizing EndWrite(). - int64_t total_size; - - std::vector directory; - - size_t dir_start; - size_t dir_count; - - // these are lump indices (into 'directory' vector) - std::vector levels; - std::vector patches; - std::vector sprites; - std::vector flats; - std::vector tx_tex; - - bool begun_write; - size_t begun_max_size; - - // when >= 0, the next added lump is placed _before_ this - size_t insert_point; - - // constructor is private - Wad_file(const char *_name, char _mode, FILE *_fp); - ~Wad_file(void); - - // open a wad file. - // - // mode is similar to the fopen() function: - // 'r' opens the wad for reading ONLY - // 'a' opens the wad for appending (read and write) - // 'w' opens the wad for writing (i.e. create it) - // - // Note: if 'a' is used and the file is read-only, it will be - // silently opened in 'r' mode instead. - // - static Wad_file *Open(const char *filename, char mode = 'a'); - - [[nodiscard]] bool IsReadOnly(void) const - { - return mode == 'r'; - } - - [[nodiscard]] size_t NumLumps(void) const - { - return directory.size(); - } - - Lump_c *GetLump(size_t index); - - [[nodiscard]] size_t LevelCount(void) const - { - return levels.size(); - } - - size_t LevelHeader(size_t lev_num); - size_t LevelLastLump(size_t lev_num); - - // returns a lump index, -1 if not found - size_t LevelLookupLump(size_t lev_num, const char *name); - - map_format_t LevelFormat(size_t lev_num); - - void SortLevels(void); - - // all changes to the wad must occur between calls to BeginWrite() - // and EndWrite() methods. the on-disk wad directory may be trashed - // during this period, it will be re-written by EndWrite(). - void BeginWrite(void); - void EndWrite(void); - - // remove the given lump(s) - // this will change index numbers on existing lumps - // (previous results of FindLumpNum or LevelHeader are invalidated). - void RemoveLumps(size_t index, size_t count = 1); - - // insert a new lump. - // The second form is for a level marker. - // The 'max_size' parameter (if >= 0) specifies the most data - // you will write into the lump -- writing more will corrupt - // something else in the WAD. - Lump_c *AddLump(const char *name, size_t max_size = NO_INDEX); - - // setup lump to write new data to it. - // the old contents are lost. - void RecreateLump(Lump_c *lump, size_t max_size = NO_INDEX); - - // set the insertion point -- the next lump will be added *before* - // this index, and it will be incremented so that a sequence of - // AddLump() calls produces lumps in the same order. - // - // passing a negative value or invalid index will reset the - // insertion point -- future lumps get added at the END. - // EndWrite() also reset it. - void InsertPoint(size_t index = NO_INDEX); - - static Wad_file *Create(const char *filename, char mode); - - // read the existing directory. - void ReadDirectory(void); - - void DetectLevels(void); - void ProcessNamespaces(void); - - // look at all the lumps and determine the lowest offset from - // start of file where we can write new data. The directory itself - // is ignored for this. - size_t HighWaterMark(void); - - // look at all lumps in directory and determine the lowest offset - // where a lump of the given length will fit. Returns same as - // HighWaterMark() when no largest gaps exist. The directory itself - // is ignored since it will be re-written at EndWrite(). - size_t FindFreeSpace(size_t length); - - // find a place (possibly at end of WAD) where we can write some - // data of max_size (-1 means unlimited), and seek to that spot - // (possibly writing some padding zeros -- the difference should - // be no more than a few bytes). Returns new position. - size_t PositionForWrite(size_t max_size = NO_INDEX); - - void FinishLump(size_t final_size); - size_t WritePadding(size_t count); - - // write the new directory, updating the dir_xxx variables - void WriteDirectory(void); - - void FixGroup(std::vector &group, size_t index, size_t num_added, size_t num_removed); -}; - -struct Lump_c -{ - struct Wad_file *parent; - - std::string lumpname; - - size_t l_start; - size_t l_length; - - bool zlib_init = false; - zng_stream zout_stream; - uint8_t zout_buffer[1024]; - - void MakeEntry(raw_wad_entry_t *entry); - - [[nodiscard]] const char *Name(void) const - { - return lumpname.c_str(); - } - - [[nodiscard]] size_t Length(void) const - { - return l_length; - } - - // case-insensitive match on the lump name - [[nodiscard]] bool Match(const char *s) const - { - return (0 == StringCaseCmp(lumpname.c_str(), s)); - } - - // ensure lump name is uppercase - void Rename(const char *new_name) - { - lumpname.clear(); - - for (const char *s = new_name; *s != 0; s++) - { - lumpname.push_back(static_cast(toupper(*s))); - } - } - - // attempt to seek to a position within the lump (default is - // the beginning). Returns true if OK, false on error. - [[nodiscard]] bool Seek(const size_t offset) const - { - return (fseek(parent->fp, static_cast(l_start + offset), SEEK_SET) == 0); - } - - // read some data from the lump, returning true if OK. - [[nodiscard]] bool Read(void *data, const size_t len) const - { - SYS_ASSERT(data && len > 0); - return (fread(data, len, 1, parent->fp) == 1); - } - - // write some data to the lump. Only the lump which had just - // been created with Wad_file::AddLump() or RecreateLump() can be - // written to. - bool Write(const void *data, const size_t len) - { - SYS_ASSERT(data && len > 0); - l_length += len; - return (fwrite(data, len, 1, parent->fp) == 1); - } - - // mark the lump as finished (after writing data to it). - void Finish(void) - { - if (l_length == 0) - { - l_start = 0; - } - - parent->FinishLump(l_length); - } - - // - // Zlib compression support - // - - void Begin_Zlib(void) - { - zlib_init = true; - zout_stream.zalloc = nullptr; - zout_stream.zfree = nullptr; - zout_stream.opaque = nullptr; - if (Z_OK != zng_deflateInit(&zout_stream, Z_DEFAULT_COMPRESSION)) - { - PrintLine(LOG_ERROR, "ERROR: Trouble starting Zlib compression."); - } - zout_stream.next_out = zout_buffer; - zout_stream.avail_out = sizeof(zout_buffer); - } - - void WriteZ(const void *data, uint32_t length) - { - if (!zlib_init) - { - this->Write(data, length); - return; - } - - SYS_ASSERT(length > 0); - - zout_stream.next_in = static_cast(data); - zout_stream.avail_in = length; - - while (zout_stream.avail_in > 0) - { - if (Z_OK != zng_deflate(&zout_stream, Z_NO_FLUSH)) - { - PrintLine(LOG_ERROR, "ERROR: Trouble Zlib compressing %d bytes.", length); - } - - if (zout_stream.avail_out == 0) - { - this->Write(zout_buffer, sizeof(zout_buffer)); - - zout_stream.next_out = zout_buffer; - zout_stream.avail_out = sizeof(zout_buffer); - } - } - } - - void Finish_Zlib(void) - { - zlib_init = false; - SYS_ASSERT(zout_stream.avail_out > 0) - size_t left_over; - - zout_stream.next_in = Z_NULL; - zout_stream.avail_in = 0; - - while (true) - { - int32_t err = zng_deflate(&zout_stream, Z_FINISH); - - if (err == Z_STREAM_END) break; - - if (err != Z_OK) - { - PrintLine(LOG_ERROR, "ERROR: Trouble finishing Zlib compression."); - } - - if (zout_stream.avail_out == 0) - { - this->Write(zout_buffer, sizeof(zout_buffer)); - - zout_stream.next_out = zout_buffer; - zout_stream.avail_out = sizeof(zout_buffer); - } - } - - left_over = sizeof(zout_buffer) - zout_stream.avail_out; - - if (left_over > 0) this->Write(zout_buffer, left_over); - - zng_deflateEnd(&zout_stream); - } -}; - -// -// Parsing -// - -enum token_kind_e -{ - TOK_EOF = 0, - TOK_ERROR, - - TOK_Ident, - TOK_Symbol, - TOK_Number, - TOK_String -}; - -struct lexer_c -{ - explicit lexer_c(const std::string &_data) : data(_data) - { - } - - ~lexer_c(void) = default; - - // parse the next token, storing contents into given string. - // returns TOK_EOF at the end of the data, and TOK_ERROR when a - // problem is encountered (s will be an error message). - token_kind_e Next(std::string &s); - - // check if the next token is an identifier or symbol matching the - // given string. the match is not case-sensitive. if it matches, - // the token is consumed and true is returned. if not, false is - // returned and the position is unchanged. - bool Match(const char *s); - - // rewind to the very beginning. - void Rewind(void); - - const std::string &data; - - size_t pos = 0; - size_t line = 1; - - void SkipToNext(); - - token_kind_e ParseIdentifier(std::string &s); - token_kind_e ParseNumber(std::string &s); - token_kind_e ParseString(std::string &s); - - void ParseEscape(std::string &s); -}; - -// helpers for converting numeric tokens. -size_t LEX_Index(const std::string &s); -int16_t LEX_Int16(const std::string &s); -int32_t LEX_Int(const std::string &s); -uint32_t LEX_UInt(const std::string &s); -double LEX_Double(const std::string &s); -bool LEX_Boolean(const std::string &s); - // // Node Build Information Structure // diff --git a/src/level.cpp b/src/level.cpp index 4568821..9b6e4f6 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -21,16 +21,16 @@ // //------------------------------------------------------------------------------ +#include "bsp.hpp" #include "core.hpp" #include "local.hpp" +#include "parse.hpp" #include #include #include -Wad_file *cur_wad; - -int CheckLinedefInsideBox(int xmin, int ymin, int xmax, int ymax, int x1, int y1, int x2, int y2) +bool CheckLinedefInsideBox(int xmin, int ymin, int xmax, int ymax, int x1, int y1, int x2, int y2) { int count = 2; int tmp; @@ -353,43 +353,18 @@ void ValidateLinedef(level_t &level, linedef_t *line) static void GetVertices_Doom(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("VERTEXES"); - - if (lump) - { - count = lump->Length() / sizeof(raw_vertex_t); - } + auto lump = io->ReadMapLump(level, "VERTEXES"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - if (lump == nullptr || count == 0) + for (size_t i = 0; i < lump.size(); i++) { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to vertices."); - } - - for (size_t i = 0; i < count; i++) - { - raw_vertex_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading vertices."); - } - vertex_t *vert = NewVertex(level); - - vert->x = ShortToFloat(GetLittleEndian(raw.x)); - vert->y = ShortToFloat(GetLittleEndian(raw.y)); + vert->x = ShortToFloat(GetLittleEndian(lump[i].x)); + vert->y = ShortToFloat(GetLittleEndian(lump[i].y)); } level.num_old_vert = level.vertices.size(); @@ -397,182 +372,86 @@ static void GetVertices_Doom(level_t &level) static void GetSectors_Doom(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("SECTORS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_sector_doom_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to sectors."); - } + auto lump = io->ReadMapLump(level, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_sector_doom_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading sectors."); - } - sector_t *sector = NewSector(level); - sector->effects = FX_Sector_None; } } static void GetThings_Doom(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("THINGS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_thing_doom_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to things."); - } + auto lump = io->ReadMapLump(level, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_thing_doom_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading things."); - } - thing_t *thing = NewThing(level); - thing->x = GetLittleEndian(raw.x); - thing->y = GetLittleEndian(raw.y); - thing->type = static_cast(GetLittleEndian(raw.type)); + thing->x = GetLittleEndian(lump[i].x); + thing->y = GetLittleEndian(lump[i].y); + thing->type = static_cast(GetLittleEndian(lump[i].type)); } } static void GetSidedefs_Doom(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("SIDEDEFS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_sidedef_doom_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to sidedefs."); - } + auto lump = io->ReadMapLump(level, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_sidedef_doom_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading sidedefs."); - } - sidedef_t *side = NewSidedef(level); - side->offset_x = GetLittleEndian(raw.x_offset); - side->offset_y = GetLittleEndian(raw.y_offset); - memcpy(side->tex_upper, raw.upper_tex, 8); - memcpy(side->tex_lower, raw.lower_tex, 8); - memcpy(side->tex_middle, raw.mid_tex, 8); - side->sector = level.SafeLookupSector(GetLittleEndian(raw.sector), i); + side->offset_x = GetLittleEndian(lump[i].x_offset); + side->offset_y = GetLittleEndian(lump[i].y_offset); + memcpy(side->tex_upper, lump[i].upper_tex, 8); + memcpy(side->tex_lower, lump[i].lower_tex, 8); + memcpy(side->tex_middle, lump[i].mid_tex, 8); + side->sector = level.SafeLookupSector(GetLittleEndian(lump[i].sector), i); } } static void GetLinedefs_Doom(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("LINEDEFS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_linedef_doom_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to linedefs."); - } + auto lump = io->ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_linedef_doom_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading linedefs."); - } linedef_t *line = NewLinedef(level); - line->start = level.SafeLookupVertex(GetLittleEndian(raw.start), i); - line->end = level.SafeLookupVertex(GetLittleEndian(raw.end), i); - line->right = level.SafeLookupSidedef(GetLittleEndian(raw.right)); - line->left = level.SafeLookupSidedef(GetLittleEndian(raw.left)); - line->special = GetLittleEndian(raw.special); - line->tag = GetLittleEndian(raw.tag); + line->start = level.SafeLookupVertex(GetLittleEndian(lump[i].start), i); + line->end = level.SafeLookupVertex(GetLittleEndian(lump[i].end), i); + line->right = level.SafeLookupSidedef(GetLittleEndian(lump[i].right)); + line->left = level.SafeLookupSidedef(GetLittleEndian(lump[i].left)); + line->special = GetLittleEndian(lump[i].special); + line->tag = GetLittleEndian(lump[i].tag); line->start->is_used = true; line->end->is_used = true; - if (HAS_BIT(GetLittleEndian(raw.flags), MLF_TWOSIDED)) + if (HAS_BIT(GetLittleEndian(lump[i].flags), MLF_TWOSIDED)) { line->effects |= FX_TwoSided; } @@ -632,94 +511,46 @@ static void GetLinedefs_Doom(level_t &level) static void GetThings_Hexen(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("THINGS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_thing_hexen_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to things."); - } + auto lump = io->ReadMapLump(level, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_thing_hexen_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading things."); - } - thing_t *thing = NewThing(level); - thing->x = GetLittleEndian(raw.x); - thing->y = GetLittleEndian(raw.y); - thing->type = static_cast(GetLittleEndian(raw.type)); + thing->x = GetLittleEndian(lump[i].x); + thing->y = GetLittleEndian(lump[i].y); + thing->type = static_cast(GetLittleEndian(lump[i].type)); } } static void GetLinedefs_Hexen(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("LINEDEFS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_linedef_hexen_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to linedefs."); - } + auto lump = io->ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_linedef_hexen_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading linedefs."); - } - linedef_t *line = NewLinedef(level); - line->start = level.SafeLookupVertex(GetLittleEndian(raw.start), i); - line->end = level.SafeLookupVertex(GetLittleEndian(raw.end), i); - line->special = raw.special; - line->right = level.SafeLookupSidedef(GetLittleEndian(raw.right)); - line->left = level.SafeLookupSidedef(GetLittleEndian(raw.left)); + line->start = level.SafeLookupVertex(GetLittleEndian(lump[i].start), i); + line->end = level.SafeLookupVertex(GetLittleEndian(lump[i].end), i); + line->special = lump[i].special; + line->right = level.SafeLookupSidedef(GetLittleEndian(lump[i].right)); + line->left = level.SafeLookupSidedef(GetLittleEndian(lump[i].left)); line->start->is_used = true; line->end->is_used = true; - if (HAS_BIT(GetLittleEndian(raw.flags), MLF_HEXEN_TWOSIDED)) + if (HAS_BIT(GetLittleEndian(lump[i].flags), MLF_HEXEN_TWOSIDED)) { line->effects |= FX_TwoSided; } @@ -731,11 +562,11 @@ static void GetLinedefs_Hexen(level_t &level) switch (line->special) { case BSP_SpecialEffects: - if (raw.args[0]) line->effects |= FX_NoBlockmap; - if (raw.args[1]) line->effects |= FX_DoNotSplitSeg; - if (raw.args[2]) line->effects |= FX_DoNotRenderBack; - if (raw.args[3]) line->effects |= FX_DoNotRenderFront; - if (raw.args[4]) line->effects |= FX_NoReject; + if (lump[i].args[0]) line->effects |= FX_NoBlockmap; + if (lump[i].args[1]) line->effects |= FX_DoNotSplitSeg; + if (lump[i].args[2]) line->effects |= FX_DoNotRenderBack; + if (lump[i].args[3]) line->effects |= FX_DoNotRenderFront; + if (lump[i].args[4]) line->effects |= FX_NoReject; break; default: break; @@ -745,43 +576,19 @@ static void GetLinedefs_Hexen(level_t &level) static void GetVertices_Doom64(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("VERTEXES"); - - if (lump) - { - count = lump->Length() / sizeof(raw_vertex_doom64_t); - } + auto lump = io->ReadMapLump(level, "VERTEXES"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - if (lump == nullptr || count == 0) + for (size_t i = 0; i < lump.size(); i++) { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "Error seeking to 32bit vertices."); - } - - for (size_t i = 0; i < count; i++) - { - raw_vertex_doom64_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "Error reading 32bit vertices."); - } - vertex_t *vert = NewVertex(level); - vert->x = static_cast(GetLittleEndian(raw.x) / FRACFACTOR); - vert->y = static_cast(GetLittleEndian(raw.y) / FRACFACTOR); + vert->x = static_cast(GetLittleEndian(lump[i].x) / FRACFACTOR); + vert->y = static_cast(GetLittleEndian(lump[i].y) / FRACFACTOR); } level.num_old_vert = level.vertices.size(); @@ -789,44 +596,20 @@ static void GetVertices_Doom64(level_t &level) static void GetSectors_Doom64(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("SECTORS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_sector_doom64_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "Error seeking to Doom64 sectors."); - } + auto lump = io->ReadMapLump(level, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_sector_doom64_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "Error reading Doom64 sectors."); - } - sector_t *sector = NewSector(level); if (!config.effects) continue; - if (GetLittleEndian(raw.special) == SS_Doom64_NoReject) + if (GetLittleEndian(lump[i].special) == SS_Doom64_NoReject) { sector->effects = FX_Sector_NoReject; } @@ -835,96 +618,48 @@ static void GetSectors_Doom64(level_t &level) static void GetSidedefs_Doom64(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("SIDEDEFS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_sidedef_doom64_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "Error seeking to Doom64 sidedefs."); - } + auto lump = io->ReadMapLump(level, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_sidedef_doom64_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "Error reading Doom64 sidedefs."); - } - sidedef_t *side = NewSidedef(level); - side->offset_x = GetLittleEndian(raw.x_offset); - side->offset_y = GetLittleEndian(raw.y_offset); + side->offset_x = GetLittleEndian(lump[i].x_offset); + side->offset_y = GetLittleEndian(lump[i].y_offset); // We don't care about texture indexes here - side->sector = level.SafeLookupSector(GetLittleEndian(raw.sector), i); + side->sector = level.SafeLookupSector(GetLittleEndian(lump[i].sector), i); } } static void GetLinedefs_Doom64(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("LINEDEFS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_linedef_doom64_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "Error seeking to Doom64 linedefs."); - } + auto lump = io->ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_linedef_doom64_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "Error reading Doom64 linedefs."); - } - linedef_t *line = NewLinedef(level); - line->start = level.SafeLookupVertex(GetLittleEndian(raw.start), i); - line->end = level.SafeLookupVertex(GetLittleEndian(raw.end), i); - line->special = GetLittleEndian(raw.special); - line->tag = GetLittleEndian(raw.tag); - line->right = level.SafeLookupSidedef(GetLittleEndian(raw.right)); - line->left = level.SafeLookupSidedef(GetLittleEndian(raw.left)); + line->start = level.SafeLookupVertex(GetLittleEndian(lump[i].start), i); + line->end = level.SafeLookupVertex(GetLittleEndian(lump[i].end), i); + line->special = GetLittleEndian(lump[i].special); + line->tag = GetLittleEndian(lump[i].tag); + line->right = level.SafeLookupSidedef(GetLittleEndian(lump[i].right)); + line->left = level.SafeLookupSidedef(GetLittleEndian(lump[i].left)); line->start->is_used = true; line->end->is_used = true; - auto flags = GetLittleEndian(raw.flags); + auto flags = GetLittleEndian(lump[i].flags); if (HAS_BIT(flags, MLF_DOOM64_TWOSIDED)) { @@ -948,44 +683,20 @@ static void GetLinedefs_Doom64(level_t &level) static void GetThings_Doom64(level_t &level) { - size_t count = 0; - - Lump_c *lump = level.FindLevelLump("THINGS"); - - if (lump) - { - count = lump->Length() / sizeof(raw_thing_doom64_t); - } - - if (lump == nullptr || count == 0) - { - return; - } - - if (!lump->Seek(0)) - { - PrintLine(LOG_ERROR, "Error seeking to Doom64 things."); - } + auto lump = io->ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, count); + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < count; i++) + for (size_t i = 0; i < lump.size(); i++) { - raw_thing_doom64_t raw; - - if (!lump->Read(&raw, sizeof(raw))) - { - PrintLine(LOG_ERROR, "Error reading Doom64 things."); - } - thing_t *thing = NewThing(level); - thing->x = GetLittleEndian(raw.x); - thing->y = GetLittleEndian(raw.y); - thing->type = static_cast(GetLittleEndian(raw.type)); + thing->x = GetLittleEndian(lump[i].x); + thing->y = GetLittleEndian(lump[i].y); + thing->type = static_cast(GetLittleEndian(lump[i].type)); } } @@ -1293,33 +1004,10 @@ static void ParseUDMF_Pass(level_t &level, const std::string &data, int pass) void ParseUDMF(level_t &level) { - Lump_c *lump = level.FindLevelLump("TEXTMAP"); - - if (lump == nullptr || !lump->Seek(0)) - { - PrintLine(LOG_ERROR, "ERROR: Failure finding TEXTMAP lump."); - } - - size_t remain = lump->Length(); + auto lump = io->ReadMapLump(level, "TEXTMAP"); // load the lump into this string - std::string data; - - while (remain > 0) - { - char buffer[4096]; - - size_t want = std::min(remain, sizeof(buffer)); - - if (!lump->Read(buffer, want)) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading TEXTMAP lump."); - } - - data.append(buffer, want); - - remain -= want; - } + auto data = std::string(lump.begin(), lump.end()); // now parse it... @@ -1397,11 +1085,11 @@ bsp_format_t CheckFormatBSP(buildinfo_t &ctx, level_t &level) void LoadLevel(level_t &level) { auto mark = Benchmarker(__func__); - Lump_c *LEV = cur_wad->GetLump(level.level_header_lump_index); + auto LEV = io->LumpName(level.level_header_lump_index); level.overflows = false; - PrintLine(LOG_NORMAL, "[%s] Reading %s", __func__, LEV->Name()); + PrintLine(LOG_NORMAL, "[%s] Reading %s", __func__, LEV); level.num_new_vert = 0; level.num_real_lines = 0; @@ -1436,7 +1124,7 @@ void LoadLevel(level_t &level) ParseUDMF(level); break; case MapFormat_INVALID: - PrintLine(LOG_ERROR, "[%s] Unknown level format on level %s", __func__, LEV->Name()); + PrintLine(LOG_ERROR, "[%s] Unknown level format on level %s", __func__, LEV); break; } @@ -1463,221 +1151,14 @@ void LoadLevel(level_t &level) } } -void FreeLevel(level_t &level) -{ - FreeVertices(level); - FreeSidedefs(level); - FreeLinedefs(level); - FreeSectors(level); - FreeThings(level); - FreeSegs(level); - FreeSubsecs(level); - FreeNodes(level); - FreeWallTips(level); - FreeIntersections(level); -} - -static void AddMissingLump(level_t &level, const char *name, const char *after) -{ - if (cur_wad->LevelLookupLump(level.level_num, name) != NO_INDEX) - { - return; - } - - size_t exist = cur_wad->LevelLookupLump(level.level_num, after); - - // if this happens, the level structure is very broken - if (exist == NO_INDEX) - { - PrintLine(LOG_NORMAL, "WARNING: Missing %s lump -- level structure is broken", after); - config.total_warnings++; - exist = cur_wad->LevelLastLump(level.level_num); - } - - cur_wad->InsertPoint(exist + 1); - cur_wad->AddLump(name)->Finish(); -} - -build_result_e SaveLevelBinaryFormat(level_t &level, node_t *root_node) -{ - // Note: root_node may be nullptr - - cur_wad->BeginWrite(); - - // ensure all necessary level lumps are present - AddMissingLump(level, "SEGS", "VERTEXES"); - AddMissingLump(level, "SSECTORS", "SEGS"); - AddMissingLump(level, "NODES", "SSECTORS"); - AddMissingLump(level, "SECTORS", "NODES"); - AddMissingLump(level, "REJECT", "SECTORS"); - AddMissingLump(level, "BLOCKMAP", "REJECT"); - - // No need to add BEHAVIOR, LIGHTS or MACROS - if (level.map_format == MapFormat_Doom64) - { - AddMissingLump(level, "LEAFS", "BLOCKMAP"); - } - - // check for overflows... - CheckBinaryFormatLimits(level); - - // If using DoomBSP format, bump to DeePBSPV4 on overflow - RaiseValue(level.bsp_format, CheckFormatBSP(config, level)); - - // Using Zlib-compressed version of ZDBSP lump format - level.bsp_compress |= config.compress; - - if (level.map_format == MapFormat_Doom64) - { - switch (level.bsp_format) - { - case BSP_DeePBSPV4: - SaveDoom64_DeePBSPV4(level, root_node); - break; - case BSP_DoomBSP: - SaveDoom64_DoomBSP(level, root_node); - break; - default: - PrintLine(LOG_ERROR, "ERROR: Tried to write unsupported BSP format #%d on Doom64 map format", level.bsp_format); - break; - } - } - else // MapFormat_Doom or MapFormat_Hexen - { - switch (level.bsp_format) - { - case BSP_XGL3: - SaveDoom_XGL3(level, root_node); - break; - case BSP_XGL2: - SaveDoom_XGL2(level, root_node); - break; - case BSP_XGLN: - SaveDoom_XGLN(level, root_node); - break; - case BSP_XNOD: - SaveDoom_XNOD(level, root_node); - break; - case BSP_DeePBSPV4: - SaveDoom_DeePBSPV4(level, root_node); - break; - case BSP_DoomBSP: - SaveDoom_DoomBSP(level, root_node); - break; - } - } - - PutBlockmap(level); - PutReject(level); - - cur_wad->EndWrite(); - - if (level.overflows) - { - // no message here - // [ in verbose mode, each overflow already printed a message ] - // [ in normal mode, we don't want any messages at all ] - return BUILD_LumpOverflow; - } - - return BUILD_OK; -} - -build_result_e SaveLevelTextMap(level_t &level, node_t *root_node) -{ - cur_wad->BeginWrite(); - - Lump_c *lump = CreateLevelLump(level, "ZNODES"); - AddMissingLump(level, "REJECT", "ZNODES"); - AddMissingLump(level, "BLOCKMAP", "REJECT"); - - if (level.num_real_lines == 0) - { - lump->Finish(); - } - else - { - SaveTextmap_ZNODES(level, root_node); - } - - PutBlockmap(level); - PutReject(level); - - cur_wad->EndWrite(); - - return BUILD_OK; -} - -/* ---------------------------------------------------------------- */ - -Lump_c *CreateLevelLump(level_t &level, const char *name, size_t max_size) -{ - // look for existing one - Lump_c *lump = level.FindLevelLump(name); - - if (lump) - { - cur_wad->RecreateLump(lump, max_size); - } - else - { - size_t last_idx = cur_wad->LevelLastLump(level.level_num); - - // in UDMF maps, insert before the ENDMAP lump, otherwise insert - // after the last known lump of the level. - if (level.map_format != MapFormat_UDMF) - { - last_idx += 1; - } - - cur_wad->InsertPoint(last_idx); - - lump = cur_wad->AddLump(name, max_size); - } - - return lump; -} +// +// INSERT OUTPUT HERE +// //------------------------------------------------------------------------ // MAIN STUFF //------------------------------------------------------------------------ -void OpenWad(const char *filename) -{ - cur_wad = Wad_file::Open(filename, 'a'); - if (cur_wad == nullptr) - { - PrintLine(LOG_ERROR, "ERROR: Cannot open file: %s", filename); - } - - if (cur_wad->IsReadOnly()) - { - delete cur_wad; - cur_wad = nullptr; - PrintLine(LOG_ERROR, "ERROR: file is read only: %s", filename); - } -} - -void CloseWad(void) -{ - if (cur_wad != nullptr) - { - // this closes the file - delete cur_wad; - cur_wad = nullptr; - } -} - -size_t LevelsInWad(void) -{ - if (cur_wad == nullptr) - { - return 0; - } - - return cur_wad->LevelCount(); -} - size_t ComputeBspHeight(const node_t *node) { if (node == nullptr) @@ -1747,7 +1228,16 @@ build_result_e BuildLevel(level_t &level, const char *filename) break; } - FreeLevel(level); + FreeVertices(level); + FreeSidedefs(level); + FreeLinedefs(level); + FreeSectors(level); + FreeThings(level); + FreeSegs(level); + FreeSubsecs(level); + FreeNodes(level); + FreeWallTips(level); + FreeIntersections(level); if (config.analysis) { diff --git a/src/local.hpp b/src/local.hpp index dffa9f9..781f1b2 100644 --- a/src/local.hpp +++ b/src/local.hpp @@ -24,21 +24,16 @@ #pragma once #include "core.hpp" +#include "wad.hpp" #include -struct Lump_c; -struct Wad_file; - -// current WAD file -extern Wad_file *cur_wad; - //------------------------------------------------------------------------ // BLOCKMAP : Generate the blockmap //------------------------------------------------------------------------ // utility routines... -int CheckLinedefInsideBox(int xmin, int ymin, int xmax, int ymax, int x1, int y1, int x2, int y2); +bool CheckLinedefInsideBox(int xmin, int ymin, int xmax, int ymax, int x1, int y1, int x2, int y2); //------------------------------------------------------------------------ // LEVEL : Level structures & read/write functions. @@ -429,18 +424,11 @@ using level_t = struct level_t double block_compression; bmap_format_t bmap_format = bmap_format_t::BMAP_DoomBSP; - inline Lump_c *FindLevelLump(const char *name) - { - SYS_ASSERT(cur_wad != nullptr); - size_t idx = cur_wad->LevelLookupLump(level_num, name); - return (idx != NO_INDEX) ? cur_wad->GetLump(idx) : nullptr; - } - inline const char *GetLevelName(void) + inline const char *GetLevelName(WadIO &io) { - SYS_ASSERT(cur_wad != nullptr); - size_t lump_idx = cur_wad->LevelHeader(level_num); - return cur_wad->GetLump(lump_idx)->Name(); + size_t lump_idx = io.LevelHeader(level_num); + return io.read_directory[lump_idx].name; } vertex_t *SafeLookupVertex(size_t num, size_t num_line) @@ -505,7 +493,6 @@ void FreeNodes(level_t &level); void FreeWallTips(level_t &level); void FreeIntersections(level_t &level); -Lump_c *CreateLevelLump(level_t &level, const char *name, size_t max_size = NO_INDEX); //------------------------------------------------------------------------ // ANALYZE : Analyzing level structures @@ -569,45 +556,3 @@ void BuildNodes(level_t &level, seg_t *seg_list, int depth, bbox_t *bounds, node // compute the height of the bsp tree, starting at 'node'. size_t ComputeBspHeight(const node_t *node); - -// put all the segs in each subsector into clockwise order, and renumber -// the seg indices. -// -// [ This cannot be done DURING BuildNodes() since splitting a seg with -// a partner will insert another seg into that partner's list, usually -// in the wrong place order-wise. ] -void ClockwiseBspTree(level_t &level); - -// traverse the BSP tree and do whatever is necessary to convert the -// node information from GL standard to normal standard (for example, -// removing minisegs). -void NormaliseBspTree(level_t &level); - -// Mark new vertices as old for writing into the VERTEXES lump -// Needed for RoundOffBspTree and saving to Doom 64 map formats directly -void RoundOffVertices(level_t &level); - -// traverse the BSP tree, doing whatever is necessary to round -// vertices to integer coordinates (for example, removing segs whose -// rounded coordinates degenerate to the same point). -void RoundOffBspTree(level_t &level); - -// both BLOCKAMP and REJECT exist as single lumps on all supported map formats -void InitBlockmap(level_t &level); -void PutBlockmap(level_t &level); -void PutReject(level_t &level); - -// the BSP tree lumps differ notably on each map format -- Doom/Hexen/Doom64 -// have NODES, SSECTORS & SEGS, but UDMF is generally only ZNODES, and there's -// some format overlap between them -void SaveDoom_DoomBSP(level_t &level, node_t *root_node); -void SaveDoom_DeePBSPV4(level_t &level, node_t *root_node); -void SaveDoom_XNOD(level_t &level, node_t *root_node); -void SaveDoom_XGLN(level_t &level, node_t *root_node); -void SaveDoom_XGL2(level_t &level, node_t *root_node); -void SaveDoom_XGL3(level_t &level, node_t *root_node); - -void SaveDoom64_DoomBSP(level_t &level, node_t *root_node); -void SaveDoom64_DeePBSPV4(level_t &level, node_t *root_node); - -void SaveTextmap_ZNODES(level_t &level, node_t *root_node); diff --git a/src/main.cpp b/src/main.cpp index 28af59f..7e5097c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "core.hpp" #include "local.hpp" +#include "wad.hpp" #include #include @@ -95,7 +96,7 @@ static void BuildFile(const char *filename) { config.total_warnings = 0; - const size_t num_levels = LevelsInWad(); + const size_t num_levels = io->LevelCount(); if (num_levels == 0) { @@ -115,8 +116,8 @@ static void BuildFile(const char *filename) // IMPORTANT: always ensure a valid map level_t level; level.level_num = n; - level.level_header_lump_index = cur_wad->LevelHeader(level.level_num); - level.map_format = cur_wad->LevelFormat(level.level_num); + level.level_header_lump_index = io->LevelHeader(level.level_num); + level.map_format = io->LevelFormat(level.level_num); if (!CheckMapInMapList(level.GetLevelName())) { @@ -163,31 +164,6 @@ static void BuildFile(const char *filename) PrintLine(LOG_NORMAL, "Serious warnings: %zu", config.total_warnings); } -void ValidateInputFilename(const char *filename) -{ - // NOTE: these checks are case-insensitive - - // files with ".bak" extension cannot be backed up, so refuse them - if (MatchExtension(filename, "bak")) - { - PrintLine(LOG_ERROR, "ERROR: cannot process a backup file: %s", filename); - } - - // we do not support packages - if (MatchExtension(filename, "pak") || MatchExtension(filename, "pk2") || MatchExtension(filename, "pk3") - || MatchExtension(filename, "pk4") || MatchExtension(filename, "pk7") || MatchExtension(filename, "epk") - || MatchExtension(filename, "pack") || MatchExtension(filename, "zip") || MatchExtension(filename, "rar")) - { - PrintLine(LOG_ERROR, "ERROR: package files (like PK3) are not supported: %s", filename); - } - - // reject anything that isn't a WAD, or a UDB temp file - if (!MatchExtension(filename, "wad") && !MatchExtension(filename, "tmp")) - { - PrintLine(LOG_ERROR, "ERROR: not a wad file: %s", filename); - } -} - void BackupFile(const char *filename) { std::string dest_name = filename; @@ -238,11 +214,20 @@ void VisitFile(const char *filename) PrintLine(LOG_NORMAL, "Building %s", filename); // this will fatal error if it fails - OpenWad(filename); + io = new WadIO(filename); + if (io == nullptr) + { + PrintLine(LOG_ERROR, "ERROR: Cannot open file: %s", filename); + } BuildFile(filename); - CloseWad(); + if (io != nullptr) + { + // this closes the file + delete io; + io = nullptr; + } } // ----- user information ----------------------------- @@ -760,11 +745,9 @@ int32_t main(const int32_t argc, const char *argv[]) // validate all filenames before processing any of them for (const auto filename : wad_list) { - ValidateInputFilename(filename); - if (!FileExists(filename)) { - PrintLine(LOG_ERROR, "ERROR: no such file: %s", filename); + throw std::runtime_error("ERROR: no such file: " + std::string(filename)); } } diff --git a/src/parse.cpp b/src/parse.cpp index f4d6bd8..739ead5 100644 --- a/src/parse.cpp +++ b/src/parse.cpp @@ -20,6 +20,7 @@ //------------------------------------------------------------------------------ #include "core.hpp" +#include "parse.hpp" #include #include diff --git a/src/parse.hpp b/src/parse.hpp new file mode 100644 index 0000000..94d47dc --- /dev/null +++ b/src/parse.hpp @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// +// ELFBSP -- Lexer (tokenixer) +// +//------------------------------------------------------------------------------ +// +// Copyright 2025-2026 Guilherme Miranda +// Copyright 2022 Andrew Apted, et al +// +// 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. +// +//------------------------------------------------------------------------------ + +#pragma once +// #include "core.hpp" + +// +// Parsing +// + +#include +#include + +enum token_kind_e +{ + TOK_EOF = 0, + TOK_ERROR, + + TOK_Ident, + TOK_Symbol, + TOK_Number, + TOK_String +}; + +struct lexer_c +{ + explicit lexer_c(const std::string &_data) : data(_data) + { + } + + ~lexer_c(void) = default; + + // parse the next token, storing contents into given string. + // returns TOK_EOF at the end of the data, and TOK_ERROR when a + // problem is encountered (s will be an error message). + token_kind_e Next(std::string &s); + + // check if the next token is an identifier or symbol matching the + // given string. the match is not case-sensitive. if it matches, + // the token is consumed and true is returned. if not, false is + // returned and the position is unchanged. + bool Match(const char *s); + + // rewind to the very beginning. + void Rewind(void); + + const std::string &data; + + size_t pos = 0; + size_t line = 1; + + void SkipToNext(); + + token_kind_e ParseIdentifier(std::string &s); + token_kind_e ParseNumber(std::string &s); + token_kind_e ParseString(std::string &s); + + void ParseEscape(std::string &s); +}; + +// helpers for converting numeric tokens. +size_t LEX_Index(const std::string &s); +int16_t LEX_Int16(const std::string &s); +int32_t LEX_Int(const std::string &s); +uint32_t LEX_UInt(const std::string &s); +double LEX_Double(const std::string &s); +bool LEX_Boolean(const std::string &s); diff --git a/src/reject.cpp b/src/reject.cpp index 43634b5..74c3f66 100644 --- a/src/reject.cpp +++ b/src/reject.cpp @@ -167,25 +167,19 @@ static void Reject_DebugGroups(level_t &level) } } -static void Reject_WriteLump(level_t &level) -{ - Lump_c *lump = CreateLevelLump(level, "REJECT", level.reject_size); - lump->Write(level.reject_matrix, level.reject_size); - lump->Finish(); -} - // // For now we only do very basic reject processing, limited to // determining all isolated groups of sectors (islands that are // surrounded by void space). // -void PutReject(level_t &level) +void PutReject(WadIO &io, level_t &level) { auto mark = Benchmarker(__func__); + io.StartWritingLump("REJECT"); + + // just leave an empty reject lump if (level.sectors.size() == 0) { - // just create an empty reject lump - CreateLevelLump(level, "REJECT")->Finish(); return; } @@ -193,8 +187,9 @@ void PutReject(level_t &level) Reject_GroupSectors(level); Reject_ProcessSectors(level); Reject_DebugGroups(level); - Reject_WriteLump(level); + io.AddToLump(level.reject_matrix, level.reject_size); Reject_Free(level); + if (config.verbose) { PrintLine(LOG_NORMAL, "Reject size: %zu", level.reject_size); diff --git a/src/wad.cpp b/src/wad.cpp index 49639fa..55a3dd9 100644 --- a/src/wad.cpp +++ b/src/wad.cpp @@ -19,290 +19,132 @@ // //------------------------------------------------------------------------------ +#include "wad.hpp" #include "core.hpp" +#include "local.hpp" -#include -#include #include +#include //------------------------------------------------------------------------ -// LUMP Handling +// Utilities //------------------------------------------------------------------------ -Lump_c *MakeLump(Wad_file *wad, const char *lumpname, size_t l_start, size_t l_length) -{ - Lump_c *new_lump = new Lump_c; - new_lump->Rename(lumpname); - new_lump->parent = wad; - new_lump->l_start = l_start; - new_lump->l_length = l_length; - return new_lump; -} - -Lump_c *MakeLumpFromEntry(Wad_file *wad, const raw_wad_entry_t *entry) +static size_t WhatLevelPart(const char *name) { - Lump_c *new_lump = new Lump_c; + if (StringCaseCmp(name, "THINGS") == 0) return 1; + if (StringCaseCmp(name, "LINEDEFS") == 0) return 2; + if (StringCaseCmp(name, "SIDEDEFS") == 0) return 3; + if (StringCaseCmp(name, "VERTEXES") == 0) return 4; + if (StringCaseCmp(name, "SECTORS") == 0) return 5; - // handle the entry name, which can lack a terminating NUL - char buffer[9]; - strncpy(buffer, entry->name, 8); - buffer[8] = 0; - new_lump->Rename(buffer); - new_lump->parent = wad; - new_lump->l_start = GetLittleEndian(entry->pos); - new_lump->l_length = GetLittleEndian(entry->size); - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] New lump '%s' @ %zu len:%zu", __func__, new_lump->Name(), new_lump->l_start, new_lump->l_length); - } - return new_lump; + return 0; } -void Lump_c::MakeEntry(raw_wad_entry_t *entry) +static bool IsLevelLump(const char *name) { - // do a dance to avoid a compiler warning from strncpy(), *sigh* - memset(entry->name, 0, 8); - memcpy(entry->name, lumpname.c_str(), lumpname.size()); + if (StringCaseCmp(name, "SEGS") == 0) return true; + if (StringCaseCmp(name, "SSECTORS") == 0) return true; + if (StringCaseCmp(name, "NODES") == 0) return true; + if (StringCaseCmp(name, "REJECT") == 0) return true; + if (StringCaseCmp(name, "BLOCKMAP") == 0) return true; + if (StringCaseCmp(name, "BEHAVIOR") == 0) return true; + if (StringCaseCmp(name, "SCRIPTS") == 0) return true; + if (StringCaseCmp(name, "LEAFS") == 0) return true; + if (StringCaseCmp(name, "LIGHTS") == 0) return true; + if (StringCaseCmp(name, "MACROS") == 0) return true; - entry->pos = GetLittleEndian(IndexToInt(l_start)); - entry->size = GetLittleEndian(IndexToInt(l_length)); + return WhatLevelPart(name) != 0; } //------------------------------------------------------------------------ // WAD Reading Interface //------------------------------------------------------------------------ -Wad_file::Wad_file(const char *_name, char _mode, FILE *_fp) - : mode(_mode), fp(_fp), kind('P'), total_size(0), directory(), dir_start(0), dir_count(0), levels(), patches(), sprites(), - flats(), tx_tex(), begun_write(false), insert_point(NO_INDEX) -{ - // nothing needed -} - -Wad_file::~Wad_file(void) +WadIO::WadIO(const char *filename) : read_file(nullptr), read_directory(), read_levels() { - fclose(fp); - - // free the directory - for (size_t k = 0; k < NumLumps(); k++) - { - delete directory[k]; - } - - directory.clear(); -} - -Wad_file *Wad_file::Open(const char *filename, char mode) -{ - SYS_ASSERT(mode == 'r' || mode == 'w' || mode == 'a'); - - if (mode == 'w') + this->read_file = fopen(filename, "rb"); + if (this->read_file == NULL) { - return Create(filename, mode); + throw std::runtime_error("ERROR: Could not open input file"); } - if (HAS_BIT(config.debug, DEBUG_WAD)) + this->SafeRead(&this->read_header, sizeof(this->read_header)); + if (StringCaseCmpMax(this->read_header.ident, "PWAD", 4) != 0 || StringCaseCmpMax(this->read_header.ident, "IWAD", 4) != 0) { - PrintLine(LOG_DEBUG, "[%s] Opening WAD file: %s", __func__, filename); + fclose(read_file); + read_file = NULL; + throw std::runtime_error("ERROR: Input file is not a wad"); } - FILE *fp = nullptr; - -retry: - fp = fopen(filename, (mode == 'r' ? "rb" : "r+b")); + this->read_header.num_entries = GetLittleEndian(this->read_header.num_entries); + this->read_header.dir_start = GetLittleEndian(this->read_header.dir_start); - if (!fp) + if (fseek(read_file, this->read_header.dir_start, SEEK_SET)) { - // mimic the fopen() semantics - if (mode == 'a' && errno == ENOENT) - { - return Create(filename, mode); - } - - // if file is read-only, open in 'r' mode instead - if (mode == 'a' && (errno == EACCES || errno == EROFS)) - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Open r/w failed, trying again in read mode...", __func__); - } - mode = 'r'; - goto retry; - } - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Open file failed: %s", __func__, strerror(errno)); - } - return nullptr; - } - - Wad_file *w = new Wad_file(filename, mode, fp); - - // determine total size (seek to end) - if (fseek(fp, 0, SEEK_END) != 0) - { - PrintLine(LOG_ERROR, "ERROR: Failure determining WAD size."); + throw std::runtime_error("ERROR: Could not read wad directory"); } - w->total_size = ftell(fp); + this->read_directory.resize(this->read_header.num_entries); + this->SafeRead(this->read_directory.data(), this->read_header.num_entries * sizeof(WadLump)); - if (HAS_BIT(config.debug, DEBUG_WAD)) + for (size_t i = 0; i < this->read_header.num_entries; ++i) { - PrintLine(LOG_DEBUG, "[%s] total_size = %zu", __func__, static_cast(w->total_size)); + this->read_directory[i].pos = GetLittleEndian(this->read_directory[i].pos); + this->read_directory[i].size = GetLittleEndian(this->read_directory[i].size); } - - if (w->total_size < 0) - { - PrintLine(LOG_ERROR, "ERROR: Failure determining WAD size."); - } - - w->ReadDirectory(); - w->DetectLevels(); - w->ProcessNamespaces(); - - return w; } -Wad_file *Wad_file::Create(const char *filename, char mode) +WadIO::~WadIO(void) { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Creating new WAD file: %s", __func__, filename); - } - - FILE *fp = fopen(filename, "w+b"); - if (!fp) - { - return nullptr; - } - - Wad_file *w = new Wad_file(filename, mode, fp); - - // write out base header - raw_wad_header_t header; - - memset(&header, 0, sizeof(header)); - memcpy(header.ident, "PWAD", 4); - - fwrite(&header, sizeof(header), 1, fp); - fflush(fp); - - w->total_size = sizeof(header); - - return w; + fclose(read_file); + this->read_directory.clear(); } -static size_t WhatLevelPart(const char *name) +// call only after seeking +void WadIO::SafeRead(void *buffer, size_t size) { - if (StringCaseCmp(name, "THINGS") == 0) - { - return 1; - } - if (StringCaseCmp(name, "LINEDEFS") == 0) - { - return 2; - } - if (StringCaseCmp(name, "SIDEDEFS") == 0) - { - return 3; - } - if (StringCaseCmp(name, "VERTEXES") == 0) - { - return 4; - } - if (StringCaseCmp(name, "SECTORS") == 0) + if (fread(buffer, 1, size, this->read_file) != size) { - return 5; + PrintLine(LOG_ERROR, "Failed to read"); } - - return 0; } -static bool IsLevelLump(const char *name) +size_t WadIO::NumLumps(void) const { - if (StringCaseCmp(name, "SEGS") == 0) - { - return true; - } - if (StringCaseCmp(name, "SSECTORS") == 0) - { - return true; - } - if (StringCaseCmp(name, "NODES") == 0) - { - return true; - } - if (StringCaseCmp(name, "REJECT") == 0) - { - return true; - } - if (StringCaseCmp(name, "BLOCKMAP") == 0) - { - return true; - } - if (StringCaseCmp(name, "BEHAVIOR") == 0) - { - return true; - } - if (StringCaseCmp(name, "SCRIPTS") == 0) - { - return true; - } - if (StringCaseCmp(name, "LEAFS") == 0) - { - return true; - } - if (StringCaseCmp(name, "LIGHTS") == 0) - { - return true; - } - if (StringCaseCmp(name, "MACROS") == 0) - { - return true; - } - - return WhatLevelPart(name) != 0; + return this->read_directory.size(); } -Lump_c *Wad_file::GetLump(size_t index) +size_t WadIO::LevelCount(void) const { - SYS_ASSERT(index < NumLumps()); - SYS_ASSERT(directory[index]); - - return directory[index]; + return this->read_levels.size(); } -size_t Wad_file::LevelLookupLump(size_t lev_num, const char *name) +const char *WadIO::LumpName(size_t lump_index) const { - size_t start = LevelHeader(lev_num); - size_t finish = LevelLastLump(lev_num); - - for (size_t k = start + 1; k <= finish; k++) - { - SYS_ASSERT(k < NumLumps()); - - if (directory[k]->Match(name)) - { - return k; - } - } + static char name[9]; + strncpy(name, this->read_directory[lump_index].name, 8); + name[8] = 0; + return name; +} - return NO_INDEX; // not found +size_t WadIO::LevelHeader(size_t lev_num) +{ + SYS_ASSERT(lev_num < LevelCount()); + return this->read_levels[lev_num]; } -size_t Wad_file::LevelLastLump(size_t lev_num) +size_t WadIO::LevelLastLump(size_t lev_num) { - size_t start = LevelHeader(lev_num); + size_t start = this->LevelHeader(lev_num); size_t count = 1; // UDMF level? - if (directory[start + 1]->Match("TEXTMAP")) + if (StringCaseCmp(read_directory[start + 1].name, "TEXTMAP")) { - while (count < MAX_LUMPS_IN_A_LEVEL && start + count < NumLumps()) + while (count < MAX_LUMPS_IN_A_LEVEL && start + count < this->NumLumps()) { - if (directory[start + count]->Match("ENDMAP")) + if (StringCaseCmp(read_directory[start + count].name, "ENDMAP")) { count++; break; @@ -313,7 +155,7 @@ size_t Wad_file::LevelLastLump(size_t lev_num) } else // standard DOOM or HEXEN format { - while (count < MAX_LUMPS_IN_A_LEVEL && start + count < NumLumps() && IsLevelLump(directory[start + count]->Name())) + while (count < MAX_LUMPS_IN_A_LEVEL && start + count < this->NumLumps() && IsLevelLump(read_directory[start + count].name)) { count++; } @@ -322,28 +164,40 @@ size_t Wad_file::LevelLastLump(size_t lev_num) return start + count - 1; } -size_t Wad_file::LevelHeader(size_t lev_num) +// returns a lump index, NO_INDEX if not found +size_t WadIO::LevelLookupLump(size_t lev_num, const char *name) { - SYS_ASSERT(lev_num < LevelCount()); + size_t start = this->LevelHeader(lev_num); + size_t finish = this->LevelLastLump(lev_num); - return levels[lev_num]; + for (size_t k = start + 1; k <= finish; k++) + { + SYS_ASSERT(k < this->NumLumps()); + + if (StringCaseCmp(read_directory[k].name, name) == 0) + { + return k; + } + } + + return NO_INDEX; // not found } -map_format_e Wad_file::LevelFormat(size_t lev_num) +map_format_e WadIO::LevelFormat(size_t lev_num) { // UDMF maps can contain BEHAVIOR or MACROS // check exclusively TEXTMAP - if (LevelLookupLump(lev_num, "TEXTMAP") != NO_INDEX) + if (this->LevelLookupLump(lev_num, "TEXTMAP") != NO_INDEX) { return MapFormat_UDMF; } - if (LevelLookupLump(lev_num, "BEHAVIOR") != NO_INDEX) + if (this->LevelLookupLump(lev_num, "BEHAVIOR") != NO_INDEX) { return MapFormat_Hexen; } - if (LevelLookupLump(lev_num, "LIGHTS") != NO_INDEX && LevelLookupLump(lev_num, "MACROS") != NO_INDEX) + if (this->LevelLookupLump(lev_num, "LIGHTS") != NO_INDEX && this->LevelLookupLump(lev_num, "MACROS") != NO_INDEX) { return MapFormat_Doom64; } @@ -351,54 +205,7 @@ map_format_e Wad_file::LevelFormat(size_t lev_num) return MapFormat_Doom; } -void Wad_file::ReadDirectory(void) -{ - // WISH: no fatal errors - - rewind(fp); - - raw_wad_header_t header; - - if (fread(&header, sizeof(header), 1, fp) != 1) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading WAD header."); - } - - // WISH: check ident for PWAD or IWAD - - kind = header.ident[0]; - - dir_start = GetLittleEndian(header.dir_start); - dir_count = GetLittleEndian(header.num_entries); - - if (dir_count > 32000) - { - PrintLine(LOG_ERROR, "ERROR: Bad WAD header, too many entries (%zu)", dir_count); - } - - if (fseek(fp, static_cast(dir_start), SEEK_SET) != 0) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to WAD directory."); - } - - for (size_t i = 0; i < dir_count; i++) - { - raw_wad_entry_t entry; - - if (fread(&entry, sizeof(entry), 1, fp) != 1) - { - PrintLine(LOG_ERROR, "ERROR: Failure reading WAD directory."); - } - - Lump_c *lump = MakeLumpFromEntry(this, &entry); - - // WISH: check if entry is valid - - directory.push_back(lump); - } -} - -void Wad_file::DetectLevels(void) +void WadIO::DetectLevels(void) { // Determine what lumps in the wad are level markers, based on the // lumps which follow it. Store the result in the 'levels' vector. @@ -411,18 +218,18 @@ void Wad_file::DetectLevels(void) // Ignore non-header map lumps // Fixes sliding window bug on single-level WADs - if (WhatLevelPart(directory[k]->Name()) != 0) + if (WhatLevelPart(read_directory[k].name) != 0) { continue; } // check for UDMF levels - if (directory[k + 1]->Match("TEXTMAP")) + if (StringCaseCmp(read_directory[k + 1].name, "TEXTMAP")) { - levels.push_back(k); + this->read_levels.push_back(k); if (HAS_BIT(config.debug, DEBUG_WAD)) { - PrintLine(LOG_DEBUG, "[%s] Detected level : %s (UDMF)", __func__, directory[k]->Name()); + PrintLine(LOG_DEBUG, "[%s] Detected level : %s (UDMF)", __func__, read_directory[k].name); } continue; @@ -431,12 +238,12 @@ void Wad_file::DetectLevels(void) // check whether the next four lumps are level lumps for (size_t i = 1; i <= 4; i++) { - if (k + i >= NumLumps()) + if (k + i >= this->NumLumps()) { break; } - size_t part = WhatLevelPart(directory[k + i]->Name()); + size_t part = WhatLevelPart(read_directory[k + i].name); if (part == 0) { @@ -455,588 +262,122 @@ void Wad_file::DetectLevels(void) if (part_count == 4) { - levels.push_back(k); + read_levels.push_back(k); if (HAS_BIT(config.debug, DEBUG_WAD)) { - PrintLine(LOG_DEBUG, "[%s] Detected level : %s", __func__, directory[k]->Name()); + PrintLine(LOG_DEBUG, "[%s] Detected level : %s", __func__, read_directory[k].name); } } } - - // sort levels into alphabetical order - SortLevels(); -} - -void Wad_file::SortLevels(void) -{ - // predicate for sorting the levels[] vector - struct level_name_CMP_pred - { - Wad_file *wad; - - level_name_CMP_pred(Wad_file *_w) : wad(_w) - { - } - - inline bool operator()(const size_t A, const size_t B) const - { - const Lump_c *L1 = wad->directory[A]; - const Lump_c *L2 = wad->directory[B]; - - return (strcmp(L1->Name(), L2->Name()) < 0); - } - }; - - std::sort(levels.begin(), levels.end(), level_name_CMP_pred(this)); } -static bool IsDummyMarker(const char *name) +template +std::vector WadIO::ReadLump(size_t index) { - // matches P1_START, F3_END etc... - if (strlen(name) < 3) + std::vector lump; + if (index >= this->read_header.num_entries) { - return false; + return lump; } - - if (!strchr("PSF", toupper(name[0]))) + if (fseek(this->read_file, this->read_directory[index].pos, SEEK_SET)) { - return false; + throw std::runtime_error("ERROR: Failed to seek"); } - - if (!isdigit(name[1])) - { - return false; - } - - if (StringCaseCmp(name + 2, "_START") == 0 || StringCaseCmp(name + 2, "_END") == 0) - { - return true; - } - - return false; + size_t size = this->read_directory[index].size / sizeof(T); + lump.resize(size); + this->SafeRead(lump.data(), size * sizeof(T)); + return lump; } -void Wad_file::ProcessNamespaces(void) +template +std::vector WadIO::ReadMapLump(level_t &level, const char *name) { - char active = 0; - - for (size_t k = 0; k < NumLumps(); k++) - { - const char *name = directory[k]->Name(); - - // skip the sub-namespace markers - if (IsDummyMarker(name)) - { - continue; - } - - if (StringCaseCmp(name, "P_START") == 0 || StringCaseCmp(name, "PP_START") == 0) - { - if (active && active != 'P') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Missing %c_END marker.", __func__, active); - } - } - - active = 'P'; - continue; - } - else if (StringCaseCmp(name, "P_END") == 0 || StringCaseCmp(name, "PP_END") == 0) - { - if (active != 'P') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Stray P_END marker found.", __func__); - } - } - - active = 0; - continue; - } - - if (StringCaseCmp(name, "S_START") == 0 || StringCaseCmp(name, "SS_START") == 0) - { - if (active && active != 'S') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Missing %c_END marker.", __func__, active); - } - } - - active = 'S'; - continue; - } - else if (StringCaseCmp(name, "S_END") == 0 || StringCaseCmp(name, "SS_END") == 0) - { - if (active != 'S') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] stray S_END marker found.", __func__); - } - } - - active = 0; - continue; - } - - if (StringCaseCmp(name, "F_START") == 0 || StringCaseCmp(name, "FF_START") == 0) - { - if (active && active != 'F') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] missing %c_END marker.", __func__, active); - } - } - - active = 'F'; - continue; - } - else if (StringCaseCmp(name, "F_END") == 0 || StringCaseCmp(name, "FF_END") == 0) - { - if (active != 'F') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] stray F_END marker found.", __func__); - } - } - - active = 0; - continue; - } - - if (StringCaseCmp(name, "TX_START") == 0) - { - if (active && active != 'T') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] missing %c_END marker.", __func__, active); - } - } - - active = 'T'; - continue; - } - else if (StringCaseCmp(name, "TX_END") == 0) - { - if (active != 'T') - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] stray TX_END marker found.", __func__); - } - } - - active = 0; - continue; - } - - if (active) - { - if (directory[k]->Length() == 0) - { - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] skipping empty lump %s in %c_START", __func__, name, active); - } - continue; - } - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Namespace %c lump : %s", __func__, active, name); - } - - switch (active) - { - case 'P': - patches.push_back(k); - break; - case 'S': - sprites.push_back(k); - break; - case 'F': - flats.push_back(k); - break; - case 'T': - tx_tex.push_back(k); - break; - - default: - PrintLine(LOG_ERROR, "ERROR: ProcessNamespaces: active = 0x%02x", active); - } - } - } - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - if (active) - { - PrintLine(LOG_DEBUG, "[%s] Missing %c_END marker (at EOF)", __func__, active); - } - } + return ReadLump(this->LevelLookupLump(level.level_num, name)); } //------------------------------------------------------------------------ // WAD Writing Interface //------------------------------------------------------------------------ -void Wad_file::BeginWrite(void) +void WadIO::SafeWrite(const void *buffer, size_t size) { - if (mode == 'r') + if (fwrite(buffer, 1, size, write_file) != size) { - PrintLine(LOG_ERROR, "ERROR: Wad_file::BeginWrite() called on read-only file"); + fclose(this->write_file); + this->write_file = NULL; + throw std::runtime_error( + "ERROR: Failed to write. Check that this directory is writable and that you have enough free disk space."); } - - if (begun_write) - { - PrintLine(LOG_ERROR, "ERROR: Wad_file::BeginWrite() called again without EndWrite()"); - } - - // put the size into a quantum state - total_size = 0; - begun_write = true; } -void Wad_file::EndWrite(void) +void WadIO::CreateEmptyLump(const char *name) { - if (!begun_write) - { - PrintLine(LOG_ERROR, "ERROR: Wad_file::EndWrite() called without BeginWrite()"); - } - - begun_write = false; - - WriteDirectory(); - - // reset the insertion point - insert_point = NO_INDEX; + WadLump lump; + strncpy(lump.name, name, 8); + lump.pos = GetLittleEndian(static_cast(ftell(this->write_file))); + lump.size = 0; + this->write_directory.push_back(lump); } -void Wad_file::FixGroup(std::vector &group, size_t index, size_t num_added, size_t num_removed) +void WadIO::StartWritingLump(const char *name) { - bool did_remove = false; - - for (size_t k = 0; k < group.size(); k++) - { - if (group[k] < index) - { - continue; - } - - if (group[k] < index + num_removed) - { - group[k] = NO_INDEX; - did_remove = true; - continue; - } - - group[k] += num_added; - group[k] -= num_removed; - } - - if (did_remove) - { - std::vector::iterator ENDP; - ENDP = std::remove(group.begin(), group.end(), -1); - group.erase(ENDP, group.end()); - } + CreateEmptyLump(name); } -Lump_c *Wad_file::AddLump(const char *name, size_t max_size) +void WadIO::WriteLump(const char *name, const void *data, size_t size) { - SYS_ASSERT(begun_write); - - begun_max_size = max_size; - - size_t start = PositionForWrite(max_size); - - Lump_c *lump = MakeLump(this, name, start, 0); - - // check if the insert_point is still valid - if (insert_point >= NumLumps()) - { - insert_point = NO_INDEX; - } - - if (insert_point != NO_INDEX) - { - // fix various arrays containing lump indices - FixGroup(levels, insert_point, 1, 0); - FixGroup(patches, insert_point, 1, 0); - FixGroup(sprites, insert_point, 1, 0); - FixGroup(flats, insert_point, 1, 0); - FixGroup(tx_tex, insert_point, 1, 0); - - directory.insert(directory.begin() + static_cast(insert_point), lump); - - insert_point++; - } - else // add to end - { - directory.push_back(lump); - } - - return lump; + WadLump lump; + strncpy(lump.name, name, 8); + lump.pos = GetLittleEndian(static_cast(ftell(this->write_file))); + lump.size = IndexToInt(size); + this->write_directory.push_back(lump); + this->SafeWrite(data, size); } -void Wad_file::RecreateLump(Lump_c *lump, size_t max_size) +void WadIO::CopyLump(size_t index) { - SYS_ASSERT(begun_write); - - begun_max_size = max_size; - - size_t start = PositionForWrite(max_size); - - lump->l_start = start; - lump->l_length = 0; + auto lump = this->ReadLump(index); + WriteLump(this->LumpName(index), lump.data(), lump.size()); } -void Wad_file::InsertPoint(size_t index) +void WadIO::AddToLump(const void *data, size_t len) { - // this is validated on usage - insert_point = index; + size_t current = this->write_directory.size() - 1; + this->SafeWrite(data, len); + this->write_directory[current].size += len; } -size_t Wad_file::HighWaterMark(void) +WadIO &WadIO::operator<<(uint8_t value) { - size_t offset = sizeof(raw_wad_header_t); - - for (size_t k = 0; k < NumLumps(); k++) - { - Lump_c *lump = directory[k]; - - // ignore zero-length lumps (their offset could be anything) - if (lump->Length() <= 0) - { - continue; - } - - size_t l_end = lump->l_start + lump->l_length; - - l_end = ((l_end + 3) / 4) * 4; - - if (offset < l_end) - { - offset = l_end; - } - } - - return offset; + AddToLump(&value, sizeof(uint8_t)); + return *this; } -size_t Wad_file::FindFreeSpace(size_t length) +WadIO &WadIO::operator<<(uint16_t value) { - length = ((length + 3) / 4) * 4; - - // collect non-zero length lumps and sort by their offset - std::vector sorted_dir; - - for (size_t k = 0; k < NumLumps(); k++) - { - Lump_c *lump = directory[k]; - - if (lump->Length() > 0) - { - sorted_dir.push_back(lump); - } - } - - struct offset_CMP_pred - { - inline bool operator()(const Lump_c *A, const Lump_c *B) const - { - return A->l_start < B->l_start; - } - }; - - std::sort(sorted_dir.begin(), sorted_dir.end(), offset_CMP_pred()); - - size_t offset = sizeof(raw_wad_header_t); - - for (size_t k = 0; k < sorted_dir.size(); k++) - { - Lump_c *lump = sorted_dir[k]; - - size_t l_start = lump->l_start; - size_t l_end = lump->l_start + lump->l_length; - - l_end = ((l_end + 3) / 4) * 4; - - if (l_end <= offset) - { - continue; - } - - if (l_start >= offset + length) - { - continue; - } - - // the lump overlapped the current gap, so bump offset - - offset = l_end; - } - - return offset; + value = GetLittleEndian(value); + AddToLump(reinterpret_cast(&value), sizeof(uint16_t)); + return *this; } -size_t Wad_file::PositionForWrite(size_t max_size) +WadIO &WadIO::operator<<(uint32_t value) { - int64_t want_pos = static_cast(max_size == NO_INDEX ? HighWaterMark() : FindFreeSpace(max_size)); - - // determine if position is past end of file - // (difference should only be a few bytes) - // - // Note: doing this for every new lump may be a little expensive, - // but trying to optimise it away will just make the code - // needlessly complex and hard to follow. - - if (fseek(fp, 0, SEEK_END) < 0) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to new write position."); - } - - total_size = ftell(fp); - - if (total_size < 0) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to new write position."); - } - - if (want_pos > total_size) - { - SYS_ASSERT(want_pos < total_size + 8); - - WritePadding(static_cast(want_pos - total_size)); - } - else if (want_pos == total_size) - { - /* ready to write */ - } - else - { - if (fseek(fp, want_pos, SEEK_SET) < 0) - { - PrintLine(LOG_ERROR, "ERROR: Failure seeking to new write position."); - } - } - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] POSITION FOR WRITE: %zu (total_size %zu)", __func__, static_cast(want_pos), - static_cast(total_size)); - } - - return static_cast(want_pos); -} - -void Wad_file::FinishLump(size_t final_size) -{ - fflush(fp); - - // sanity check - if (final_size > begun_max_size) - { - PrintLine(LOG_ERROR, "ERROR: wrote too much in lump (%zu > %zu)", final_size, begun_max_size); - } - - int64_t pos = ftell(fp); - - if (pos & 3) - { - WritePadding(4 - (pos & 3)); - } - - fflush(fp); + value = GetLittleEndian(value); + AddToLump(reinterpret_cast(&value), sizeof(uint32_t)); + return *this; } -size_t Wad_file::WritePadding(size_t count) +WadIO &WadIO::operator<<(int16_t value) { - static byte zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - - SYS_ASSERT(1 <= count && count <= 8); - - fwrite(zeros, count, 1, fp); - - return count; + value = GetLittleEndian(value); + AddToLump(reinterpret_cast(&value), sizeof(int16_t)); + return *this; } -// -// IDEA : Truncate file to "total_size" after writing the directory. -// -// On Linux / MacOSX, this can be done as follows: -// - fflush(fp) -- ensure STDIO has empty buffers -// - ftruncate(fileno(fp), total_size); -// - freopen(fp) -// -// On Windows: -// - instead of ftruncate, use _chsize() or _chsize_s() -// [ investigate what the difference is.... ] -// - -void Wad_file::WriteDirectory(void) +WadIO &WadIO::operator<<(fixed_t value) { - dir_start = PositionForWrite(); - dir_count = NumLumps(); - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] dir_start:%zu dir_count:%zu", __func__, dir_start, dir_count); - } - - for (size_t k = 0; k < dir_count; k++) - { - Lump_c *lump = directory[k]; - SYS_ASSERT(lump); - - raw_wad_entry_t entry; - - lump->MakeEntry(&entry); - - if (fwrite(&entry, sizeof(entry), 1, fp) != 1) - { - PrintLine(LOG_ERROR, "ERROR: Failure writing WAD directory."); - } - } - - fflush(fp); - - total_size = ftell(fp); - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] total_size: %zu", __func__, static_cast(total_size)); - } - - if (total_size < 0) - { - PrintLine(LOG_ERROR, "ERROR: Failure determining WAD size."); - } - - // update header at start of file - - rewind(fp); - - raw_wad_header_t header; - - memcpy(header.ident, (kind == 'I') ? "IWAD" : "PWAD", 4); - - header.dir_start = GetLittleEndian(IndexToInt(dir_start)); - header.num_entries = GetLittleEndian(IndexToInt(dir_count)); - - if (fwrite(&header, sizeof(header), 1, fp) != 1) - { - PrintLine(LOG_ERROR, "ERROR: Failure writing WAD header."); - } - - fflush(fp); + value = GetLittleEndian(value); + AddToLump(reinterpret_cast(&value), sizeof(fixed_t)); + return *this; } diff --git a/src/wad.hpp b/src/wad.hpp new file mode 100644 index 0000000..67b19c4 --- /dev/null +++ b/src/wad.hpp @@ -0,0 +1,101 @@ +//------------------------------------------------------------------------------ +// +// ELFBSP -- WAD Reading / Writing +// +//------------------------------------------------------------------------------ +// +// Copyright 2025-2026 Guilherme Miranda +// Copyright 2001-2018 Andrew Apted +// Copyright 2002-2006 Marisa Heit +// +// 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. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include "core.hpp" + +//------------------------------------------------------------------------ +// WAD STRUCTURES +//------------------------------------------------------------------------ + +// wad header +using WadHeader = struct WadHeader +{ + char ident[4]; + uint32_t num_entries; + uint32_t dir_start; +} PACKEDATTR; + +// directory entry +using WadLump = struct WadLump +{ + uint32_t pos; + uint32_t size; + char name[8]; +} PACKEDATTR; + +static_size(WadHeader, 12); +static_size(WadLump, 16); + +// +// File handling +// + +struct WadIO +{ + // Read data + FILE *read_file; + WadHeader read_header; + std::vector read_directory; + std::vector read_levels; // these are lump indices (into 'directory' vector) + + // Write data + FILE *write_file; + WadHeader write_header; + std::vector write_directory; + + // Init/die + WadIO(const char *filename); + ~WadIO(void); + + // Read operations + void SafeRead(void *buffer, size_t size); + size_t NumLumps(void) const; + size_t LevelCount(void) const; + const char *LumpName(size_t lump_index) const; + size_t LevelHeader(size_t lev_num); + size_t LevelLastLump(size_t lev_num); + size_t LevelLookupLump(size_t lev_num, const char *name); + map_format_t LevelFormat(size_t lev_num); + void DetectLevels(void); + template + std::vector ReadLump(size_t index); + template + std::vector ReadMapLump(level_t &level, const char *name); + + // Write operations + void SafeWrite(const void *buffer, size_t size); + void CreateEmptyLump(const char *name); + void WriteLump(const char *name, const void *data, size_t size); + void CopyLump(size_t lump); + + // Partial writing + void StartWritingLump(const char *name); + void AddToLump(const void *data, size_t len); + void AddToLumpZ(const void *data, size_t len); + WadIO &operator<<(uint8_t value); + WadIO &operator<<(uint16_t value); + WadIO &operator<<(uint32_t value); + WadIO &operator<<(int16_t value); + WadIO &operator<<(fixed_t value); +}; From 76d0282ee6db778e026340cf6b396d78274bd612 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Tue, 19 May 2026 16:08:45 -0300 Subject: [PATCH 02/10] YADC, yet another dummy commit --- src/bsp.cpp | 438 +++++++++++++------------------------------------- src/bsp.hpp | 45 +++--- src/core.hpp | 19 +-- src/info.cpp | 10 +- src/level.cpp | 113 +++++++------ src/local.hpp | 7 +- src/main.cpp | 87 ---------- src/misc.cpp | 3 - src/node.cpp | 17 -- src/wad.cpp | 216 +++++++++++++------------ src/wad.hpp | 18 ++- 11 files changed, 335 insertions(+), 638 deletions(-) diff --git a/src/bsp.cpp b/src/bsp.cpp index f04aaef..b70a3df 100644 --- a/src/bsp.cpp +++ b/src/bsp.cpp @@ -21,11 +21,12 @@ // //------------------------------------------------------------------------------ +#include "bsp.hpp" #include "core.hpp" #include "local.hpp" -#include "bsp.hpp" #include +#include #include // Upper-most bit is used for distinguishing sub-sectors, i.e tree leaves @@ -101,66 +102,37 @@ static inline short_angle_t VanillaSegAngle(const seg_t *seg) return result; } -static void PutVertices_Doom(WadIO &io, level_t &level) +void PutVertices_Integral(WadIO &io, level_t &level, bool only_old) { - size_t count = 0; + const size_t vert_count = only_old ? level.num_old_vert : level.vertices.size(); io.StartWritingLump("VERTEXES"); - for (size_t i = 0; i < level.vertices.size(); i++) + for (size_t i = 0; i < vert_count; i++) { - raw_vertex_t raw; - const vertex_t *vert = level.vertices[i]; - // see: RoundOffVertices() - if (vert->is_new) - { - continue; - } - + raw_vertex_short_t raw; raw.x = GetLittleEndian(FloatToShort(floor(vert->x))); raw.y = GetLittleEndian(FloatToShort(floor(vert->y))); - count++; - io.AddToLump(&raw, sizeof(raw_vertex_t)); - } - - // sanity count - // double check that bsp-vertices are written out in the correct formats - if (count != level.num_old_vert) - { - PrintLine(LOG_ERROR, "ERROR: PutVertices miscounted (%zu != %zu)", count, level.num_old_vert); + io.AddToLump(&raw, sizeof(raw)); } } -static void PutVertices_Doom64(WadIO &io, level_t &level) +void PutVertices_Fractional(WadIO &io, level_t &level, bool only_old) { - size_t count = 0; + const size_t vert_count = only_old ? level.num_old_vert : level.vertices.size(); io.StartWritingLump("VERTEXES"); - for (size_t i = 0; i < level.vertices.size(); i++) + for (size_t i = 0; i < vert_count; i++) { - raw_vertex_doom64_t raw; - const vertex_t *vert = level.vertices[i]; - // see: RoundOffVertices() - if (vert->is_new) - { - continue; - } - + raw_vertex_fixed_t raw; raw.x = GetLittleEndian(FloatToFixed(vert->x)); raw.y = GetLittleEndian(FloatToFixed(vert->y)); - io.AddToLump(&raw, sizeof(raw_vertex_doom64_t)); - } - - // sanity count - // double check that bsp-vertices are written out in the correct formats - if (count != level.num_old_vert) - { - PrintLine(LOG_ERROR, "ERROR: PutVertices miscounted (%zu != %zu)", count, level.num_old_vert); + io.AddToLump(&raw, sizeof(raw)); } } @@ -168,13 +140,13 @@ static void PutVertices_Doom64(WadIO &io, level_t &level) // Vanilla format // -static void PutSegs_Vanilla(WadIO &io, level_t &level) +void PutSegs_Vanilla(WadIO &io, level_t &level) { io.StartWritingLump("SEGS"); for (size_t i = 0; i < level.segs.size(); i++) { - raw_seg_vanilla_t raw; + raw_seg_doombsp_t raw; const seg_t *seg = level.segs[i]; @@ -192,24 +164,24 @@ static void PutSegs_Vanilla(WadIO &io, level_t &level) seg->side ? "L" : "R", GetLittleEndian(raw.angle), seg->start->x, seg->start->y, seg->end->x, seg->end->y); } - io.AddToLump(&raw, sizeof(raw_seg_vanilla_t)); + io.AddToLump(&raw, sizeof(raw)); } } -static void PutSubsecs_Vanilla(WadIO &io, level_t &level) +void PutSubsecs_Vanilla(WadIO &io, level_t &level) { io.StartWritingLump("SSECTORS"); for (size_t i = 0; i < level.subsecs.size(); i++) { - raw_subsec_vanilla_t raw; + raw_subsec_doombsp_t raw; const subsec_t *sub = level.subsecs[i]; raw.first = GetLittleEndian(IndexToShort(sub->seg_list->index)); raw.num = GetLittleEndian(IndexToShort(sub->seg_count)); - io.AddToLump(&raw, sizeof(raw_subsec_vanilla_t)); + io.AddToLump(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -219,21 +191,21 @@ static void PutSubsecs_Vanilla(WadIO &io, level_t &level) } } -static void PutOneNode_Vanilla(node_t *node, size_t &node_cur_index) +static void PutOneNode_Vanilla(WadIO &io, node_t *node, size_t &node_cur_index) { if (node->r.node) { - PutOneNode_Vanilla(lump, node->r.node, node_cur_index); + PutOneNode_Vanilla(io, node->r.node, node_cur_index); } if (node->l.node) { - PutOneNode_Vanilla(lump, node->l.node, node_cur_index); + PutOneNode_Vanilla(io, node->l.node, node_cur_index); } node->index = node_cur_index++; - raw_node_vanilla_t raw; + raw_node_doombsp_t raw; raw.x = GetLittleEndian(FloatToShort(node->x)); raw.y = GetLittleEndian(FloatToShort(node->y)); @@ -276,6 +248,7 @@ static void PutOneNode_Vanilla(node_t *node, size_t &node_cur_index) PrintLine(LOG_ERROR, "ERROR: Bad left child in node %zu", node->index); } + io.AddToLump(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -284,26 +257,25 @@ static void PutOneNode_Vanilla(node_t *node, size_t &node_cur_index) } } -static void PutNodes_Vanilla(WadIO &io, level_t &level, node_t *root_node) +void PutNodes_Vanilla(WadIO &io, level_t &level) { - // this can be bigger than the actual size, but never smaller - size_t max_size = (level.nodes.size() + 1) * sizeof(raw_node_vanilla_t); size_t node_cur_index = 0; + io.StartWritingLump("NODES"); - if (root_node != nullptr) + if (level.root_node != nullptr) { - PutOneNode_Vanilla(lump, root_node, node_cur_index); + PutOneNode_Vanilla(io, level.root_node, node_cur_index); } - if (node_cur_index != level.nodes.size()) { PrintLine(LOG_ERROR, "ERROR: PutNodes miscounted (%zu != %zu)", node_cur_index, level.nodes.size()); } } -static void PutLeafs_Vanilla(WadIO &io, level_t &level) +void PutLeafs_Vanilla(WadIO &io, level_t &level) { + io.StartWritingLump("LEAFS"); uint16_t actual_seg_index = 0; for (size_t i = 0; i < level.subsecs.size(); i++) @@ -312,28 +284,20 @@ static void PutLeafs_Vanilla(WadIO &io, level_t &level) seg_t *seg = subsec->seg_list; size_t seg_count = subsec->seg_count; - - if (HAS_BIT(config.debug, DEBUG_BSP)) { PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %zu", __func__, i, seg_count); } - if (seg_count < 3) - { - PrintLine(LOG_ERROR, "[%s] Subsector[%zu] has fewer than 3 leaf references", __func__, i); - } - for (size_t j = 0; j < seg_count; j++) { // Do not remove minisegs from tree before // we are able to write all this - raw_leaf_vanilla_t raw; + raw_leaf_doombsp_t raw; vertex_t *vert = seg->start; raw.vertex = GetLittleEndian(IndexToShort(vert->index)); if (seg->linedef) - // if (seg->linedef && !vert->is_new) { raw.seg = GetLittleEndian(actual_seg_index); actual_seg_index++; @@ -343,7 +307,8 @@ static void PutLeafs_Vanilla(WadIO &io, level_t &level) raw.seg = NO_INDEX_INT16; } - + io.AddToLump(&raw, sizeof(raw)); + seg = seg->next; if (HAS_BIT(config.debug, DEBUG_BSP)) @@ -358,10 +323,9 @@ static void PutLeafs_Vanilla(WadIO &io, level_t &level) // DeePBSPV4 format // -static void PutSegs_DeePBSPV4(WadIO &io, level_t &level) +void PutSegs_DeePBSPV4(WadIO &io, level_t &level) { - // this size is worst-case scenario - size_t size = level.segs.size() * sizeof(raw_seg_deepbspv4_t); + io.StartWritingLump("SEGS"); for (size_t i = 0; i < level.segs.size(); i++) { @@ -376,7 +340,7 @@ static void PutSegs_DeePBSPV4(WadIO &io, level_t &level) raw.flip = GetLittleEndian(seg->side); raw.dist = GetLittleEndian(VanillaSegDist(seg)); - + io.AddToLump(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -385,13 +349,11 @@ static void PutSegs_DeePBSPV4(WadIO &io, level_t &level) seg->side ? "L" : "R", GetLittleEndian(raw.angle), seg->start->x, seg->start->y, seg->end->x, seg->end->y); } } - } -static void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level) +void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level) { - size_t size = level.subsecs.size() * sizeof(raw_subsec_deepbspv4_t); - + io.StartWritingLump("SSECTORS"); for (size_t i = 0; i < level.subsecs.size(); i++) { @@ -402,7 +364,7 @@ static void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level) raw.first = GetLittleEndian(IndexToInt(sub->seg_list->index)); raw.num = GetLittleEndian(IndexToShort(sub->seg_count)); - + io.AddToLump(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -410,19 +372,18 @@ static void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level) GetLittleEndian(raw.num)); } } - } -static void PutOneNode_DeePBSPV4(node_t *node, size_t &node_cur_index) +static void PutOneNode_DeePBSPV4(WadIO &io, node_t *node, size_t &node_cur_index) { if (node->r.node) { - PutOneNode_DeePBSPV4(lump, node->r.node, node_cur_index); + PutOneNode_DeePBSPV4(io, node->r.node, node_cur_index); } if (node->l.node) { - PutOneNode_DeePBSPV4(lump, node->l.node, node_cur_index); + PutOneNode_DeePBSPV4(io, node->l.node, node_cur_index); } node->index = node_cur_index++; @@ -470,6 +431,7 @@ static void PutOneNode_DeePBSPV4(node_t *node, size_t &node_cur_index) PrintLine(LOG_ERROR, "ERROR: Bad left child in node %zu", node->index); } + io.AddToLumpZ(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -478,28 +440,25 @@ static void PutOneNode_DeePBSPV4(node_t *node, size_t &node_cur_index) } } -static void PutNodes_DeePBSPV4(WadIO &io, level_t &level, node_t *root_node) +void PutNodes_DeePBSPV4(WadIO &io, level_t &level) { - // this can be bigger than the actual size, but never smaller - // 8 bytes for BSP_MAGIC_DEEPBSPV4 header - // size_t max_size = 8 + (level.nodes.size() + 1) * sizeof(raw_node_deepbspv4_t); + io.StartWritingLump("NODES"); size_t node_cur_index = 0; - - if (root_node != nullptr) + if (level.root_node != nullptr) { - PutOneNode_DeePBSPV4(lump, root_node, node_cur_index); + PutOneNode_DeePBSPV4(io, level.root_node, node_cur_index); } - if (node_cur_index != level.nodes.size()) { PrintLine(LOG_ERROR, "ERROR: PutNodes miscounted (%zu != %zu)", node_cur_index, level.nodes.size()); } } -static void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) +void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) { + io.StartWritingLump("LEAFS"); uint32_t actual_seg_index = 0; for (size_t i = 0; i < level.subsecs.size(); i++) @@ -508,18 +467,11 @@ static void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) seg_t *seg = subsec->seg_list; size_t seg_count = subsec->seg_count; - - if (HAS_BIT(config.debug, DEBUG_BSP)) { PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %zu", __func__, i, seg_count); } - if (seg_count < 3) - { - PrintLine(LOG_ERROR, "[%s] Subsector[%zu] has fewer than 3 leaf references", __func__, i); - } - for (size_t j = 0; j < seg_count; j++) { // Do not remove minisegs from tree before @@ -527,9 +479,8 @@ static void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) raw_leaf_deepbspv4_t raw; vertex_t *vert = seg->start; - raw.vertex = GetLittleEndian(IndexToShort(vert->index)); + raw.vertex = GetLittleEndian(IndexToInt(vert->index)); if (seg->linedef) - // if (seg->linedef && !vert->is_new) { raw.seg = GetLittleEndian(actual_seg_index); actual_seg_index++; @@ -539,7 +490,8 @@ static void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) raw.seg = NO_INDEX_INT32; } - + io.AddToLump(&raw, sizeof(raw)); + seg = seg->next; if (HAS_BIT(config.debug, DEBUG_BSP)) @@ -554,21 +506,13 @@ static void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) // ZDoom format -- XNOD // -static inline uint32_t VertexIndex_XNOD(level_t &level, const vertex_t *v) -{ - if (v->is_new) - { - return IndexToInt(level.num_old_vert + v->index); - } - - return IndexToInt(v->index); -} - static void PutVertices_Xnod(WadIO &io, level_t &level) { - size_t orgverts = GetLittleEndian(level.num_old_vert); - size_t newverts = GetLittleEndian(level.num_new_vert); + uint32_t orgverts = GetLittleEndian(IndexToInt(level.num_old_vert)); + uint32_t newverts = GetLittleEndian(IndexToInt(level.num_new_vert)); + io.AddToLumpZ(&orgverts, sizeof(orgverts)); + io.AddToLumpZ(&newverts, sizeof(newverts)); size_t count = 0; for (size_t i = 0; i < level.vertices.size(); i++) @@ -585,7 +529,7 @@ static void PutVertices_Xnod(WadIO &io, level_t &level) raw.x = GetLittleEndian(FloatToFixed(vert->x)); raw.y = GetLittleEndian(FloatToFixed(vert->y)); - + io.AddToLumpZ(&raw, sizeof(raw)); count++; } @@ -598,15 +542,17 @@ static void PutVertices_Xnod(WadIO &io, level_t &level) static void PutSubsecs_Xnod(WadIO &io, level_t &level) { - size_t raw_num = GetLittleEndian(level.subsecs.size()); + uint32_t raw_num = GetLittleEndian(IndexToInt(level.subsecs.size())); + io.AddToLumpZ(&raw_num, sizeof(raw_num)); size_t cur_seg_index = 0; for (size_t i = 0; i < level.subsecs.size(); i++) { const subsec_t *sub = level.subsecs[i]; - raw_num = GetLittleEndian(sub->seg_count); - + raw_subsec_xnod_t raw; + raw.segnum = GetLittleEndian(IndexToInt(sub->seg_count)); + io.AddToLumpZ(&raw, sizeof(raw)); // sanity check the seg index values size_t count = 0; @@ -634,7 +580,8 @@ static void PutSubsecs_Xnod(WadIO &io, level_t &level) static void PutSegs_Xnod(WadIO &io, level_t &level) { - size_t raw_num = GetLittleEndian(level.segs.size()); + uint32_t raw_num = GetLittleEndian(IndexToInt(level.segs.size())); + io.AddToLumpZ(&raw_num, sizeof(raw_num)); for (size_t i = 0; i < level.segs.size(); i++) { @@ -647,30 +594,31 @@ static void PutSegs_Xnod(WadIO &io, level_t &level) raw_seg_xnod_t raw = {}; - raw.start = GetLittleEndian(VertexIndex_XNOD(level, seg->start)); - raw.end = GetLittleEndian(VertexIndex_XNOD(level, seg->end)); + raw.start = GetLittleEndian(IndexToInt(seg->start->index)); + raw.end = GetLittleEndian(IndexToInt(seg->end->index)); raw.linedef = GetLittleEndian(IndexToShort(seg->linedef->index)); raw.side = seg->side; + io.AddToLumpZ(&raw, sizeof(raw)); } } -static void PutOneNode_Xnod(node_t *node, size_t &node_cur_index) +static void PutOneNode_Xnod(WadIO &io, node_t *node, size_t &node_cur_index) { - raw_node_xnod_t raw; - if (node->r.node) { - PutOneNode_Xnod(lump, node->r.node, node_cur_index); + PutOneNode_Xnod(io, node->r.node, node_cur_index); } if (node->l.node) { - PutOneNode_Xnod(lump, node->l.node, node_cur_index); + PutOneNode_Xnod(io, node->l.node, node_cur_index); } node->index = node_cur_index++; + raw_node_xnod_t raw; + raw.x = GetLittleEndian(FloatToShort(node->x)); raw.y = GetLittleEndian(FloatToShort(node->y)); raw.dx = GetLittleEndian(FloatToShort(node->dx)); @@ -712,6 +660,7 @@ static void PutOneNode_Xnod(node_t *node, size_t &node_cur_index) PrintLine(LOG_ERROR, "ERROR: Bad left child in ZDoom node %zu", node->index); } + io.AddToLumpZ(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -720,14 +669,15 @@ static void PutOneNode_Xnod(node_t *node, size_t &node_cur_index) } } -static void PutNodes_Xnod(WadIO &io, level_t &level, node_t *root) +static void PutNodes_Xnod(WadIO &io, level_t &level) { size_t node_cur_index = 0; - size_t raw_num = GetLittleEndian(level.nodes.size()); + uint32_t raw_num = GetLittleEndian(IndexToInt(level.nodes.size())); + io.AddToLumpZ(&raw_num, sizeof(raw_num)); - if (root) + if (level.root_node) { - PutOneNode_Xnod(lump, root, node_cur_index); + PutOneNode_Xnod(io, level.root_node, node_cur_index); } if (node_cur_index != level.nodes.size()) @@ -736,29 +686,14 @@ static void PutNodes_Xnod(WadIO &io, level_t &level, node_t *root) } } -static size_t CalcXnodNodesSize(WadIO &io, level_t &level) -{ - // compute size of the ZDoom format nodes. - // it does not need to be exact, but it *does* need to be bigger - // (or equal) to the actual size of the lump. - - size_t size = 32; // header + a bit extra - - size += 8 + level.vertices.size() * sizeof(raw_vertex_xnod_t); - size += 4 + level.subsecs.size() * sizeof(raw_subsec_xnod_t); - size += 4 + level.segs.size() * sizeof(raw_seg_xgl2_t); - size += 4 + level.nodes.size() * sizeof(raw_node_xgl3_t); - - return size; -} - // // ZDoom format -- XGLN, XGL2, XGL3 // static void PutSegs_Xgln(WadIO &io, level_t &level) { - size_t raw_num = GetLittleEndian(level.segs.size()); + uint32_t raw_num = GetLittleEndian(IndexToInt(level.segs.size())); + io.AddToLumpZ(&raw_num, sizeof(raw_num)); for (size_t i = 0; i < level.segs.size(); i++) { @@ -771,12 +706,12 @@ static void PutSegs_Xgln(WadIO &io, level_t &level) raw_seg_xgln_t raw = {}; - raw.vertex = GetLittleEndian(VertexIndex_XNOD(level, seg->start)); + raw.vertex = GetLittleEndian(IndexToInt(seg->start->index)); raw.partner = GetLittleEndian(IndexToInt(seg->partner ? seg->partner->index : NO_INDEX)); raw.linedef = GetLittleEndian(IndexToShort(seg->linedef ? seg->linedef->index : NO_INDEX)); raw.side = seg->side; - + io.AddToLumpZ(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -788,7 +723,8 @@ static void PutSegs_Xgln(WadIO &io, level_t &level) static void PutSegs_Xgl2(WadIO &io, level_t &level) { - size_t raw_num = GetLittleEndian(level.segs.size()); + uint32_t raw_num = GetLittleEndian(IndexToInt(level.segs.size())); + io.AddToLumpZ(&raw_num, sizeof(raw_num)); for (size_t i = 0; i < level.segs.size(); i++) { @@ -801,12 +737,12 @@ static void PutSegs_Xgl2(WadIO &io, level_t &level) raw_seg_xgl2_t raw = {}; - raw.vertex = GetLittleEndian(VertexIndex_XNOD(level, seg->start)); + raw.vertex = GetLittleEndian(IndexToInt(seg->start->index)); raw.partner = GetLittleEndian(IndexToInt(seg->partner ? seg->partner->index : NO_INDEX)); raw.linedef = GetLittleEndian(IndexToInt(seg->linedef ? seg->linedef->index : NO_INDEX)); raw.side = seg->side; - + io.AddToLumpZ(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -816,22 +752,22 @@ static void PutSegs_Xgl2(WadIO &io, level_t &level) } } -static void PutOneNode_Xgl3(node_t *node, size_t &node_cur_index) +static void PutOneNode_Xgl3(WadIO &io, node_t *node, size_t &node_cur_index) { - raw_node_xgl3_t raw; - if (node->r.node) { - PutOneNode_Xgl3(lump, node->r.node, node_cur_index); + PutOneNode_Xgl3(io, node->r.node, node_cur_index); } if (node->l.node) { - PutOneNode_Xgl3(lump, node->l.node, node_cur_index); + PutOneNode_Xgl3(io, node->l.node, node_cur_index); } node->index = node_cur_index++; + raw_node_xgl3_t raw; + raw.x = GetLittleEndian(FloatToFixed(node->x)); raw.y = GetLittleEndian(FloatToFixed(node->y)); raw.dx = GetLittleEndian(FloatToFixed(node->dx)); @@ -873,6 +809,7 @@ static void PutOneNode_Xgl3(node_t *node, size_t &node_cur_index) PrintLine(LOG_ERROR, "ERROR: Bad left child in ZDoom node %zu", node->index); } + io.AddToLumpZ(&raw, sizeof(raw)); if (HAS_BIT(config.debug, DEBUG_BSP)) { @@ -881,14 +818,15 @@ static void PutOneNode_Xgl3(node_t *node, size_t &node_cur_index) } } -static void PutNodes_Xgl3(WadIO &io, level_t &level, node_t *root) +static void PutNodes_Xgl3(WadIO &io, level_t &level) { size_t node_cur_index = 0; - size_t raw_num = GetLittleEndian(level.nodes.size()); + uint32_t raw_num = GetLittleEndian(IndexToInt(level.nodes.size())); + io.AddToLumpZ(&raw_num, sizeof(raw_num)); - if (root) + if (level.root_node) { - PutOneNode_Xgl3(lump, root, node_cur_index); + PutOneNode_Xgl3(io, level.root_node, node_cur_index); } if (node_cur_index != level.nodes.size()) @@ -898,184 +836,34 @@ static void PutNodes_Xgl3(WadIO &io, level_t &level, node_t *root) } // -// Lump writing procedures +// The family of ZDBSP BSP tree formats use a single lump no matter what // - -void SaveDoom_DoomBSP(WadIO &io, level_t &level, node_t *root_node) -{ - auto mark = Benchmarker(__func__); - // remove all the minisegs from subsectors - NormaliseBspTree(level); - // reduce vertex precision for classic DOOM nodes. - // some segs can become "degenerate" after this, and these - // are removed from subsectors. - RoundOffBspTree(level); - SortSegs(level); - PutVertices_Doom(level); - PutSegs_Vanilla(level); - PutSubsecs_Vanilla(level); - PutNodes_Vanilla(level, root_node); -} - -void SaveDoom_DeePBSPV4(WadIO &io, level_t &level, node_t *root_node) -{ - auto mark = Benchmarker(__func__); - // remove all the minisegs from subsectors - NormaliseBspTree(level); - // reduce vertex precision for classic DOOM nodes. - // some segs can become "degenerate" after this, and these - // are removed from subsectors. - RoundOffBspTree(level); - SortSegs(level); - PutVertices_Doom(level); - PutSegs_DeePBSPV4(level); - PutSubsecs_DeePBSPV4(level); - PutNodes_DeePBSPV4(level, root_node); -} - -void SaveDoom_XNOD(WadIO &io, level_t &level, node_t *root_node) -{ - auto mark = Benchmarker(__func__); - CreateLevelLump(level, "SEGS")->Finish(); - CreateLevelLump(level, "SSECTORS")->Finish(); - // remove all the minisegs from subsectors - NormaliseBspTree(level); - SortSegs(level); - - - if (level.bsp_compress - - PutVertices_Xnod(level, lump); - PutSubsecs_Xnod(level, lump); - PutSegs_Xnod(level, lump); - PutNodes_Xnod(level, lump, root_node); - - if (level.bsp_compress - - lump = nullptr; -} - -void SaveDoom_XGLN(WadIO &io, level_t &level, node_t *root_node) -{ - auto mark = Benchmarker(__func__); - // leave SEGS empty - CreateLevelLump(level, "SEGS")->Finish(); - - SortSegs(level); - - - if (level.bsp_compress - - PutVertices_Xnod(level, lump); - PutSubsecs_Xnod(level, lump); - PutSegs_Xgln(level, lump); - PutNodes_Xnod(level, lump, root_node); - - if (level.bsp_compress - - lump = nullptr; - - // leave NODES empty - CreateLevelLump(level, "NODES")->Finish(); -} - -void SaveDoom_XGL2(WadIO &io, level_t &level, node_t *root_node) +void PutTree_ZDBSP(WadIO &io, level_t &level, std::string lumpname, bsp_format_t bsp_format, bool compress) { auto mark = Benchmarker(__func__); - // leave SEGS empty - CreateLevelLump(level, "SEGS")->Finish(); - - SortSegs(level); - - - if (level.bsp_compress - - PutVertices_Xnod(level, lump); - PutSubsecs_Xnod(level, lump); - PutSegs_Xgl2(level, lump); - PutNodes_Xnod(level, lump, root_node); - - if (level.bsp_compress - lump = nullptr; - - // leave NODES empty - CreateLevelLump(level, "NODES")->Finish(); -} - -void SaveDoom_XGL3(WadIO &io, level_t &level, node_t *root_node) -{ - auto mark = Benchmarker(__func__); - // leave SEGS empty - CreateLevelLump(level, "SEGS")->Finish(); + if (bsp_format == BSP_XNOD) NormaliseBspTree(level); SortSegs(level); + io.StartWritingLump(lumpname.c_str()); - if (level.bsp_compress - - PutVertices_Xnod(level, lump); - PutSubsecs_Xnod(level, lump); - PutSegs_Xgl2(level, lump); - PutNodes_Xgl3(level, lump, root_node); - - if (level.bsp_compress - - lump = nullptr; + if (bsp_format == BSP_XNOD) io.AddToLump(compress ? "ZNOD" : "XNOD", 4); + if (bsp_format == BSP_XGLN) io.AddToLump(compress ? "ZGLN" : "XGLN", 4); + if (bsp_format == BSP_XGL2) io.AddToLump(compress ? "ZGL2" : "XGL2", 4); + if (bsp_format == BSP_XGL3) io.AddToLump(compress ? "ZGL3" : "XGL3", 4); - // leave NODES empty - CreateLevelLump(level, "NODES")->Finish(); -} + if (compress) io.Begin_Zlib(); -// -// Doom64 has some differences from Doom-format or Hexen-format -// This could also be shared with PSX Doom and PSX Final Doom, but we don't support those -// - -void SaveDoom64_DoomBSP(WadIO &io, level_t &level, node_t *root_node) -{ - // Needed for LEAFS - RoundOffVertices(level); - PutVertices_Doom64(level); - // We need minisegs just for leafs - PutLeafs_Vanilla(level); - // remove all the minisegs from subsectors - NormaliseBspTree(level); - SortSegs(level); - PutSegs_Vanilla(level); - PutSubsecs_Vanilla(level); - PutNodes_Vanilla(level, root_node); -} - -void SaveDoom64_DeePBSPV4(WadIO &io, level_t &level, node_t *root_node) -{ - // Needed for LEAFS - RoundOffVertices(level); - PutVertices_Doom64(level); - // We need minisegs just for leafs - PutLeafs_DeePBSPV4(level); - // remove all the minisegs from subsectors - NormaliseBspTree(level); - SortSegs(level); - PutSegs_DeePBSPV4(level); - PutSubsecs_DeePBSPV4(level); - PutNodes_DeePBSPV4(level, root_node); -} - -// -// Unlike the the Doom and Hexen map formats, UDMF has a tight requirement for fractional coordinates. -// Always use the latest high-precision BSP format we support. -// -void SaveTextmap_ZNODES(WadIO &io, level_t &level, node_t *root_node) -{ - auto mark = Benchmarker(__func__); - SortSegs(level); + PutVertices_Xnod(io, level); + PutSubsecs_Xnod(io, level); + if (bsp_format == BSP_XNOD) PutSegs_Xnod(io, level); + if (bsp_format == BSP_XGLN) PutSegs_Xgln(io, level); + if (bsp_format == BSP_XGL2 || bsp_format == BSP_XGL3) PutSegs_Xgl2(io, level); - PutVertices_Xnod(level, lump); - PutSubsecs_Xnod(level, lump); - PutSegs_Xgl2(level, lump); - PutNodes_Xgl3(level, lump, root_node); + if (bsp_format == BSP_XNOD || bsp_format == BSP_XGLN || bsp_format == BSP_XGL2) PutNodes_Xnod(io, level); + if (bsp_format == BSP_XGL3) PutNodes_Xgl3(io, level); - lump = nullptr; + if (compress) io.Finish_Zlib(); } diff --git a/src/bsp.hpp b/src/bsp.hpp index f23c9df..636bec0 100644 --- a/src/bsp.hpp +++ b/src/bsp.hpp @@ -27,9 +27,9 @@ #include "local.hpp" // -// Vanilla BSP +// DoomBSP // -using raw_node_vanilla_t = struct raw_node_vanilla_s +using raw_node_doombsp_t = struct raw_node_doombsp_s { int16_t x, y; // starting point int16_t dx, dy; // offset to ending point @@ -37,13 +37,13 @@ using raw_node_vanilla_t = struct raw_node_vanilla_s uint16_t right, left; // children: Node or SSector (if high bit is set) } PACKEDATTR; -using raw_subsec_vanilla_t = struct raw_subsec_vanilla_s +using raw_subsec_doombsp_t = struct raw_subsec_doombsp_s { uint16_t num; // number of Segs in this Sub-Sector uint16_t first; // first Seg } PACKEDATTR; -using raw_seg_vanilla_t = struct raw_seg_vanilla_s +using raw_seg_doombsp_t = struct raw_seg_doombsp_s { uint16_t start; // from this vertex... uint16_t end; // ... to this vertex @@ -53,14 +53,14 @@ using raw_seg_vanilla_t = struct raw_seg_vanilla_s int16_t dist; // distance from starting point } PACKEDATTR; -using raw_leaf_vanilla_t = struct raw_leaf_vanilla_s +using raw_leaf_doombsp_t = struct raw_leaf_doombsp_s { uint16_t vertex; uint16_t seg; } PACKEDATTR; // -// DeepSea BSP +// DeepSea DeePBSP // * compared to vanilla, some types were raise to 32bit // using raw_node_deepbspv4_t = struct raw_node_deepbspv4_s @@ -100,8 +100,8 @@ using raw_leaf_deepbspv4_t = struct raw_leaf_deepbspv4_s // using raw_vertex_xnod_t = struct raw_vertex_xnod_s { - int32_t x; - int32_t y; + fixed_t x; + fixed_t y; } PACKEDATTR; using raw_node_xnod_t = struct raw_node_xnod_s @@ -153,9 +153,9 @@ using raw_node_xgl3_t = struct raw_node_xgl3_s uint32_t right, left; // children: Node or SSector (if high bit is set) } PACKEDATTR; -static_size(raw_node_vanilla_t, 28); -static_size(raw_subsec_vanilla_t, 4); -static_size(raw_seg_vanilla_t, 12); +static_size(raw_node_doombsp_t, 28); +static_size(raw_subsec_doombsp_t, 4); +static_size(raw_seg_doombsp_t, 12); static_size(raw_node_deepbspv4_t, 32); static_size(raw_subsec_deepbspv4_t, 6); static_size(raw_seg_deepbspv4_t, 16); @@ -197,14 +197,19 @@ void PutReject(level_t &level); // the BSP tree lumps differ notably on each map format -- Doom/Hexen/Doom64 // have NODES, SSECTORS & SEGS, but UDMF is generally only ZNODES, and there's // some format overlap between them -void SaveDoom_DoomBSP(level_t &level, node_t *root_node); -void SaveDoom_DeePBSPV4(level_t &level, node_t *root_node); -void SaveDoom_XNOD(level_t &level, node_t *root_node); -void SaveDoom_XGLN(level_t &level, node_t *root_node); -void SaveDoom_XGL2(level_t &level, node_t *root_node); -void SaveDoom_XGL3(level_t &level, node_t *root_node); +void SortSegs(level_t &level); -void SaveDoom64_DoomBSP(level_t &level, node_t *root_node); -void SaveDoom64_DeePBSPV4(level_t &level, node_t *root_node); +void PutVertices_Integral(WadIO &io, level_t &level, bool only_old); +void PutVertices_Fractional(WadIO &io, level_t &level, bool only_old); -void SaveTextmap_ZNODES(level_t &level, node_t *root_node); +void PutSegs_Vanilla(WadIO &io, level_t &level); +void PutSubsecs_Vanilla(WadIO &io, level_t &level); +void PutNodes_Vanilla(WadIO &io, level_t &level); +void PutLeafs_Vanilla(WadIO &io, level_t &level); + +void PutSegs_DeePBSPV4(WadIO &io, level_t &level); +void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level); +void PutNodes_DeePBSPV4(WadIO &io, level_t &level); +void PutLeafs_DeePBSPV4(WadIO &io, level_t &level); + +void PutTree_ZDBSP(WadIO &io, level_t &level, std::string lumpname, bsp_format_t bsp_format, bool compress); diff --git a/src/core.hpp b/src/core.hpp index ad54ec2..ae1a4a0 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -655,12 +655,18 @@ using lump_order_t = enum lump_order_e static constexpr uint32_t MAX_LUMPS_IN_A_LEVEL = 21; -using raw_vertex_t = struct raw_vertex_s +using raw_vertex_short_t = struct raw_vertex_short_s { int16_t x; int16_t y; } PACKEDATTR; +using raw_vertex_fixed_t = struct raw_vertex_fixed_s +{ + fixed_t x; + fixed_t y; +} PACKEDATTR; + using raw_linedef_doom_t = struct raw_linedef_doom_s { uint16_t start; // from this vertex... @@ -731,12 +737,6 @@ using raw_thing_hexen_t = struct raw_thing_hexen_s // Some are shared with PSX Doom's and PSX Final Doom's formats // but we don't support those -using raw_vertex_doom64_t = struct raw_vertex_doom64_s -{ - fixed_t x; - fixed_t y; -} PACKEDATTR; - using raw_thing_doom64_t = struct raw_thing_doom64_s { int16_t x; // x position of thing @@ -857,14 +857,15 @@ using raw_blockmap_xbm1_header_t = struct raw_blockmap_xbm1_header_s #define static_size(x, y) \ static_assert(sizeof(x) == y, "Size mismatch for '" #x "'. Should be " #y ".") -static_assert(sizeof(raw_vertex_t) == 4, "Size mismatch for 'raw_vertex_t'. Should be 4."); +static_size(raw_vertex_short_t, 4); +static_size(raw_vertex_fixed_t, 8); + static_assert(sizeof(raw_linedef_doom_t) == 14, "Size mismatch for 'raw_linedef_doom_t'. Should be 14."); static_assert(sizeof(raw_sidedef_doom_t) == 30, "Size mismatch for 'raw_sidedef_doom_t'. Should be 30."); static_assert(sizeof(raw_sector_doom_t) == 26, "Size mismatch for 'raw_sector_doom_t'. Should be 26."); static_assert(sizeof(raw_thing_doom_t) == 10, "Size mismatch for 'raw_thing_doom_t'. Should be 10."); static_assert(sizeof(raw_linedef_hexen_t) == 16, "Size mismatch for 'raw_linedef_hexen_t'. Should be 16."); static_assert(sizeof(raw_thing_hexen_t) == 20, "Size mismatch for 'raw_thing_hexen_t'. Should be 20."); -static_assert(sizeof(raw_vertex_doom64_t) == 8, "Size mismatch for 'raw_vertex_doom64_t'. Should be 8."); static_assert(sizeof(raw_thing_doom64_t) == 12, "Size mismatch for 'raw_thing_doom64_t'. Should be 12."); static_assert(sizeof(raw_linedef_doom64_t) == 16, "Size mismatch for 'raw_linedef_doom64_t'. Should be 16."); static_assert(sizeof(raw_sidedef_doom64_t) == 12, "Size mismatch for 'raw_sidedef_doom64_t'. Should be 12."); diff --git a/src/info.cpp b/src/info.cpp index b541406..11ac639 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -110,10 +110,10 @@ static void ComputeTotalBspHeights(const node_t *node, size_t depth, double &lea } } -void GenerateAnalysis(level_t &level, const char *filename) +void GenerateAnalysis(WadIO &io, level_t &level, const char *filename) { auto mark = Benchmarker(__func__); - auto generate_analysis_data = [](level_t &level, bool is_fast, size_t split_cost) -> auto + auto generate_analysis_data = [](WadIO &io, level_t &level, bool is_fast, size_t split_cost) -> auto { AnalysisData data; node_t *analysis_node = nullptr; @@ -174,7 +174,7 @@ void GenerateAnalysis(level_t &level, const char *filename) data.worst_case_ratio = min_epl / max_epl; data.tree_quality = ((min_epl / total_depth_sum) - data.worst_case_ratio) / (1 - data.worst_case_ratio); - std::string line = std::format("{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", level.GetLevelName(), is_fast, + std::string line = std::format("{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", level.GetLevelName(io), is_fast, split_cost, data.vertex, data.lines, data.sides, data.sectors, data.bsp_vertex, data.nodes, data.subsecs, data.segs, data.splits, data.left_depth, data.right_depth, data.average_depth, data.optimal_depth, data.tree_balance, data.worst_case_ratio, data.tree_quality); @@ -193,8 +193,8 @@ void GenerateAnalysis(level_t &level, const char *filename) // across all split costs for (size_t split_cost = 1; split_cost <= 32; split_cost++) { - generate_analysis_data(level, is_fast != 0, split_cost); - PrintLine(LOG_NORMAL, "[%s] Analyzed %s, %s mode, split cost factor of %zu", __func__, level.GetLevelName(), + generate_analysis_data(io, level, is_fast != 0, split_cost); + PrintLine(LOG_NORMAL, "[%s] Analyzed %s, %s mode, split cost factor of %zu", __func__, level.GetLevelName(io), is_fast ? "fast" : "normal", split_cost); } } diff --git a/src/level.cpp b/src/level.cpp index 9b6e4f6..abe1ffb 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -351,9 +351,9 @@ void ValidateLinedef(level_t &level, linedef_t *line) } } -static void GetVertices_Doom(level_t &level) +static void GetVertices_Doom(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "VERTEXES"); + auto lump = io.ReadMapLump(level, "VERTEXES"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -370,9 +370,9 @@ static void GetVertices_Doom(level_t &level) level.num_old_vert = level.vertices.size(); } -static void GetSectors_Doom(level_t &level) +static void GetSectors_Doom(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "SECTORS"); + auto lump = io.ReadMapLump(level, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -386,9 +386,9 @@ static void GetSectors_Doom(level_t &level) } } -static void GetThings_Doom(level_t &level) +static void GetThings_Doom(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "THINGS"); + auto lump = io.ReadMapLump(level, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -405,9 +405,9 @@ static void GetThings_Doom(level_t &level) } } -static void GetSidedefs_Doom(level_t &level) +static void GetSidedefs_Doom(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "SIDEDEFS"); + auto lump = io.ReadMapLump(level, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -427,9 +427,9 @@ static void GetSidedefs_Doom(level_t &level) } } -static void GetLinedefs_Doom(level_t &level) +static void GetLinedefs_Doom(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -509,9 +509,9 @@ static void GetLinedefs_Doom(level_t &level) } } -static void GetThings_Hexen(level_t &level) +static void GetThings_Hexen(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "THINGS"); + auto lump = io.ReadMapLump(level, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -528,9 +528,9 @@ static void GetThings_Hexen(level_t &level) } } -static void GetLinedefs_Hexen(level_t &level) +static void GetLinedefs_Hexen(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -574,9 +574,9 @@ static void GetLinedefs_Hexen(level_t &level) } } -static void GetVertices_Doom64(level_t &level) +static void GetVertices_Doom64(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "VERTEXES"); + auto lump = io.ReadMapLump(level, "VERTEXES"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -594,9 +594,9 @@ static void GetVertices_Doom64(level_t &level) level.num_old_vert = level.vertices.size(); } -static void GetSectors_Doom64(level_t &level) +static void GetSectors_Doom64(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "SECTORS"); + auto lump = io.ReadMapLump(level, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -616,9 +616,9 @@ static void GetSectors_Doom64(level_t &level) } } -static void GetSidedefs_Doom64(level_t &level) +static void GetSidedefs_Doom64(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "SIDEDEFS"); + auto lump = io.ReadMapLump(level, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -636,9 +636,9 @@ static void GetSidedefs_Doom64(level_t &level) } } -static void GetLinedefs_Doom64(level_t &level) +static void GetLinedefs_Doom64(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -681,9 +681,9 @@ static void GetLinedefs_Doom64(level_t &level) } } -static void GetThings_Doom64(level_t &level) +static void GetThings_Doom64(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -1002,9 +1002,9 @@ static void ParseUDMF_Pass(level_t &level, const std::string &data, int pass) } } -void ParseUDMF(level_t &level) +void ParseUDMF(WadIO &io, level_t &level) { - auto lump = io->ReadMapLump(level, "TEXTMAP"); + auto lump = io.ReadMapLump(level, "TEXTMAP"); // load the lump into this string auto data = std::string(lump.begin(), lump.end()); @@ -1027,7 +1027,7 @@ void ParseUDMF(level_t &level) // Check Limits // -static void CheckBinaryFormatLimits(level_t &level) +static void CheckBinaryFormatLimits(WadIO &io, level_t &level) { if (level.num_old_vert > LIMIT_VERT) { @@ -1082,10 +1082,10 @@ bsp_format_t CheckFormatBSP(buildinfo_t &ctx, level_t &level) /* ----- whole-level routines --------------------------- */ -void LoadLevel(level_t &level) +void LoadLevel(WadIO &io, level_t &level) { auto mark = Benchmarker(__func__); - auto LEV = io->LumpName(level.level_header_lump_index); + auto LEV = io.LumpName(level.level_header_lump_index); level.overflows = false; @@ -1097,31 +1097,31 @@ void LoadLevel(level_t &level) switch (level.map_format) { case MapFormat_Doom: - GetVertices_Doom(level); - GetSectors_Doom(level); - GetSidedefs_Doom(level); - GetLinedefs_Doom(level); - GetThings_Doom(level); + GetVertices_Doom(io, level); + GetSectors_Doom(io, level); + GetSidedefs_Doom(io, level); + GetLinedefs_Doom(io, level); + GetThings_Doom(io, level); PruneVerticesAtEnd(level); break; case MapFormat_Hexen: - GetVertices_Doom(level); - GetSectors_Doom(level); - GetSidedefs_Doom(level); - GetLinedefs_Hexen(level); - GetThings_Hexen(level); + GetVertices_Doom(io, level); + GetSectors_Doom(io, level); + GetSidedefs_Doom(io, level); + GetLinedefs_Hexen(io, level); + GetThings_Hexen(io, level); PruneVerticesAtEnd(level); break; case MapFormat_Doom64: - GetVertices_Doom64(level); - GetSectors_Doom64(level); - GetSidedefs_Doom64(level); - GetLinedefs_Doom64(level); - GetThings_Doom64(level); + GetVertices_Doom64(io, level); + GetSectors_Doom64(io, level); + GetSidedefs_Doom64(io, level); + GetLinedefs_Doom64(io, level); + GetThings_Doom64(io, level); PruneVerticesAtEnd(level); break; case MapFormat_UDMF: - ParseUDMF(level); + ParseUDMF(io, level); break; case MapFormat_INVALID: PrintLine(LOG_ERROR, "[%s] Unknown level format on level %s", __func__, LEV); @@ -1174,12 +1174,9 @@ size_t ComputeBspHeight(const node_t *node) /* ----- build nodes for a single level ----- */ -build_result_e BuildLevel(level_t &level, const char *filename) +build_result_e BuildLevel(WadIO &io, level_t &level, const char *filename) { - node_t *root_node = nullptr; - subsec_t *root_sub = nullptr; - - LoadLevel(level); + LoadLevel(io, level); InitBlockmap(level); @@ -1187,7 +1184,7 @@ build_result_e BuildLevel(level_t &level, const char *filename) { if (config.analysis) { - PrintLine(LOG_NORMAL, "[%s] Starting analysis loop for %s", __func__, level.GetLevelName()); + PrintLine(LOG_NORMAL, "[%s] Starting analysis loop for %s", __func__, level.GetLevelName(io)); GenerateAnalysis(level, filename); } @@ -1196,19 +1193,19 @@ build_result_e BuildLevel(level_t &level, const char *filename) seg_t *seg_list = CreateSegs(level); // recursive function T-T auto mark = Benchmarker("BuildNodes"); - BuildNodes(level, seg_list, 0, &dummy, &root_node, &root_sub, config.split_cost, config.fast, false); + BuildNodes(level, seg_list, 0, &dummy, &level.root_node, &level.root_sub, config.split_cost, config.fast, false); } if (config.verbose) { PrintLine(LOG_NORMAL, "Built %zu NODES, %zu SSECTORS, %zu SEGS, %zu VERTEXES", level.nodes.size(), level.subsecs.size(), - level.segs.size(), level.num_old_vert + level.num_new_vert); + level.segs.size(), level.vertices.size()); } - if (config.verbose && root_node != nullptr) + if (config.verbose && level.root_node != nullptr) { - PrintLine(LOG_NORMAL, "Heights of subtrees: %zu / %zu", ComputeBspHeight(root_node->l.node), - ComputeBspHeight(root_node->r.node)); + PrintLine(LOG_NORMAL, "Heights of subtrees: %zu / %zu", ComputeBspHeight(level.root_node->l.node), + ComputeBspHeight(level.root_node->r.node)); } ClockwiseBspTree(level); @@ -1219,10 +1216,10 @@ build_result_e BuildLevel(level_t &level, const char *filename) case MapFormat_Doom: case MapFormat_Hexen: case MapFormat_Doom64: - ret = SaveLevelBinaryFormat(level, root_node); + ret = SaveLevelBinaryFormat(io, level); break; case MapFormat_UDMF: - ret = SaveLevelTextMap(level, root_node); + ret = SaveLevelTextMap(io, level); break; default: break; diff --git a/src/local.hpp b/src/local.hpp index 781f1b2..0cd725a 100644 --- a/src/local.hpp +++ b/src/local.hpp @@ -392,7 +392,6 @@ using level_t = struct level_t size_t num_old_vert = 0; size_t num_new_vert = 0; size_t num_real_lines = 0; - size_t level_num = NO_INDEX; size_t level_header_lump_index = NO_INDEX; bool overflows = false; @@ -402,6 +401,9 @@ using level_t = struct level_t std::vector sectors; std::vector things; + node_t *root_node = nullptr; + subsec_t *root_sub = nullptr; + std::vector segs; std::vector subsecs; std::vector nodes; @@ -427,8 +429,7 @@ using level_t = struct level_t inline const char *GetLevelName(WadIO &io) { - size_t lump_idx = io.LevelHeader(level_num); - return io.read_directory[lump_idx].name; + return io.read_directory[level_header_lump_index].name; } vertex_t *SafeLookupVertex(size_t num, size_t num_line) diff --git a/src/main.cpp b/src/main.cpp index 7e5097c..5703101 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -92,78 +92,6 @@ bool CheckMapInMapList(const char *name) return false; } -static void BuildFile(const char *filename) -{ - config.total_warnings = 0; - - const size_t num_levels = io->LevelCount(); - - if (num_levels == 0) - { - PrintLine(LOG_NORMAL, "No levels in wad"); - total_empty_files += 1; - return; - } - - size_t visited = 0; - size_t failures = 0; - - build_result_e res = BUILD_OK; - - // loop over each level in the wad - for (size_t n = 0; n < num_levels; n++) - { - // IMPORTANT: always ensure a valid map - level_t level; - level.level_num = n; - level.level_header_lump_index = io->LevelHeader(level.level_num); - level.map_format = io->LevelFormat(level.level_num); - - if (!CheckMapInMapList(level.GetLevelName())) - { - continue; - } - - visited += 1; - - res = BuildLevel(level, filename); - - // handle a failed map (due to lump overflow) - if (res == BUILD_LumpOverflow) - { - res = BUILD_OK; - failures += 1; - continue; - } - - if (res != BUILD_OK) - { - break; - } - - total_built_maps += 1; - } - - if (visited == 0) - { - PrintLine(LOG_NORMAL, "No matching levels"); - total_empty_files += 1; - return; - } - - total_failed_maps += failures; - - if (failures > 0) - { - PrintLine(LOG_NORMAL, "Failed maps: %zu (out of %zu)", failures, visited); - - // allow building other files - total_failed_files += 1; - } - - PrintLine(LOG_NORMAL, "Serious warnings: %zu", config.total_warnings); -} - void BackupFile(const char *filename) { std::string dest_name = filename; @@ -213,21 +141,6 @@ void VisitFile(const char *filename) PrintLine(LOG_NORMAL, "Building %s", filename); - // this will fatal error if it fails - io = new WadIO(filename); - if (io == nullptr) - { - PrintLine(LOG_ERROR, "ERROR: Cannot open file: %s", filename); - } - - BuildFile(filename); - - if (io != nullptr) - { - // this closes the file - delete io; - io = nullptr; - } } // ----- user information ----------------------------- diff --git a/src/misc.cpp b/src/misc.cpp index 83c28b6..e08a78b 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -324,9 +324,6 @@ vertex_t *NewVertexFromSplitSeg(level_t &level, seg_t *seg, double x, double y) vert->is_new = true; vert->is_used = true; - vert->index = level.num_new_vert; - level.num_new_vert++; - // compute wall-tip info if (seg->linedef == nullptr) { diff --git a/src/node.cpp b/src/node.cpp index 76ba87a..1a48c26 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -1779,21 +1779,6 @@ void NormaliseBspTree(level_t &level) } } -void RoundOffVertices(level_t &level) -{ - for (size_t i = 0; i < level.vertices.size(); i++) - { - vertex_t *vert = level.vertices[i]; - - if (vert->is_new) - { - vert->is_new = false; - vert->index = level.num_old_vert; - level.num_old_vert++; - } - } -} - void RoundOffSubsector(level_t &level, subsec_t *subsec) { // use head + tail to maintain same order of segs @@ -1916,8 +1901,6 @@ void RoundOffBspTree(level_t &level) { size_t cur_seg_index = 0; - RoundOffVertices(level); - for (size_t i = 0; i < level.subsecs.size(); i++) { subsec_t *sub = level.subsecs[i]; diff --git a/src/wad.cpp b/src/wad.cpp index 55a3dd9..5963174 100644 --- a/src/wad.cpp +++ b/src/wad.cpp @@ -61,7 +61,7 @@ static bool IsLevelLump(const char *name) // WAD Reading Interface //------------------------------------------------------------------------ -WadIO::WadIO(const char *filename) : read_file(nullptr), read_directory(), read_levels() +WadIO::WadIO(const char *filename) { this->read_file = fopen(filename, "rb"); if (this->read_file == NULL) @@ -115,11 +115,6 @@ size_t WadIO::NumLumps(void) const return this->read_directory.size(); } -size_t WadIO::LevelCount(void) const -{ - return this->read_levels.size(); -} - const char *WadIO::LumpName(size_t lump_index) const { static char name[9]; @@ -128,23 +123,16 @@ const char *WadIO::LumpName(size_t lump_index) const return name; } -size_t WadIO::LevelHeader(size_t lev_num) +size_t WadIO::LevelLastLump(size_t lump_index) { - SYS_ASSERT(lev_num < LevelCount()); - return this->read_levels[lev_num]; -} - -size_t WadIO::LevelLastLump(size_t lev_num) -{ - size_t start = this->LevelHeader(lev_num); size_t count = 1; // UDMF level? - if (StringCaseCmp(read_directory[start + 1].name, "TEXTMAP")) + if (StringCaseCmp(read_directory[lump_index + 1].name, "TEXTMAP")) { - while (count < MAX_LUMPS_IN_A_LEVEL && start + count < this->NumLumps()) + while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps()) { - if (StringCaseCmp(read_directory[start + count].name, "ENDMAP")) + if (StringCaseCmp(read_directory[lump_index + count].name, "ENDMAP")) { count++; break; @@ -155,22 +143,22 @@ size_t WadIO::LevelLastLump(size_t lev_num) } else // standard DOOM or HEXEN format { - while (count < MAX_LUMPS_IN_A_LEVEL && start + count < this->NumLumps() && IsLevelLump(read_directory[start + count].name)) + while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps() + && IsLevelLump(read_directory[lump_index + count].name)) { count++; } } - return start + count - 1; + return lump_index + count - 1; } // returns a lump index, NO_INDEX if not found -size_t WadIO::LevelLookupLump(size_t lev_num, const char *name) +size_t WadIO::LevelLookupLump(size_t lump_index, const char *name) { - size_t start = this->LevelHeader(lev_num); - size_t finish = this->LevelLastLump(lev_num); + size_t finish = this->LevelLastLump(lump_index); - for (size_t k = start + 1; k <= finish; k++) + for (size_t k = lump_index + 1; k <= finish; k++) { SYS_ASSERT(k < this->NumLumps()); @@ -183,21 +171,21 @@ size_t WadIO::LevelLookupLump(size_t lev_num, const char *name) return NO_INDEX; // not found } -map_format_e WadIO::LevelFormat(size_t lev_num) +map_format_e WadIO::LevelFormat(size_t lump_index) { // UDMF maps can contain BEHAVIOR or MACROS // check exclusively TEXTMAP - if (this->LevelLookupLump(lev_num, "TEXTMAP") != NO_INDEX) + if (this->LevelLookupLump(lump_index, "TEXTMAP") != NO_INDEX) { return MapFormat_UDMF; } - if (this->LevelLookupLump(lev_num, "BEHAVIOR") != NO_INDEX) + if (this->LevelLookupLump(lump_index, "BEHAVIOR") != NO_INDEX) { return MapFormat_Hexen; } - if (this->LevelLookupLump(lev_num, "LIGHTS") != NO_INDEX && this->LevelLookupLump(lev_num, "MACROS") != NO_INDEX) + if (this->LevelLookupLump(lump_index, "LIGHTS") != NO_INDEX && this->LevelLookupLump(lump_index, "MACROS") != NO_INDEX) { return MapFormat_Doom64; } @@ -205,73 +193,6 @@ map_format_e WadIO::LevelFormat(size_t lev_num) return MapFormat_Doom; } -void WadIO::DetectLevels(void) -{ - // Determine what lumps in the wad are level markers, based on the - // lumps which follow it. Store the result in the 'levels' vector. - // The test here is rather lax, since wads exist with a non-standard - // ordering of level lumps. - for (size_t k = 0; k + 1 < NumLumps(); k++) - { - size_t part_mask = 0; - size_t part_count = 0; - - // Ignore non-header map lumps - // Fixes sliding window bug on single-level WADs - if (WhatLevelPart(read_directory[k].name) != 0) - { - continue; - } - - // check for UDMF levels - if (StringCaseCmp(read_directory[k + 1].name, "TEXTMAP")) - { - this->read_levels.push_back(k); - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Detected level : %s (UDMF)", __func__, read_directory[k].name); - } - - continue; - } - - // check whether the next four lumps are level lumps - for (size_t i = 1; i <= 4; i++) - { - if (k + i >= this->NumLumps()) - { - break; - } - - size_t part = WhatLevelPart(read_directory[k + i].name); - - if (part == 0) - { - break; - } - - // do not allow duplicates - if (part_mask & (1 << part)) - { - break; - } - - part_mask |= (1 << part); - part_count++; - } - - if (part_count == 4) - { - read_levels.push_back(k); - - if (HAS_BIT(config.debug, DEBUG_WAD)) - { - PrintLine(LOG_DEBUG, "[%s] Detected level : %s", __func__, read_directory[k].name); - } - } - } -} - template std::vector WadIO::ReadLump(size_t index) { @@ -293,13 +214,12 @@ std::vector WadIO::ReadLump(size_t index) template std::vector WadIO::ReadMapLump(level_t &level, const char *name) { - return ReadLump(this->LevelLookupLump(level.level_num, name)); + return ReadLump(this->LevelLookupLump(level.level_header_lump_index, name)); } -//------------------------------------------------------------------------ -// WAD Writing Interface -//------------------------------------------------------------------------ - +// +// Write operations +// void WadIO::SafeWrite(const void *buffer, size_t size) { if (fwrite(buffer, 1, size, write_file) != size) @@ -320,11 +240,6 @@ void WadIO::CreateEmptyLump(const char *name) this->write_directory.push_back(lump); } -void WadIO::StartWritingLump(const char *name) -{ - CreateEmptyLump(name); -} - void WadIO::WriteLump(const char *name, const void *data, size_t size) { WadLump lump; @@ -341,6 +256,15 @@ void WadIO::CopyLump(size_t index) WriteLump(this->LumpName(index), lump.data(), lump.size()); } +// +// Partial writing +// + +void WadIO::StartWritingLump(const char *name) +{ + CreateEmptyLump(name); +} + void WadIO::AddToLump(const void *data, size_t len) { size_t current = this->write_directory.size() - 1; @@ -381,3 +305,87 @@ WadIO &WadIO::operator<<(fixed_t value) AddToLump(reinterpret_cast(&value), sizeof(fixed_t)); return *this; } + +// +// Zlib compression support +// + +void WadIO::Begin_Zlib(void) +{ + this->zlib_init = true; + this->zout_stream.zalloc = nullptr; + this->zout_stream.zfree = nullptr; + this->zout_stream.opaque = nullptr; + if (Z_OK != zng_deflateInit(&this->zout_stream, Z_DEFAULT_COMPRESSION)) + { + PrintLine(LOG_ERROR, "ERROR: Trouble starting Zlib compression."); + } + this->zout_stream.next_out = this->zout_buffer; + this->zout_stream.avail_out = sizeof(this->zout_buffer); +} + +void WadIO::AddToLumpZ(const void *data, size_t length) +{ + if (!this->zlib_init) + { + this->AddToLump(data, length); + return; + } + + SYS_ASSERT(length > 0); + + this->zout_stream.next_in = static_cast(data); + this->zout_stream.avail_in = IndexToInt(length); + + while (this->zout_stream.avail_in > 0) + { + if (Z_OK != zng_deflate(&this->zout_stream, Z_NO_FLUSH)) + { + PrintLine(LOG_ERROR, "ERROR: Trouble Zlib compressing %zu bytes.", length); + } + + if (this->zout_stream.avail_out == 0) + { + this->AddToLump(this->zout_buffer, sizeof(this->zout_buffer)); + + this->zout_stream.next_out = this->zout_buffer; + this->zout_stream.avail_out = sizeof(this->zout_buffer); + } + } +} + +void WadIO::Finish_Zlib(void) +{ + SYS_ASSERT(this->zout_stream.avail_out > 0) + size_t left_over; + this->zlib_init = false; + + this->zout_stream.next_in = Z_NULL; + this->zout_stream.avail_in = 0; + + while (true) + { + int32_t err = zng_deflate(&this->zout_stream, Z_FINISH); + + if (err == Z_STREAM_END) break; + + if (err != Z_OK) + { + PrintLine(LOG_ERROR, "ERROR: Trouble finishing Zlib compression."); + } + + if (this->zout_stream.avail_out == 0) + { + this->AddToLump(&this->zout_buffer, sizeof(this->zout_buffer)); + + this->zout_stream.next_out = this->zout_buffer; + this->zout_stream.avail_out = sizeof(this->zout_buffer); + } + } + + left_over = sizeof(zout_buffer) - zout_stream.avail_out; + + if (left_over > 0) this->AddToLump(zout_buffer, left_over); + + zng_deflateEnd(&zout_stream); +} diff --git a/src/wad.hpp b/src/wad.hpp index 67b19c4..2d59a09 100644 --- a/src/wad.hpp +++ b/src/wad.hpp @@ -57,12 +57,14 @@ struct WadIO FILE *read_file; WadHeader read_header; std::vector read_directory; - std::vector read_levels; // these are lump indices (into 'directory' vector) // Write data FILE *write_file; WadHeader write_header; std::vector write_directory; + bool zlib_init = false; + zng_stream zout_stream; + byte zout_buffer[8192]; // Init/die WadIO(const char *filename); @@ -73,11 +75,9 @@ struct WadIO size_t NumLumps(void) const; size_t LevelCount(void) const; const char *LumpName(size_t lump_index) const; - size_t LevelHeader(size_t lev_num); - size_t LevelLastLump(size_t lev_num); - size_t LevelLookupLump(size_t lev_num, const char *name); - map_format_t LevelFormat(size_t lev_num); - void DetectLevels(void); + size_t LevelLastLump(size_t lump_index); + size_t LevelLookupLump(size_t lump_index, const char *name); + map_format_t LevelFormat(size_t lump_index); template std::vector ReadLump(size_t index); template @@ -92,10 +92,14 @@ struct WadIO // Partial writing void StartWritingLump(const char *name); void AddToLump(const void *data, size_t len); - void AddToLumpZ(const void *data, size_t len); WadIO &operator<<(uint8_t value); WadIO &operator<<(uint16_t value); WadIO &operator<<(uint32_t value); WadIO &operator<<(int16_t value); WadIO &operator<<(fixed_t value); + + // Zlib compression support + void Begin_Zlib(void); + void AddToLumpZ(const void *data, size_t length); + void Finish_Zlib(void); }; From 66ef515be4f194350e29dd1d75579a051e106d32 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Wed, 20 May 2026 22:19:39 -0300 Subject: [PATCH 03/10] its running with the new code! the output wads are kind broked, though... --- docs/command_line.md | 8 +- src/bsp.cpp | 24 ++--- src/bsp.hpp | 8 +- src/core.hpp | 72 --------------- src/info.cpp | 9 +- src/info.hpp | 27 ++++++ src/level.cpp | 204 +++++++++++++++++++++---------------------- src/local.hpp | 4 +- src/main.cpp | 116 ++++++++++++------------ src/misc.cpp | 1 + src/wad.cpp | 189 +++++++++++++++++++++++++++------------ src/wad.hpp | 41 +++++++-- 12 files changed, 389 insertions(+), 314 deletions(-) create mode 100644 src/info.hpp diff --git a/docs/command_line.md b/docs/command_line.md index 2149fb8..7db92f7 100644 --- a/docs/command_line.md +++ b/docs/command_line.md @@ -1,7 +1,6 @@ # Command Line Interface ELFBSP is a command-line tool. -It can handle multiple wad files, and while it modifies each file in-place, there is an option to backup each file first. The output to the terminal is fairly terse, but greater verbosity can be enabled. Generally all the maps in a wad will processed, but this can be limited to a specific set. @@ -40,11 +39,6 @@ elfbsp example.wad --map MAP04,MAP22-MAP25 # or you may combine both Produces more verbose output to the terminal. Some warnings which are normally hidden (except for a final tally) will be shown when enabled. -#### `-b --backup` -Backs up each input file before processing it. -The backup files will have the ".bak" extension (replacing the ".wad" extension). -If the backup file already exists, it will be silently overwritten. - #### `-f --fast` Enables a faster method for selecting partition lines. On large maps this can be significantly faster, however the BSP tree may not be as good. @@ -103,7 +97,7 @@ Generates CSV files containing multiple builds of the input maps, used for data #### `-o --output FILE` This option is provided *only* for compatibility with existing node builders. It causes the input file to be copied to the specified file, and that file is the one processed. -This option *cannot* be used with multiple input files, or with the --backup option. +This option *cannot* be used with multiple input files. #### `-h --help` Displays a brief help screen, then exits. diff --git a/src/bsp.cpp b/src/bsp.cpp index b70a3df..e5a520d 100644 --- a/src/bsp.cpp +++ b/src/bsp.cpp @@ -140,7 +140,7 @@ void PutVertices_Fractional(WadIO &io, level_t &level, bool only_old) // Vanilla format // -void PutSegs_Vanilla(WadIO &io, level_t &level) +void PutSegs_DoomBSP(WadIO &io, level_t &level) { io.StartWritingLump("SEGS"); @@ -157,18 +157,18 @@ void PutSegs_Vanilla(WadIO &io, level_t &level) raw.flip = GetLittleEndian(seg->side); raw.dist = GetLittleEndian(VanillaSegDist(seg)); + io.AddToLump(&raw, sizeof(raw)); + if (HAS_BIT(config.debug, DEBUG_BSP)) { PrintLine(LOG_DEBUG, "[%s] %zu Vert %04X->%04X Line %04X %s Angle %04X (%1.1f,%1.1f) -> (%1.1f,%1.1f)", __func__, seg->index, GetLittleEndian(raw.start), GetLittleEndian(raw.end), GetLittleEndian(raw.linedef), seg->side ? "L" : "R", GetLittleEndian(raw.angle), seg->start->x, seg->start->y, seg->end->x, seg->end->y); } - - io.AddToLump(&raw, sizeof(raw)); } } -void PutSubsecs_Vanilla(WadIO &io, level_t &level) +void PutSubsecs_DoomBSP(WadIO &io, level_t &level) { io.StartWritingLump("SSECTORS"); @@ -257,7 +257,7 @@ static void PutOneNode_Vanilla(WadIO &io, node_t *node, size_t &node_cur_index) } } -void PutNodes_Vanilla(WadIO &io, level_t &level) +void PutNodes_DoomBSP(WadIO &io, level_t &level) { size_t node_cur_index = 0; io.StartWritingLump("NODES"); @@ -273,7 +273,7 @@ void PutNodes_Vanilla(WadIO &io, level_t &level) } } -void PutLeafs_Vanilla(WadIO &io, level_t &level) +void PutLeafs_DoomBSP(WadIO &io, level_t &level) { io.StartWritingLump("LEAFS"); uint16_t actual_seg_index = 0; @@ -282,11 +282,13 @@ void PutLeafs_Vanilla(WadIO &io, level_t &level) { subsec_t *subsec = level.subsecs[i]; seg_t *seg = subsec->seg_list; - size_t seg_count = subsec->seg_count; + uint16_t seg_count = IndexToShort(subsec->seg_count); + + io.AddToLump(&seg_count, sizeof(seg_count)); if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %zu", __func__, i, seg_count); + PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %hu", __func__, i, seg_count); } for (size_t j = 0; j < seg_count; j++) @@ -465,11 +467,13 @@ void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) { subsec_t *subsec = level.subsecs[i]; seg_t *seg = subsec->seg_list; - size_t seg_count = subsec->seg_count; + uint32_t seg_count = IndexToInt(subsec->seg_count); + + io.AddToLump(&seg_count, sizeof(seg_count)); if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %zu", __func__, i, seg_count); + PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %u", __func__, i, seg_count); } for (size_t j = 0; j < seg_count; j++) diff --git a/src/bsp.hpp b/src/bsp.hpp index 636bec0..faf4ff3 100644 --- a/src/bsp.hpp +++ b/src/bsp.hpp @@ -202,10 +202,10 @@ void SortSegs(level_t &level); void PutVertices_Integral(WadIO &io, level_t &level, bool only_old); void PutVertices_Fractional(WadIO &io, level_t &level, bool only_old); -void PutSegs_Vanilla(WadIO &io, level_t &level); -void PutSubsecs_Vanilla(WadIO &io, level_t &level); -void PutNodes_Vanilla(WadIO &io, level_t &level); -void PutLeafs_Vanilla(WadIO &io, level_t &level); +void PutSegs_DoomBSP(WadIO &io, level_t &level); +void PutSubsecs_DoomBSP(WadIO &io, level_t &level); +void PutNodes_DoomBSP(WadIO &io, level_t &level); +void PutLeafs_DoomBSP(WadIO &io, level_t &level); void PutSegs_DeePBSPV4(WadIO &io, level_t &level); void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level); diff --git a/src/core.hpp b/src/core.hpp index ae1a4a0..f8e0ad8 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -39,8 +39,6 @@ #include #include -#include -#include #include "zlib-ng.h" @@ -408,70 +406,6 @@ constexpr void UtilFree(T *data) free(data); } -//------------------------------------------------------------------------ -// FILE MANAGEMENT -//------------------------------------------------------------------------ - -inline void FileClear(const char *filename) -{ - if (FILE *fp = fopen(filename, "w")) - { - fclose(fp); - } -} - -inline bool FileExists(const char *filename) -{ - - if (FILE *fp = fopen(filename, "rb")) - { - fclose(fp); - return true; - } - - return false; -} - -inline bool FileCopy(const char *src_name, const char *dest_name) -{ - char buffer[MSG_BUFFER_LENGTH]; - - FILE *src = fopen(src_name, "rb"); - if (!src) - { - return false; - } - - FILE *dest = fopen(dest_name, "wb"); - if (!dest) - { - fclose(src); - return false; - } - - while (true) - { - size_t rlen = fread(buffer, 1, sizeof(buffer), src); - if (rlen == 0) - { - break; - } - - size_t wlen = fwrite(buffer, 1, rlen, dest); - if (wlen != rlen) - { - break; - } - } - - bool was_OK = !ferror(src) && !ferror(dest); - - fclose(dest); - fclose(src); - - return was_OK; -} - //------------------------------------------------------------------------ // STRINGS //------------------------------------------------------------------------ @@ -1140,7 +1074,6 @@ struct buildinfo_s bsp_format_t bsp_format = bsp_format_t::BSP_XNOD; bmap_format_t bmap_format = bmap_format_t::BMAP_DoomBSP; bool fast = false; // use a faster method to pick nodes - bool backup = false; // keep a copy of the WAD bool analysis = false; // write out CSV for data analysis and visualization bool verbose = false; // this affects how some messages are shown bool effects = true; // disable special effects @@ -1175,7 +1108,6 @@ constexpr const char PRINT_HELP[] = "\n" "\n" "Available options are:\n" " -v --verbose Verbose output, show all warnings\n" - " -b --backup Backup input files (.bak extension)\n" " -f --fast Faster partition selection\n" " -m --map XXXX Control which map(s) are built\n" " -c --cost ## Cost assigned to seg splits (1-32)\n" @@ -1210,10 +1142,6 @@ size_t LevelsInWad(void); // BUILD_LumpOverflow if some limits were exceeded. build_result_e BuildLevel(struct level_t &level, const char *filename); -void SetupAnalysisFile(const char *filepath); -void GenerateAnalysis(level_t &level, const char *filename); -void WriteAnalysis(const char *filename); - // // Benchmark // diff --git a/src/info.cpp b/src/info.cpp index 11ac639..9e3a210 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -28,6 +28,14 @@ static std::vector analysis_csv; //------------------------------------------------------------------------ +static void FileClear(const char *filename) +{ + if (FILE *fp = fopen(filename, "w")) + { + fclose(fp); + } +} + void SetupAnalysisFile(const char *filepath) { auto csv_path = std::string(filepath); @@ -138,7 +146,6 @@ void GenerateAnalysis(WadIO &io, level_t &level, const char *filename) analysis_seg = CreateSegs(level); BuildNodes(level, analysis_seg, 0, &dummy, &analysis_node, &analysis_sub, static_cast(split_cost), is_fast, true); - // TODO: pass level data by context instead of globally :v data.vertex = level.num_old_vert; data.lines = level.linedefs.size(); data.sides = level.sidedefs.size(); diff --git a/src/info.hpp b/src/info.hpp new file mode 100644 index 0000000..696a4ec --- /dev/null +++ b/src/info.hpp @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// ELFBSP +// +//------------------------------------------------------------------------------ +// +// Copyright 2026 Guilherme Miranda +// +// 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. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include "wad.hpp" + +void SetupAnalysisFile(const char *filepath); +void GenerateAnalysis(WadIO &io, level_t &level, const char *filename); +void WriteAnalysis(const char *filename); diff --git a/src/level.cpp b/src/level.cpp index abe1ffb..366067c 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -23,6 +23,7 @@ #include "bsp.hpp" #include "core.hpp" +#include "info.hpp" #include "local.hpp" #include "parse.hpp" @@ -302,7 +303,7 @@ void FreeIntersections(level_t &level) /* ----- reading routines ------------------------------ */ -void ValidateLinedef(level_t &level, linedef_t *line) +static void ValidateLinedef(level_t &level, linedef_t *line) { if (line->right || line->left) { @@ -351,20 +352,40 @@ void ValidateLinedef(level_t &level, linedef_t *line) } } -static void GetVertices_Doom(WadIO &io, level_t &level) +static void GetVertices_Integral(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "VERTEXES"); + auto lump = io.ReadMapLump(level.lump, "VERTEXES"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { vertex_t *vert = NewVertex(level); - vert->x = ShortToFloat(GetLittleEndian(lump[i].x)); - vert->y = ShortToFloat(GetLittleEndian(lump[i].y)); + vert->x = ShortToFloat(GetLittleEndian(raw.x)); + vert->y = ShortToFloat(GetLittleEndian(raw.y)); + } + + level.num_old_vert = level.vertices.size(); +} + +static void GetVertices_Fractional(WadIO &io, level_t &level) +{ + auto lump = io.ReadMapLump(level.lump, "VERTEXES"); + + if (HAS_BIT(config.debug, DEBUG_LOAD)) + { + PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); + } + + for (auto &raw : lump) + { + vertex_t *vert = NewVertex(level); + + vert->x = static_cast(GetLittleEndian(raw.x) / FRACFACTOR); + vert->y = static_cast(GetLittleEndian(raw.y) / FRACFACTOR); } level.num_old_vert = level.vertices.size(); @@ -372,14 +393,14 @@ static void GetVertices_Doom(WadIO &io, level_t &level) static void GetSectors_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "SECTORS"); + auto lump = io.ReadMapLump(level.lump, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &_ : lump) { sector_t *sector = NewSector(level); sector->effects = FX_Sector_None; @@ -388,70 +409,69 @@ static void GetSectors_Doom(WadIO &io, level_t &level) static void GetThings_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "THINGS"); + auto lump = io.ReadMapLump(level.lump, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { thing_t *thing = NewThing(level); - thing->x = GetLittleEndian(lump[i].x); - thing->y = GetLittleEndian(lump[i].y); - thing->type = static_cast(GetLittleEndian(lump[i].type)); + thing->x = GetLittleEndian(raw.x); + thing->y = GetLittleEndian(raw.y); + thing->type = static_cast(GetLittleEndian(raw.type)); } } static void GetSidedefs_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "SIDEDEFS"); + auto lump = io.ReadMapLump(level.lump, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { sidedef_t *side = NewSidedef(level); - side->offset_x = GetLittleEndian(lump[i].x_offset); - side->offset_y = GetLittleEndian(lump[i].y_offset); - memcpy(side->tex_upper, lump[i].upper_tex, 8); - memcpy(side->tex_lower, lump[i].lower_tex, 8); - memcpy(side->tex_middle, lump[i].mid_tex, 8); - side->sector = level.SafeLookupSector(GetLittleEndian(lump[i].sector), i); + side->offset_x = GetLittleEndian(raw.x_offset); + side->offset_y = GetLittleEndian(raw.y_offset); + memcpy(side->tex_upper, raw.upper_tex, 8); + memcpy(side->tex_lower, raw.lower_tex, 8); + memcpy(side->tex_middle, raw.mid_tex, 8); + side->sector = level.SafeLookupSector(GetLittleEndian(raw.sector), side->index); } } static void GetLinedefs_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { - linedef_t *line = NewLinedef(level); - line->start = level.SafeLookupVertex(GetLittleEndian(lump[i].start), i); - line->end = level.SafeLookupVertex(GetLittleEndian(lump[i].end), i); - line->right = level.SafeLookupSidedef(GetLittleEndian(lump[i].right)); - line->left = level.SafeLookupSidedef(GetLittleEndian(lump[i].left)); - line->special = GetLittleEndian(lump[i].special); - line->tag = GetLittleEndian(lump[i].tag); + line->start = level.SafeLookupVertex(GetLittleEndian(raw.start), line->index); + line->end = level.SafeLookupVertex(GetLittleEndian(raw.end), line->index); + line->right = level.SafeLookupSidedef(GetLittleEndian(raw.right)); + line->left = level.SafeLookupSidedef(GetLittleEndian(raw.left)); + line->special = GetLittleEndian(raw.special); + line->tag = GetLittleEndian(raw.tag); line->start->is_used = true; line->end->is_used = true; - if (HAS_BIT(GetLittleEndian(lump[i].flags), MLF_TWOSIDED)) + if (HAS_BIT(GetLittleEndian(raw.flags), MLF_TWOSIDED)) { line->effects |= FX_TwoSided; } @@ -511,46 +531,46 @@ static void GetLinedefs_Doom(WadIO &io, level_t &level) static void GetThings_Hexen(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "THINGS"); + auto lump = io.ReadMapLump(level.lump, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { thing_t *thing = NewThing(level); - thing->x = GetLittleEndian(lump[i].x); - thing->y = GetLittleEndian(lump[i].y); - thing->type = static_cast(GetLittleEndian(lump[i].type)); + thing->x = GetLittleEndian(raw.x); + thing->y = GetLittleEndian(raw.y); + thing->type = static_cast(GetLittleEndian(raw.type)); } } static void GetLinedefs_Hexen(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { linedef_t *line = NewLinedef(level); - line->start = level.SafeLookupVertex(GetLittleEndian(lump[i].start), i); - line->end = level.SafeLookupVertex(GetLittleEndian(lump[i].end), i); - line->special = lump[i].special; - line->right = level.SafeLookupSidedef(GetLittleEndian(lump[i].right)); - line->left = level.SafeLookupSidedef(GetLittleEndian(lump[i].left)); + line->start = level.SafeLookupVertex(GetLittleEndian(raw.start), line->index); + line->end = level.SafeLookupVertex(GetLittleEndian(raw.end), line->index); + line->special = raw.special; + line->right = level.SafeLookupSidedef(GetLittleEndian(raw.right)); + line->left = level.SafeLookupSidedef(GetLittleEndian(raw.left)); line->start->is_used = true; line->end->is_used = true; - if (HAS_BIT(GetLittleEndian(lump[i].flags), MLF_HEXEN_TWOSIDED)) + if (HAS_BIT(GetLittleEndian(raw.flags), MLF_HEXEN_TWOSIDED)) { line->effects |= FX_TwoSided; } @@ -562,11 +582,11 @@ static void GetLinedefs_Hexen(WadIO &io, level_t &level) switch (line->special) { case BSP_SpecialEffects: - if (lump[i].args[0]) line->effects |= FX_NoBlockmap; - if (lump[i].args[1]) line->effects |= FX_DoNotSplitSeg; - if (lump[i].args[2]) line->effects |= FX_DoNotRenderBack; - if (lump[i].args[3]) line->effects |= FX_DoNotRenderFront; - if (lump[i].args[4]) line->effects |= FX_NoReject; + if (raw.args[0]) line->effects |= FX_NoBlockmap; + if (raw.args[1]) line->effects |= FX_DoNotSplitSeg; + if (raw.args[2]) line->effects |= FX_DoNotRenderBack; + if (raw.args[3]) line->effects |= FX_DoNotRenderFront; + if (raw.args[4]) line->effects |= FX_NoReject; break; default: break; @@ -574,42 +594,22 @@ static void GetLinedefs_Hexen(WadIO &io, level_t &level) } } -static void GetVertices_Doom64(WadIO &io, level_t &level) -{ - auto lump = io.ReadMapLump(level, "VERTEXES"); - - if (HAS_BIT(config.debug, DEBUG_LOAD)) - { - PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); - } - - for (size_t i = 0; i < lump.size(); i++) - { - vertex_t *vert = NewVertex(level); - - vert->x = static_cast(GetLittleEndian(lump[i].x) / FRACFACTOR); - vert->y = static_cast(GetLittleEndian(lump[i].y) / FRACFACTOR); - } - - level.num_old_vert = level.vertices.size(); -} - static void GetSectors_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "SECTORS"); + auto lump = io.ReadMapLump(level.lump, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { sector_t *sector = NewSector(level); if (!config.effects) continue; - if (GetLittleEndian(lump[i].special) == SS_Doom64_NoReject) + if (GetLittleEndian(raw.special) == SS_Doom64_NoReject) { sector->effects = FX_Sector_NoReject; } @@ -618,48 +618,48 @@ static void GetSectors_Doom64(WadIO &io, level_t &level) static void GetSidedefs_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "SIDEDEFS"); + auto lump = io.ReadMapLump(level.lump, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { sidedef_t *side = NewSidedef(level); - side->offset_x = GetLittleEndian(lump[i].x_offset); - side->offset_y = GetLittleEndian(lump[i].y_offset); + side->offset_x = GetLittleEndian(raw.x_offset); + side->offset_y = GetLittleEndian(raw.y_offset); // We don't care about texture indexes here - side->sector = level.SafeLookupSector(GetLittleEndian(lump[i].sector), i); + side->sector = level.SafeLookupSector(GetLittleEndian(raw.sector), side->index); } } static void GetLinedefs_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { linedef_t *line = NewLinedef(level); - line->start = level.SafeLookupVertex(GetLittleEndian(lump[i].start), i); - line->end = level.SafeLookupVertex(GetLittleEndian(lump[i].end), i); - line->special = GetLittleEndian(lump[i].special); - line->tag = GetLittleEndian(lump[i].tag); - line->right = level.SafeLookupSidedef(GetLittleEndian(lump[i].right)); - line->left = level.SafeLookupSidedef(GetLittleEndian(lump[i].left)); + line->start = level.SafeLookupVertex(GetLittleEndian(raw.start), line->index); + line->end = level.SafeLookupVertex(GetLittleEndian(raw.end), line->index); + line->special = GetLittleEndian(raw.special); + line->tag = GetLittleEndian(raw.tag); + line->right = level.SafeLookupSidedef(GetLittleEndian(raw.right)); + line->left = level.SafeLookupSidedef(GetLittleEndian(raw.left)); line->start->is_used = true; line->end->is_used = true; - auto flags = GetLittleEndian(lump[i].flags); + auto flags = GetLittleEndian(raw.flags); if (HAS_BIT(flags, MLF_DOOM64_TWOSIDED)) { @@ -683,20 +683,20 @@ static void GetLinedefs_Doom64(WadIO &io, level_t &level) static void GetThings_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "LINEDEFS"); + auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { PrintLine(LOG_DEBUG, "[%s] num = %zu", __func__, lump.size()); } - for (size_t i = 0; i < lump.size(); i++) + for (auto &raw : lump) { thing_t *thing = NewThing(level); - thing->x = GetLittleEndian(lump[i].x); - thing->y = GetLittleEndian(lump[i].y); - thing->type = static_cast(GetLittleEndian(lump[i].type)); + thing->x = GetLittleEndian(raw.x); + thing->y = GetLittleEndian(raw.y); + thing->type = static_cast(GetLittleEndian(raw.type)); } } @@ -1004,7 +1004,7 @@ static void ParseUDMF_Pass(level_t &level, const std::string &data, int pass) void ParseUDMF(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level, "TEXTMAP"); + auto lump = io.ReadMapLump(level.lump, "TEXTMAP"); // load the lump into this string auto data = std::string(lump.begin(), lump.end()); @@ -1054,7 +1054,7 @@ static void CheckBinaryFormatLimits(WadIO &io, level_t &level) } } -bsp_format_t CheckFormatBSP(buildinfo_t &ctx, level_t &level) +static bsp_format_t CheckFormatBSP(buildinfo_t &ctx, level_t &level) { bsp_format_t level_type = ctx.bsp_format; @@ -1085,7 +1085,7 @@ bsp_format_t CheckFormatBSP(buildinfo_t &ctx, level_t &level) void LoadLevel(WadIO &io, level_t &level) { auto mark = Benchmarker(__func__); - auto LEV = io.LumpName(level.level_header_lump_index); + auto LEV = io.LumpName(level.lump); level.overflows = false; @@ -1097,7 +1097,7 @@ void LoadLevel(WadIO &io, level_t &level) switch (level.map_format) { case MapFormat_Doom: - GetVertices_Doom(io, level); + GetVertices_Integral(io, level); GetSectors_Doom(io, level); GetSidedefs_Doom(io, level); GetLinedefs_Doom(io, level); @@ -1105,7 +1105,7 @@ void LoadLevel(WadIO &io, level_t &level) PruneVerticesAtEnd(level); break; case MapFormat_Hexen: - GetVertices_Doom(io, level); + GetVertices_Integral(io, level); GetSectors_Doom(io, level); GetSidedefs_Doom(io, level); GetLinedefs_Hexen(io, level); @@ -1113,7 +1113,7 @@ void LoadLevel(WadIO &io, level_t &level) PruneVerticesAtEnd(level); break; case MapFormat_Doom64: - GetVertices_Doom64(io, level); + GetVertices_Fractional(io, level); GetSectors_Doom64(io, level); GetSidedefs_Doom64(io, level); GetLinedefs_Doom64(io, level); @@ -1151,9 +1151,7 @@ void LoadLevel(WadIO &io, level_t &level) } } -// -// INSERT OUTPUT HERE -// +// TODO: map output goes here //------------------------------------------------------------------------ // MAIN STUFF @@ -1185,7 +1183,7 @@ build_result_e BuildLevel(WadIO &io, level_t &level, const char *filename) if (config.analysis) { PrintLine(LOG_NORMAL, "[%s] Starting analysis loop for %s", __func__, level.GetLevelName(io)); - GenerateAnalysis(level, filename); + GenerateAnalysis(io, level, filename); } bbox_t dummy; @@ -1216,10 +1214,10 @@ build_result_e BuildLevel(WadIO &io, level_t &level, const char *filename) case MapFormat_Doom: case MapFormat_Hexen: case MapFormat_Doom64: - ret = SaveLevelBinaryFormat(io, level); + // ret = SaveLevelBinaryFormat(io, level); break; case MapFormat_UDMF: - ret = SaveLevelTextMap(io, level); + // ret = SaveLevelTextMap(io, level); break; default: break; diff --git a/src/local.hpp b/src/local.hpp index 0cd725a..6e3ab5d 100644 --- a/src/local.hpp +++ b/src/local.hpp @@ -392,7 +392,7 @@ using level_t = struct level_t size_t num_old_vert = 0; size_t num_new_vert = 0; size_t num_real_lines = 0; - size_t level_header_lump_index = NO_INDEX; + size_t lump = NO_INDEX; bool overflows = false; std::vector vertices; @@ -429,7 +429,7 @@ using level_t = struct level_t inline const char *GetLevelName(WadIO &io) { - return io.read_directory[level_header_lump_index].name; + return io.read_directory[lump].name; } vertex_t *SafeLookupVertex(size_t num, size_t num_line) diff --git a/src/main.cpp b/src/main.cpp index 5703101..d88e624 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,7 @@ //------------------------------------------------------------------------------ #include "core.hpp" -#include "local.hpp" +#include "info.hpp" #include "wad.hpp" #include @@ -53,7 +53,23 @@ buildinfo_t config; //------------------------------------------------------------------------ -bool CheckMapInRange(const map_range_t *range, const char *name) +//------------------------------------------------------------------------ +// FILE MANAGEMENT +//------------------------------------------------------------------------ + +static bool FileExists(const char *filename) +{ + + if (FILE *fp = fopen(filename, "rb")) + { + fclose(fp); + return true; + } + + return false; +} + +static bool CheckMapInRange(const map_range_t *range, const char *name) { if (strlen(name) != range->low.size()) { @@ -73,7 +89,7 @@ bool CheckMapInRange(const map_range_t *range, const char *name) return true; } -bool CheckMapInMapList(const char *name) +static bool CheckMapInMapList(const char *name) { // when --map is not used, allow everything if (map_list.empty()) @@ -92,46 +108,24 @@ bool CheckMapInMapList(const char *name) return false; } -void BackupFile(const char *filename) +static void VisitFile(const char *filename) { - std::string dest_name = filename; + auto mark = Benchmarker(__func__); + auto in_file = std::string(filename); + auto out_file = std::string(); - // replace file extension (if any) with .bak - - size_t ext_pos = FindExtension(filename); - if (ext_pos > 0) - { - dest_name.resize(ext_pos); - } - - dest_name += ".bak"; - - if (!FileCopy(filename, dest_name.c_str())) - { - PrintLine(LOG_ERROR, "ERROR: failed to create backup: %s", dest_name.c_str()); - } - - PrintLine(LOG_NORMAL, "Created backup: %s", dest_name.c_str()); -} - -void VisitFile(const char *filename) -{ // handle the -o option - if (!opt_output.empty()) + // by this point, we are guaranteed to have an opt_output that is different than wad_list[0] + bool is_same_file = false; + if (opt_output.empty()) { - if (!FileCopy(filename, opt_output.c_str())) - { - PrintLine(LOG_ERROR, "ERROR: failed to create output file: %s", opt_output.c_str()); - } - - PrintLine(LOG_NORMAL, "Copied input file: %s", filename); - - filename = opt_output.c_str(); + is_same_file = true; + out_file = in_file + ".x"; } - - if (config.backup) + else { - BackupFile(filename); + is_same_file = false; + out_file = opt_output; } if (config.analysis) @@ -140,12 +134,36 @@ void VisitFile(const char *filename) } PrintLine(LOG_NORMAL, "Building %s", filename); + { + auto wad = WadIO(in_file, out_file); + size_t current = 0; + size_t max = wad.NumLumps(); + while (current < max) + { + if (wad.IsLevel(current)) + { + PrintLine(LOG_NORMAL, "Processing level %zu:%s", current, wad.LumpName(current)); + } + // TODO: process glbsp + wad.CopyLump(current); + current++; + } + } + + if (is_same_file) + { + remove(in_file.c_str()); + if (rename(out_file.c_str(), in_file.c_str())) + { + throw std::runtime_error("ERROR: Failed to rename output file (" + out_file + ") to input file (" + in_file + ")"); + } + } } // ----- user information ----------------------------- -bool ValidateMapName(char *name) +static bool ValidateMapName(char *name) { if (strlen(name) < 2 || strlen(name) > 8) { @@ -174,7 +192,7 @@ bool ValidateMapName(char *name) return true; } -void ParseMapRange(char *tok) +static void ParseMapRange(char *tok) { char *low = tok; char *high = tok; @@ -229,7 +247,7 @@ void ParseMapRange(char *tok) map_list.push_back(range); } -void ParseMapList(const char *arg) +static void ParseMapList(const char *arg) { while (*arg != 0) { @@ -263,7 +281,7 @@ void ParseMapList(const char *arg) } } -void ParseShortArgument(const char *arg) +static void ParseShortArgument(const char *arg) { // skip the leading '-' arg++; @@ -284,9 +302,6 @@ void ParseShortArgument(const char *arg) case 'v': config.verbose = true; continue; - case 'b': - config.backup = true; - continue; case 'f': config.fast = true; continue; @@ -335,7 +350,7 @@ void ParseShortArgument(const char *arg) } } -bool ProcessDebugParam(const char *param, uint32_t &debug) +static bool ProcessDebugParam(const char *param, uint32_t &debug) { if (strcmp(param, "--debug-blockmap") == 0) { @@ -397,7 +412,7 @@ bool ProcessDebugParam(const char *param, uint32_t &debug) return debug != 0; } -int32_t ParseLongArgument(const char *name, const int32_t argc, const char *argv[]) +static int32_t ParseLongArgument(const char *name, const int32_t argc, const char *argv[]) { int32_t used = 0; @@ -417,10 +432,6 @@ int32_t ParseLongArgument(const char *name, const int32_t argc, const char *argv { config.verbose = true; } - else if (strcmp(name, "--backup") == 0 || strcmp(name, "--backups") == 0) - { - config.backup = true; - } else if (strcmp(name, "--fast") == 0) { config.fast = true; @@ -639,11 +650,6 @@ int32_t main(const int32_t argc, const char *argv[]) if (!opt_output.empty()) { - if (config.backup) - { - PrintLine(LOG_ERROR, "ERROR: cannot use --backup with --output"); - } - if (total_files > 1) { PrintLine(LOG_ERROR, "ERROR: cannot use multiple input files with --output"); diff --git a/src/misc.cpp b/src/misc.cpp index e08a78b..f1f7394 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -323,6 +323,7 @@ vertex_t *NewVertexFromSplitSeg(level_t &level, seg_t *seg, double x, double y) vert->is_new = true; vert->is_used = true; + level.num_new_vert++; // compute wall-tip info if (seg->linedef == nullptr) diff --git a/src/wad.cpp b/src/wad.cpp index 5963174..43d47d6 100644 --- a/src/wad.cpp +++ b/src/wad.cpp @@ -61,26 +61,31 @@ static bool IsLevelLump(const char *name) // WAD Reading Interface //------------------------------------------------------------------------ -WadIO::WadIO(const char *filename) +WadIO::WadIO(std::string in_file, std::string out_file) { - this->read_file = fopen(filename, "rb"); + // Input stuff + this->read_file = fopen(in_file.c_str(), "rb"); if (this->read_file == NULL) { throw std::runtime_error("ERROR: Could not open input file"); } this->SafeRead(&this->read_header, sizeof(this->read_header)); - if (StringCaseCmpMax(this->read_header.ident, "PWAD", 4) != 0 || StringCaseCmpMax(this->read_header.ident, "IWAD", 4) != 0) + if (this->read_header.ident[0] != 'I' // check for IWADs (base game resources) + && this->read_header.ident[0] != 'P' // or PWADs (additional/modded resources) + && this->read_header.ident[1] != 'W' // + && this->read_header.ident[2] != 'A' // + && this->read_header.ident[3] != 'D') { - fclose(read_file); - read_file = NULL; + fclose(this->read_file); + this->read_file = NULL; throw std::runtime_error("ERROR: Input file is not a wad"); } this->read_header.num_entries = GetLittleEndian(this->read_header.num_entries); this->read_header.dir_start = GetLittleEndian(this->read_header.dir_start); - if (fseek(read_file, this->read_header.dir_start, SEEK_SET)) + if (fseek(this->read_file, this->read_header.dir_start, SEEK_SET)) { throw std::runtime_error("ERROR: Could not read wad directory"); } @@ -93,12 +98,47 @@ WadIO::WadIO(const char *filename) this->read_directory[i].pos = GetLittleEndian(this->read_directory[i].pos); this->read_directory[i].size = GetLittleEndian(this->read_directory[i].size); } + + // Output stuff + this->write_file = fopen(out_file.c_str(), "wb"); + if (this->write_file == NULL) + { + throw std::runtime_error("ERROR: Could not open output file"); + } + + this->write_header.ident[0] = this->read_header.ident[0]; + this->write_header.ident[1] = this->read_header.ident[1]; + this->write_header.ident[2] = this->read_header.ident[2]; + this->write_header.ident[3] = this->read_header.ident[3]; + this->write_header.num_entries = 0; + this->write_header.dir_start = 0; + + this->SafeWrite(&this->write_header, sizeof(this->write_header)); } WadIO::~WadIO(void) { - fclose(read_file); + // Input stuff + fclose(this->read_file); this->read_directory.clear(); + + // Output stuff + if (this->write_file) + { + // Send entire directory, right after + this->SafeWrite(this->write_directory.data(), sizeof(WadLump) * this->write_directory.size()); + + // Skip file magic header and write directory data + uint32_t num_entries = GetLittleEndian(IndexToInt(this->write_directory.size())); + uint32_t dir_start = GetLittleEndian(static_cast(ftell(this->write_file))); + + fseek(this->write_file, sizeof(char[4]), SEEK_SET); + this->SafeWrite(&num_entries, sizeof(num_entries)); + this->SafeWrite(&dir_start, sizeof(dir_start)); + fclose(this->write_file); + + this->write_file = NULL; + } } // call only after seeking @@ -123,52 +163,67 @@ const char *WadIO::LumpName(size_t lump_index) const return name; } -size_t WadIO::LevelLastLump(size_t lump_index) +bool WadIO::IsLevel(size_t k) { - size_t count = 1; + // Determine what lumps in the wad are level markers, based on the + // lumps which follow it. Store the result in the 'levels' vector. + // The test here is rather lax, since wads exist with a non-standard + // ordering of level lumps. + size_t part_mask = 0; + size_t part_count = 0; + + // Ignore non-header map lumps + // Fixes sliding window bug on single-level WADs + if (WhatLevelPart(read_directory[k].name) != 0) + { + return false; + } - // UDMF level? - if (StringCaseCmp(read_directory[lump_index + 1].name, "TEXTMAP")) + // check for UDMF levels + if (StringCaseCmp(read_directory[k + 1].name, "TEXTMAP")) { - while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps()) + if (HAS_BIT(config.debug, DEBUG_WAD)) { - if (StringCaseCmp(read_directory[lump_index + count].name, "ENDMAP")) - { - count++; - break; - } - - count++; + PrintLine(LOG_DEBUG, "[%s] Detected level : %s (UDMF)", __func__, read_directory[k].name); } + return true; } - else // standard DOOM or HEXEN format + + // check whether the next four lumps are level lumps + for (size_t i = 1; i <= 4; i++) { - while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps() - && IsLevelLump(read_directory[lump_index + count].name)) + if (k + i >= NumLumps()) { - count++; + break; } - } - return lump_index + count - 1; -} + size_t part = WhatLevelPart(read_directory[k + i].name); -// returns a lump index, NO_INDEX if not found -size_t WadIO::LevelLookupLump(size_t lump_index, const char *name) -{ - size_t finish = this->LevelLastLump(lump_index); + if (part == 0) + { + break; + } - for (size_t k = lump_index + 1; k <= finish; k++) - { - SYS_ASSERT(k < this->NumLumps()); + // do not allow duplicates + if (part_mask & (1 << part)) + { + break; + } - if (StringCaseCmp(read_directory[k].name, name) == 0) + part_mask |= (1 << part); + part_count++; + } + + if (part_count == 4) + { + if (HAS_BIT(config.debug, DEBUG_WAD)) { - return k; + PrintLine(LOG_DEBUG, "[%s] Detected level : %s", __func__, read_directory[k].name); } + return true; } - return NO_INDEX; // not found + return false; } map_format_e WadIO::LevelFormat(size_t lump_index) @@ -193,28 +248,52 @@ map_format_e WadIO::LevelFormat(size_t lump_index) return MapFormat_Doom; } -template -std::vector WadIO::ReadLump(size_t index) +size_t WadIO::LevelLastLump(size_t lump_index) { - std::vector lump; - if (index >= this->read_header.num_entries) + size_t count = 1; + + // UDMF level? + if (lump_index + 1 < this->NumLumps() && StringCaseCmp(read_directory[lump_index + 1].name, "TEXTMAP")) { - return lump; + while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps()) + { + if (StringCaseCmp(read_directory[lump_index + count].name, "ENDMAP")) + { + count++; + break; + } + + count++; + } } - if (fseek(this->read_file, this->read_directory[index].pos, SEEK_SET)) + else // standard DOOM or HEXEN format { - throw std::runtime_error("ERROR: Failed to seek"); + while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps() + && IsLevelLump(read_directory[lump_index + count].name)) + { + count++; + } } - size_t size = this->read_directory[index].size / sizeof(T); - lump.resize(size); - this->SafeRead(lump.data(), size * sizeof(T)); - return lump; + + return lump_index + count - 1; } -template -std::vector WadIO::ReadMapLump(level_t &level, const char *name) +// returns a lump index, NO_INDEX if not found +size_t WadIO::LevelLookupLump(size_t lump_index, const char *name) { - return ReadLump(this->LevelLookupLump(level.level_header_lump_index, name)); + size_t finish = this->LevelLastLump(lump_index); + + for (size_t k = lump_index + 1; k <= finish; k++) + { + SYS_ASSERT(k < this->NumLumps()); + + if (StringCaseCmp(read_directory[k].name, name) == 0) + { + return k; + } + } + + return NO_INDEX; // not found } // @@ -274,35 +353,35 @@ void WadIO::AddToLump(const void *data, size_t len) WadIO &WadIO::operator<<(uint8_t value) { - AddToLump(&value, sizeof(uint8_t)); + AddToLumpZ(&value, sizeof(uint8_t)); return *this; } WadIO &WadIO::operator<<(uint16_t value) { value = GetLittleEndian(value); - AddToLump(reinterpret_cast(&value), sizeof(uint16_t)); + AddToLumpZ(reinterpret_cast(&value), sizeof(uint16_t)); return *this; } WadIO &WadIO::operator<<(uint32_t value) { value = GetLittleEndian(value); - AddToLump(reinterpret_cast(&value), sizeof(uint32_t)); + AddToLumpZ(reinterpret_cast(&value), sizeof(uint32_t)); return *this; } WadIO &WadIO::operator<<(int16_t value) { value = GetLittleEndian(value); - AddToLump(reinterpret_cast(&value), sizeof(int16_t)); + AddToLumpZ(reinterpret_cast(&value), sizeof(int16_t)); return *this; } WadIO &WadIO::operator<<(fixed_t value) { value = GetLittleEndian(value); - AddToLump(reinterpret_cast(&value), sizeof(fixed_t)); + AddToLumpZ(reinterpret_cast(&value), sizeof(fixed_t)); return *this; } diff --git a/src/wad.hpp b/src/wad.hpp index 2d59a09..4bbeedd 100644 --- a/src/wad.hpp +++ b/src/wad.hpp @@ -23,6 +23,7 @@ #pragma once #include "core.hpp" +#include //------------------------------------------------------------------------ // WAD STRUCTURES @@ -67,21 +68,21 @@ struct WadIO byte zout_buffer[8192]; // Init/die - WadIO(const char *filename); + WadIO(std::string in_file, std::string out_file); ~WadIO(void); // Read operations void SafeRead(void *buffer, size_t size); size_t NumLumps(void) const; - size_t LevelCount(void) const; + bool IsLevel(size_t k); + map_format_t LevelFormat(size_t lump_index); const char *LumpName(size_t lump_index) const; size_t LevelLastLump(size_t lump_index); size_t LevelLookupLump(size_t lump_index, const char *name); - map_format_t LevelFormat(size_t lump_index); template - std::vector ReadLump(size_t index); + std::vector ReadLump(size_t lump_index); template - std::vector ReadMapLump(level_t &level, const char *name); + std::vector ReadMapLump(size_t lump_index, const char *name); // Write operations void SafeWrite(const void *buffer, size_t size); @@ -103,3 +104,33 @@ struct WadIO void AddToLumpZ(const void *data, size_t length); void Finish_Zlib(void); }; + +// +// fuck C++ start +// +template +std::vector WadIO::ReadLump(size_t lump_index) +{ + std::vector lump; + if (lump_index >= this->read_header.num_entries) + { + return lump; + } + if (fseek(this->read_file, this->read_directory[lump_index].pos, SEEK_SET)) + { + throw std::runtime_error("ERROR: Failed to seek"); + } + size_t size = this->read_directory[lump_index].size / sizeof(T); + lump.resize(size); + this->SafeRead(lump.data(), size * sizeof(T)); + return lump; +} + +template +std::vector WadIO::ReadMapLump(size_t lump_index, const char *name) +{ + return ReadLump(this->LevelLookupLump(lump_index, name)); +} +// +// fuck C++ end +// From 95f2906ee5a25c46ac3c8ce804e712532e78a37d Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Wed, 20 May 2026 22:56:36 -0300 Subject: [PATCH 04/10] :/ twasn't signedness --- src/wad.cpp | 34 ++++++++++++++-------------------- src/wad.hpp | 2 +- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/wad.cpp b/src/wad.cpp index 43d47d6..543ab47 100644 --- a/src/wad.cpp +++ b/src/wad.cpp @@ -21,7 +21,6 @@ #include "wad.hpp" #include "core.hpp" -#include "local.hpp" #include #include @@ -82,18 +81,18 @@ WadIO::WadIO(std::string in_file, std::string out_file) throw std::runtime_error("ERROR: Input file is not a wad"); } - this->read_header.num_entries = GetLittleEndian(this->read_header.num_entries); - this->read_header.dir_start = GetLittleEndian(this->read_header.dir_start); + auto num_entries = GetLittleEndian(this->read_header.num_entries); + auto dir_start = GetLittleEndian(this->read_header.dir_start); - if (fseek(this->read_file, this->read_header.dir_start, SEEK_SET)) + if (fseek(this->read_file, dir_start, SEEK_SET)) { throw std::runtime_error("ERROR: Could not read wad directory"); } - this->read_directory.resize(this->read_header.num_entries); - this->SafeRead(this->read_directory.data(), this->read_header.num_entries * sizeof(WadLump)); + this->read_directory.resize(num_entries); + this->SafeRead(this->read_directory.data(), this->read_directory.size() * sizeof(WadLump)); - for (size_t i = 0; i < this->read_header.num_entries; ++i) + for (size_t i = 0; i < this->read_directory.size(); ++i) { this->read_directory[i].pos = GetLittleEndian(this->read_directory[i].pos); this->read_directory[i].size = GetLittleEndian(this->read_directory[i].size); @@ -123,22 +122,17 @@ WadIO::~WadIO(void) this->read_directory.clear(); // Output stuff - if (this->write_file) - { - // Send entire directory, right after - this->SafeWrite(this->write_directory.data(), sizeof(WadLump) * this->write_directory.size()); + this->SafeWrite(this->write_directory.data(), sizeof(WadLump) * this->write_directory.size()); - // Skip file magic header and write directory data - uint32_t num_entries = GetLittleEndian(IndexToInt(this->write_directory.size())); - uint32_t dir_start = GetLittleEndian(static_cast(ftell(this->write_file))); + uint32_t num_entries = GetLittleEndian(IndexToInt(this->write_directory.size())); + uint32_t dir_start = GetLittleEndian(static_cast(ftell(this->write_file))); - fseek(this->write_file, sizeof(char[4]), SEEK_SET); - this->SafeWrite(&num_entries, sizeof(num_entries)); - this->SafeWrite(&dir_start, sizeof(dir_start)); - fclose(this->write_file); + fseek(this->write_file, sizeof(char[4]), SEEK_SET); + this->SafeWrite(&num_entries, sizeof(num_entries)); + this->SafeWrite(&dir_start, sizeof(dir_start)); + fclose(this->write_file); - this->write_file = NULL; - } + this->write_file = NULL; } // call only after seeking diff --git a/src/wad.hpp b/src/wad.hpp index 4bbeedd..d59f789 100644 --- a/src/wad.hpp +++ b/src/wad.hpp @@ -112,7 +112,7 @@ template std::vector WadIO::ReadLump(size_t lump_index) { std::vector lump; - if (lump_index >= this->read_header.num_entries) + if (lump_index >= this->read_directory.size()) { return lump; } From db2062db450a208f115e7d6d68233d6b1ba49c85 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Thu, 21 May 2026 18:50:56 -0300 Subject: [PATCH 05/10] reading loading and node building working! time to write... --- src/bsp.cpp | 151 +++++++++++++++++++-------------------------- src/bsp.hpp | 4 +- src/core.hpp | 40 +++--------- src/info.cpp | 14 ++--- src/info.hpp | 7 ++- src/level.cpp | 4 +- src/local.hpp | 22 ++++--- src/main.cpp | 168 +++++++++++++++++++++++++++++++------------------- src/wad.cpp | 165 +++++++++++++++++++++++++------------------------ src/wad.hpp | 21 ++++--- 10 files changed, 303 insertions(+), 293 deletions(-) diff --git a/src/bsp.cpp b/src/bsp.cpp index e5a520d..786b88f 100644 --- a/src/bsp.cpp +++ b/src/bsp.cpp @@ -102,9 +102,9 @@ static inline short_angle_t VanillaSegAngle(const seg_t *seg) return result; } -void PutVertices_Integral(WadIO &io, level_t &level, bool only_old) +void PutVertices_Integral(WadIO &io, level_t &level, bool include_new_verts) { - const size_t vert_count = only_old ? level.num_old_vert : level.vertices.size(); + const size_t vert_count = include_new_verts ? level.vertices.size() : level.num_old_vert; io.StartWritingLump("VERTEXES"); for (size_t i = 0; i < vert_count; i++) @@ -119,9 +119,9 @@ void PutVertices_Integral(WadIO &io, level_t &level, bool only_old) } } -void PutVertices_Fractional(WadIO &io, level_t &level, bool only_old) +void PutVertices_Fractional(WadIO &io, level_t &level, bool include_new_verts) { - const size_t vert_count = only_old ? level.num_old_vert : level.vertices.size(); + const size_t vert_count = include_new_verts ? level.vertices.size() : level.num_old_vert; io.StartWritingLump("VERTEXES"); for (size_t i = 0; i < vert_count; i++) @@ -144,12 +144,10 @@ void PutSegs_DoomBSP(WadIO &io, level_t &level) { io.StartWritingLump("SEGS"); - for (size_t i = 0; i < level.segs.size(); i++) + for (auto seg : level.segs) { raw_seg_doombsp_t raw; - const seg_t *seg = level.segs[i]; - raw.start = GetLittleEndian(IndexToShort(seg->start->index)); raw.end = GetLittleEndian(IndexToShort(seg->end->index)); raw.angle = GetLittleEndian(VanillaSegAngle(seg)); @@ -172,12 +170,10 @@ void PutSubsecs_DoomBSP(WadIO &io, level_t &level) { io.StartWritingLump("SSECTORS"); - for (size_t i = 0; i < level.subsecs.size(); i++) + for (auto sub : level.subsecs) { raw_subsec_doombsp_t raw; - const subsec_t *sub = level.subsecs[i]; - raw.first = GetLittleEndian(IndexToShort(sub->seg_list->index)); raw.num = GetLittleEndian(IndexToShort(sub->seg_count)); @@ -185,8 +181,8 @@ void PutSubsecs_DoomBSP(WadIO &io, level_t &level) if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] %zu First %04X Num %04X", __func__, sub->index, GetLittleEndian(raw.first), - GetLittleEndian(raw.num)); + PrintLine(LOG_DEBUG, "[%s] %zu First %04X Num %04X Mid (%1.1f,%1.1f)", __func__, sub->index, GetLittleEndian(raw.first), + GetLittleEndian(raw.num), sub->mid_x, sub->mid_y); } } } @@ -278,9 +274,8 @@ void PutLeafs_DoomBSP(WadIO &io, level_t &level) io.StartWritingLump("LEAFS"); uint16_t actual_seg_index = 0; - for (size_t i = 0; i < level.subsecs.size(); i++) + for (auto subsec : level.subsecs) { - subsec_t *subsec = level.subsecs[i]; seg_t *seg = subsec->seg_list; uint16_t seg_count = IndexToShort(subsec->seg_count); @@ -288,7 +283,7 @@ void PutLeafs_DoomBSP(WadIO &io, level_t &level) if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %hu", __func__, i, seg_count); + PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %hu", __func__, subsec->index, seg_count); } for (size_t j = 0; j < seg_count; j++) @@ -329,12 +324,10 @@ void PutSegs_DeePBSPV4(WadIO &io, level_t &level) { io.StartWritingLump("SEGS"); - for (size_t i = 0; i < level.segs.size(); i++) + for (auto seg : level.segs) { raw_seg_deepbspv4_t raw; - const seg_t *seg = level.segs[i]; - raw.start = GetLittleEndian(IndexToInt(seg->start->index)); raw.end = GetLittleEndian(IndexToInt(seg->end->index)); raw.angle = GetLittleEndian(VanillaSegAngle(seg)); @@ -357,12 +350,10 @@ void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level) { io.StartWritingLump("SSECTORS"); - for (size_t i = 0; i < level.subsecs.size(); i++) + for (auto sub : level.subsecs) { raw_subsec_deepbspv4_t raw; - const subsec_t *sub = level.subsecs[i]; - raw.first = GetLittleEndian(IndexToInt(sub->seg_list->index)); raw.num = GetLittleEndian(IndexToShort(sub->seg_count)); @@ -370,8 +361,8 @@ void PutSubsecs_DeePBSPV4(WadIO &io, level_t &level) if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] %zu First %08X Num %04X", __func__, sub->index, GetLittleEndian(raw.first), - GetLittleEndian(raw.num)); + PrintLine(LOG_DEBUG, "[%s] %zu First %08X Num %04X Mid (%1.1f,%1.1f)", __func__, sub->index, GetLittleEndian(raw.first), + GetLittleEndian(raw.num), sub->mid_x, sub->mid_y); } } } @@ -463,9 +454,8 @@ void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) io.StartWritingLump("LEAFS"); uint32_t actual_seg_index = 0; - for (size_t i = 0; i < level.subsecs.size(); i++) + for (auto subsec : level.subsecs) { - subsec_t *subsec = level.subsecs[i]; seg_t *seg = subsec->seg_list; uint32_t seg_count = IndexToInt(subsec->seg_count); @@ -473,7 +463,7 @@ void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %u", __func__, i, seg_count); + PrintLine(LOG_DEBUG, "[%s] Subsector[%zu] leaf references: %u", __func__, subsec->index, seg_count); } for (size_t j = 0; j < seg_count; j++) @@ -510,7 +500,7 @@ void PutLeafs_DeePBSPV4(WadIO &io, level_t &level) // ZDoom format -- XNOD // -static void PutVertices_Xnod(WadIO &io, level_t &level) +static void PutVertices_XNOD(WadIO &io, level_t &level) { uint32_t orgverts = GetLittleEndian(IndexToInt(level.num_old_vert)); uint32_t newverts = GetLittleEndian(IndexToInt(level.num_new_vert)); @@ -519,12 +509,10 @@ static void PutVertices_Xnod(WadIO &io, level_t &level) io.AddToLumpZ(&newverts, sizeof(newverts)); size_t count = 0; - for (size_t i = 0; i < level.vertices.size(); i++) + for (auto vert : level.vertices) { raw_vertex_xnod_t raw; - const vertex_t *vert = level.vertices[i]; - if (!vert->is_new) { continue; @@ -544,16 +532,14 @@ static void PutVertices_Xnod(WadIO &io, level_t &level) } } -static void PutSubsecs_Xnod(WadIO &io, level_t &level) +static void PutSubsecs_XNOD(WadIO &io, level_t &level) { uint32_t raw_num = GetLittleEndian(IndexToInt(level.subsecs.size())); io.AddToLumpZ(&raw_num, sizeof(raw_num)); size_t cur_seg_index = 0; - for (size_t i = 0; i < level.subsecs.size(); i++) + for (auto sub : level.subsecs) { - const subsec_t *sub = level.subsecs[i]; - raw_subsec_xnod_t raw; raw.segnum = GetLittleEndian(IndexToInt(sub->seg_count)); io.AddToLumpZ(&raw, sizeof(raw)); @@ -564,7 +550,8 @@ static void PutSubsecs_Xnod(WadIO &io, level_t &level) { if (cur_seg_index != seg->index) { - PrintLine(LOG_ERROR, "ERROR: PutZSubsecs: seg index mismatch in sub %zu (%zu != %zu)", i, cur_seg_index, seg->index); + PrintLine(LOG_ERROR, "ERROR: PutZSubsecs: seg index mismatch in sub %zu (%zu != %zu)", sub->index, cur_seg_index, + seg->index); } count++; @@ -572,7 +559,7 @@ static void PutSubsecs_Xnod(WadIO &io, level_t &level) if (count != sub->seg_count) { - PrintLine(LOG_ERROR, "ERROR: PutZSubsecs: miscounted segs in sub %zu (%zu != %zu)", i, count, sub->seg_count); + PrintLine(LOG_ERROR, "ERROR: PutZSubsecs: miscounted segs in sub %zu (%zu != %zu)", sub->index, count, sub->seg_count); } } @@ -582,21 +569,14 @@ static void PutSubsecs_Xnod(WadIO &io, level_t &level) } } -static void PutSegs_Xnod(WadIO &io, level_t &level) +static void PutSegs_XNOD(WadIO &io, level_t &level) { uint32_t raw_num = GetLittleEndian(IndexToInt(level.segs.size())); io.AddToLumpZ(&raw_num, sizeof(raw_num)); - for (size_t i = 0; i < level.segs.size(); i++) + for (auto seg : level.segs) { - const seg_t *seg = level.segs[i]; - - if (seg->index != i) - { - PrintLine(LOG_ERROR, "ERROR: PutZSegs: seg index mismatch (%zu != %zu)", seg->index, i); - } - - raw_seg_xnod_t raw = {}; + raw_seg_xnod_t raw; raw.start = GetLittleEndian(IndexToInt(seg->start->index)); raw.end = GetLittleEndian(IndexToInt(seg->end->index)); @@ -604,19 +584,26 @@ static void PutSegs_Xnod(WadIO &io, level_t &level) raw.side = seg->side; io.AddToLumpZ(&raw, sizeof(raw)); + + if (HAS_BIT(config.debug, DEBUG_BSP)) + { + PrintLine(LOG_DEBUG, "[%s] %zu Vert %04X->%04X Line %04X %s (%1.1f,%1.1f) -> (%1.1f,%1.1f)", __func__, seg->index, + GetLittleEndian(raw.start), GetLittleEndian(raw.end), GetLittleEndian(raw.linedef), raw.side ? "L" : "R", + seg->start->x, seg->start->y, seg->end->x, seg->end->y); + } } } -static void PutOneNode_Xnod(WadIO &io, node_t *node, size_t &node_cur_index) +static void PutOneNode_XNOD(WadIO &io, node_t *node, size_t &node_cur_index) { if (node->r.node) { - PutOneNode_Xnod(io, node->r.node, node_cur_index); + PutOneNode_XNOD(io, node->r.node, node_cur_index); } if (node->l.node) { - PutOneNode_Xnod(io, node->l.node, node_cur_index); + PutOneNode_XNOD(io, node->l.node, node_cur_index); } node->index = node_cur_index++; @@ -673,7 +660,7 @@ static void PutOneNode_Xnod(WadIO &io, node_t *node, size_t &node_cur_index) } } -static void PutNodes_Xnod(WadIO &io, level_t &level) +static void PutNodes_XNOD(WadIO &io, level_t &level) { size_t node_cur_index = 0; uint32_t raw_num = GetLittleEndian(IndexToInt(level.nodes.size())); @@ -681,7 +668,7 @@ static void PutNodes_Xnod(WadIO &io, level_t &level) if (level.root_node) { - PutOneNode_Xnod(io, level.root_node, node_cur_index); + PutOneNode_XNOD(io, level.root_node, node_cur_index); } if (node_cur_index != level.nodes.size()) @@ -694,21 +681,14 @@ static void PutNodes_Xnod(WadIO &io, level_t &level) // ZDoom format -- XGLN, XGL2, XGL3 // -static void PutSegs_Xgln(WadIO &io, level_t &level) +static void PutSegs_XGLN(WadIO &io, level_t &level) { uint32_t raw_num = GetLittleEndian(IndexToInt(level.segs.size())); io.AddToLumpZ(&raw_num, sizeof(raw_num)); - for (size_t i = 0; i < level.segs.size(); i++) + for (auto seg : level.segs) { - const seg_t *seg = level.segs[i]; - - if (seg->index != i) - { - PrintLine(LOG_ERROR, "ERROR: PutXGL3Segs: seg index mismatch (%zu != %zu)", seg->index, i); - } - - raw_seg_xgln_t raw = {}; + raw_seg_xgln_t raw; raw.vertex = GetLittleEndian(IndexToInt(seg->start->index)); raw.partner = GetLittleEndian(IndexToInt(seg->partner ? seg->partner->index : NO_INDEX)); @@ -719,27 +699,21 @@ static void PutSegs_Xgln(WadIO &io, level_t &level) if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] SEG[%zu] v1=%d partner=%d line=%d side=%d", __func__, i, raw.vertex, raw.partner, raw.linedef, - raw.side); + PrintLine(LOG_DEBUG, "[%s] %zu Vert %04X Line %04X %s (%1.1f,%1.1f) -> (%1.1f,%1.1f)", __func__, seg->index, + GetLittleEndian(raw.vertex), GetLittleEndian(raw.linedef), raw.side ? "L" : "R", seg->start->x, seg->start->y, + seg->end->x, seg->end->y); } } } -static void PutSegs_Xgl2(WadIO &io, level_t &level) +static void PutSegs_XGL2(WadIO &io, level_t &level) { uint32_t raw_num = GetLittleEndian(IndexToInt(level.segs.size())); io.AddToLumpZ(&raw_num, sizeof(raw_num)); - for (size_t i = 0; i < level.segs.size(); i++) + for (auto seg : level.segs) { - const seg_t *seg = level.segs[i]; - - if (seg->index != i) - { - PrintLine(LOG_ERROR, "ERROR: PutXGL3Segs: seg index mismatch (%zu != %zu)", seg->index, i); - } - - raw_seg_xgl2_t raw = {}; + raw_seg_xgl2_t raw; raw.vertex = GetLittleEndian(IndexToInt(seg->start->index)); raw.partner = GetLittleEndian(IndexToInt(seg->partner ? seg->partner->index : NO_INDEX)); @@ -750,22 +724,23 @@ static void PutSegs_Xgl2(WadIO &io, level_t &level) if (HAS_BIT(config.debug, DEBUG_BSP)) { - PrintLine(LOG_DEBUG, "[%s] SEG[%zu] v1=%d partner=%d line=%d side=%d", __func__, i, raw.vertex, raw.partner, raw.linedef, - raw.side); + PrintLine(LOG_DEBUG, "[%s] %zu Vert %04X Line %04X %s (%1.1f,%1.1f) -> (%1.1f,%1.1f)", __func__, seg->index, + GetLittleEndian(raw.vertex), GetLittleEndian(raw.linedef), raw.side ? "L" : "R", seg->start->x, seg->start->y, + seg->end->x, seg->end->y); } } } -static void PutOneNode_Xgl3(WadIO &io, node_t *node, size_t &node_cur_index) +static void PutOneNode_XGL3(WadIO &io, node_t *node, size_t &node_cur_index) { if (node->r.node) { - PutOneNode_Xgl3(io, node->r.node, node_cur_index); + PutOneNode_XGL3(io, node->r.node, node_cur_index); } if (node->l.node) { - PutOneNode_Xgl3(io, node->l.node, node_cur_index); + PutOneNode_XGL3(io, node->l.node, node_cur_index); } node->index = node_cur_index++; @@ -822,7 +797,7 @@ static void PutOneNode_Xgl3(WadIO &io, node_t *node, size_t &node_cur_index) } } -static void PutNodes_Xgl3(WadIO &io, level_t &level) +static void PutNodes_XGL3(WadIO &io, level_t &level) { size_t node_cur_index = 0; uint32_t raw_num = GetLittleEndian(IndexToInt(level.nodes.size())); @@ -830,7 +805,7 @@ static void PutNodes_Xgl3(WadIO &io, level_t &level) if (level.root_node) { - PutOneNode_Xgl3(io, level.root_node, node_cur_index); + PutOneNode_XGL3(io, level.root_node, node_cur_index); } if (node_cur_index != level.nodes.size()) @@ -844,8 +819,6 @@ static void PutNodes_Xgl3(WadIO &io, level_t &level) // void PutTree_ZDBSP(WadIO &io, level_t &level, std::string lumpname, bsp_format_t bsp_format, bool compress) { - auto mark = Benchmarker(__func__); - if (bsp_format == BSP_XNOD) NormaliseBspTree(level); SortSegs(level); @@ -859,15 +832,15 @@ void PutTree_ZDBSP(WadIO &io, level_t &level, std::string lumpname, bsp_format_t if (compress) io.Begin_Zlib(); - PutVertices_Xnod(io, level); - PutSubsecs_Xnod(io, level); + PutVertices_XNOD(io, level); + PutSubsecs_XNOD(io, level); - if (bsp_format == BSP_XNOD) PutSegs_Xnod(io, level); - if (bsp_format == BSP_XGLN) PutSegs_Xgln(io, level); - if (bsp_format == BSP_XGL2 || bsp_format == BSP_XGL3) PutSegs_Xgl2(io, level); + if (bsp_format == BSP_XNOD) PutSegs_XNOD(io, level); + if (bsp_format == BSP_XGLN) PutSegs_XGLN(io, level); + if (bsp_format == BSP_XGL2 || bsp_format == BSP_XGL3) PutSegs_XGL2(io, level); - if (bsp_format == BSP_XNOD || bsp_format == BSP_XGLN || bsp_format == BSP_XGL2) PutNodes_Xnod(io, level); - if (bsp_format == BSP_XGL3) PutNodes_Xgl3(io, level); + if (bsp_format == BSP_XNOD || bsp_format == BSP_XGLN || bsp_format == BSP_XGL2) PutNodes_XNOD(io, level); + if (bsp_format == BSP_XGL3) PutNodes_XGL3(io, level); if (compress) io.Finish_Zlib(); } diff --git a/src/bsp.hpp b/src/bsp.hpp index faf4ff3..2cf49e6 100644 --- a/src/bsp.hpp +++ b/src/bsp.hpp @@ -199,8 +199,8 @@ void PutReject(level_t &level); // some format overlap between them void SortSegs(level_t &level); -void PutVertices_Integral(WadIO &io, level_t &level, bool only_old); -void PutVertices_Fractional(WadIO &io, level_t &level, bool only_old); +void PutVertices_Integral(WadIO &io, level_t &level, bool include_new_verts); +void PutVertices_Fractional(WadIO &io, level_t &level, bool include_new_verts); void PutSegs_DoomBSP(WadIO &io, level_t &level); void PutSubsecs_DoomBSP(WadIO &io, level_t &level); diff --git a/src/core.hpp b/src/core.hpp index f8e0ad8..652b138 100644 --- a/src/core.hpp +++ b/src/core.hpp @@ -477,9 +477,9 @@ inline bool HasExtension(const char *filename) // // Return offset of the '.', or NO_INDEX when no extension was found. // -inline size_t FindExtension(const char *filename) +inline size_t FindExtension(std::string_view filename) { - const size_t len = strlen(filename); + const size_t len = strlen(filename.data()); if (len == 0) { return NO_INDEX; @@ -539,13 +539,14 @@ inline short_angle_t ComputeAngle_BAM(double dx, double dy) using map_format_t = enum map_format_e { MapFormat_INVALID = 0, - MapFormat_Doom, MapFormat_Hexen, MapFormat_Doom64, MapFormat_UDMF, }; +static std::string_view format_name[] = {"ERROR", "Doom Format", "Hexen Format", "Doom64 Format", "UDMF"}; + // Lump order in a map WAD: each map needs a couple of lumps // to provide a complete scene geometry description. using lump_order_t = enum lump_order_e @@ -788,8 +789,7 @@ using raw_blockmap_xbm1_header_t = struct raw_blockmap_xbm1_header_s } PACKEDATTR; // Fail way earlier -#define static_size(x, y) \ - static_assert(sizeof(x) == y, "Size mismatch for '" #x "'. Should be " #y ".") +#define static_size(x, y) static_assert(sizeof(x) == y, "Size mismatch for '" #x "'. Should be " #y ".") static_size(raw_vertex_short_t, 4); static_size(raw_vertex_fixed_t, 8); @@ -1118,30 +1118,6 @@ constexpr const char PRINT_HELP[] = "\n" "Map names should be full, like E1M3 or MAP24, but a list\n" "and/or ranges can be specified: MAP01,MAP04-MAP07,MAP12\n"; -using build_result_t = enum build_result_e -{ - // everything went peachy keen - BUILD_OK = 0, - - // when saving the map, one or more lumps overflowed - BUILD_LumpOverflow -}; - -// attempt to open a wad. on failure, the FatalError method in the -// buildinfo_t interface is called. -void OpenWad(const char *filename); - -// close a previously opened wad. -void CloseWad(void); - -// give the number of levels detected in the wad. -size_t LevelsInWad(void); - -// build the nodes of a particular level. otherwise the wad -// is updated to store the new lumps and returns either BUILD_OK or -// BUILD_LumpOverflow if some limits were exceeded. -build_result_e BuildLevel(struct level_t &level, const char *filename); - // // Benchmark // @@ -1150,10 +1126,10 @@ struct Benchmarker { using clock = std::chrono::steady_clock; clock::time_point start; - const char *name; + std::string_view name; bool enabled; - Benchmarker(const char *_name, bool _enabled = true) + Benchmarker(std::string_view _name, bool _enabled = true) { if (!_enabled) return; enabled = _enabled; @@ -1166,6 +1142,6 @@ struct Benchmarker if (!enabled) return; auto end = clock::now(); auto time = std::chrono::duration(end - start); - PrintLine(LOG_NORMAL, "[Benchmarker] '%s' runtime: %.2f ms", name, time.count()); + PrintLine(LOG_NORMAL, "[Benchmarker] '%s' runtime: %.2f ms", name.data(), time.count()); }; }; diff --git a/src/info.cpp b/src/info.cpp index 9e3a210..a2bf666 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -28,15 +28,15 @@ static std::vector analysis_csv; //------------------------------------------------------------------------ -static void FileClear(const char *filename) +static void FileClear(std::string_view filename) { - if (FILE *fp = fopen(filename, "w")) + if (FILE *fp = fopen(filename.data(), "w")) { fclose(fp); } } -void SetupAnalysisFile(const char *filepath) +void SetupAnalysisFile(std::string_view filepath) { auto csv_path = std::string(filepath); @@ -56,7 +56,7 @@ void SetupAnalysisFile(const char *filepath) // writes out for current file // expects AnalysisPushLine to have been called with all 0-32 split costs during node-building -void WriteAnalysis(const char *filename) +void WriteAnalysis(std::string_view filename) { auto mark = Benchmarker(__func__); auto csv_path = std::string(filename); @@ -118,7 +118,7 @@ static void ComputeTotalBspHeights(const node_t *node, size_t depth, double &lea } } -void GenerateAnalysis(WadIO &io, level_t &level, const char *filename) +void GenerateAnalysis(WadIO &io, level_t &level, std::string_view filename) { auto mark = Benchmarker(__func__); auto generate_analysis_data = [](WadIO &io, level_t &level, bool is_fast, size_t split_cost) -> auto @@ -181,7 +181,7 @@ void GenerateAnalysis(WadIO &io, level_t &level, const char *filename) data.worst_case_ratio = min_epl / max_epl; data.tree_quality = ((min_epl / total_depth_sum) - data.worst_case_ratio) / (1 - data.worst_case_ratio); - std::string line = std::format("{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", level.GetLevelName(io), is_fast, + std::string line = std::format("{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}", io.LumpName(level.lump), is_fast, split_cost, data.vertex, data.lines, data.sides, data.sectors, data.bsp_vertex, data.nodes, data.subsecs, data.segs, data.splits, data.left_depth, data.right_depth, data.average_depth, data.optimal_depth, data.tree_balance, data.worst_case_ratio, data.tree_quality); @@ -201,7 +201,7 @@ void GenerateAnalysis(WadIO &io, level_t &level, const char *filename) for (size_t split_cost = 1; split_cost <= 32; split_cost++) { generate_analysis_data(io, level, is_fast != 0, split_cost); - PrintLine(LOG_NORMAL, "[%s] Analyzed %s, %s mode, split cost factor of %zu", __func__, level.GetLevelName(io), + PrintLine(LOG_NORMAL, "[%s] Analyzed %s, %s mode, split cost factor of %zu", __func__, io.LumpName(level.lump), is_fast ? "fast" : "normal", split_cost); } } diff --git a/src/info.hpp b/src/info.hpp index 696a4ec..658405d 100644 --- a/src/info.hpp +++ b/src/info.hpp @@ -20,8 +20,9 @@ #pragma once +#include "local.hpp" #include "wad.hpp" -void SetupAnalysisFile(const char *filepath); -void GenerateAnalysis(WadIO &io, level_t &level, const char *filename); -void WriteAnalysis(const char *filename); +void SetupAnalysisFile(std::string_view filepath); +void GenerateAnalysis(WadIO &io, level_t &level, std::string_view filename); +void WriteAnalysis(std::string_view filename); diff --git a/src/level.cpp b/src/level.cpp index 366067c..f87c851 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1172,7 +1172,7 @@ size_t ComputeBspHeight(const node_t *node) /* ----- build nodes for a single level ----- */ -build_result_e BuildLevel(WadIO &io, level_t &level, const char *filename) +build_result_e BuildLevel(WadIO &io, level_t &level, std::string_view filename) { LoadLevel(io, level); @@ -1182,7 +1182,7 @@ build_result_e BuildLevel(WadIO &io, level_t &level, const char *filename) { if (config.analysis) { - PrintLine(LOG_NORMAL, "[%s] Starting analysis loop for %s", __func__, level.GetLevelName(io)); + PrintLine(LOG_NORMAL, "[%s] Starting analysis loop for %s", __func__, io.LumpName(level.lump)); GenerateAnalysis(io, level, filename); } diff --git a/src/local.hpp b/src/local.hpp index 6e3ab5d..1acfaa1 100644 --- a/src/local.hpp +++ b/src/local.hpp @@ -39,6 +39,7 @@ bool CheckLinedefInsideBox(int xmin, int ymin, int xmax, int ymax, int x1, int y // LEVEL : Level structures & read/write functions. //------------------------------------------------------------------------ +struct level_t; struct node_t; struct sector_t; struct quadtree_c; @@ -426,12 +427,6 @@ using level_t = struct level_t double block_compression; bmap_format_t bmap_format = bmap_format_t::BMAP_DoomBSP; - - inline const char *GetLevelName(WadIO &io) - { - return io.read_directory[lump].name; - } - vertex_t *SafeLookupVertex(size_t num, size_t num_line) { if (num >= vertices.size()) @@ -494,7 +489,6 @@ void FreeNodes(level_t &level); void FreeWallTips(level_t &level); void FreeIntersections(level_t &level); - //------------------------------------------------------------------------ // ANALYZE : Analyzing level structures //------------------------------------------------------------------------ @@ -557,3 +551,17 @@ void BuildNodes(level_t &level, seg_t *seg_list, int depth, bbox_t *bounds, node // compute the height of the bsp tree, starting at 'node'. size_t ComputeBspHeight(const node_t *node); + +using build_result_t = enum build_result_e +{ + // everything went peachy keen + BUILD_OK = 0, + + // when saving the map, one or more lumps overflowed + BUILD_LumpOverflow +}; + +// build the nodes of a particular level. otherwise the wad +// is updated to store the new lumps and returns either BUILD_OK or +// BUILD_LumpOverflow if some limits were exceeded. +build_result_e BuildLevel(WadIO &io, level_t &level, std::string_view filename); diff --git a/src/main.cpp b/src/main.cpp index d88e624..8b92486 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,18 +23,20 @@ #include "core.hpp" #include "info.hpp" +#include "local.hpp" #include "wad.hpp" #include #include +#include #include static bool opt_help = false; static bool opt_version = false; -static std::string opt_output; +static std::string_view opt_output; -static std::vector wad_list; +static std::vector wad_list; static size_t total_failed_files = 0; static size_t total_empty_files = 0; @@ -57,10 +59,10 @@ buildinfo_t config; // FILE MANAGEMENT //------------------------------------------------------------------------ -static bool FileExists(const char *filename) +static bool FileExists(std::string filename) { - if (FILE *fp = fopen(filename, "rb")) + if (FILE *fp = fopen(filename.c_str(), "rb")) { fclose(fp); return true; @@ -69,19 +71,19 @@ static bool FileExists(const char *filename) return false; } -static bool CheckMapInRange(const map_range_t *range, const char *name) +static bool CheckMapInRange(const map_range_t *range, std::string name) { - if (strlen(name) != range->low.size()) + if (name.size() != range->low.size()) { return false; } - if (strcmp(name, range->low.c_str()) < 0) + if (name < range->low) { return false; } - if (strcmp(name, range->high.c_str()) > 0) + if (name > range->high) { return false; } @@ -89,7 +91,7 @@ static bool CheckMapInRange(const map_range_t *range, const char *name) return true; } -static bool CheckMapInMapList(const char *name) +static bool CheckMapInMapList(std::string name) { // when --map is not used, allow everything if (map_list.empty()) @@ -108,7 +110,7 @@ static bool CheckMapInMapList(const char *name) return false; } -static void VisitFile(const char *filename) +static void VisitFile(std::string_view filename) { auto mark = Benchmarker(__func__); auto in_file = std::string(filename); @@ -130,25 +132,67 @@ static void VisitFile(const char *filename) if (config.analysis) { - SetupAnalysisFile(filename); + SetupAnalysisFile(in_file); } - PrintLine(LOG_NORMAL, "Building %s", filename); + PrintLine(LOG_NORMAL, "Building %s", in_file.c_str()); { + config.total_warnings = 0; auto wad = WadIO(in_file, out_file); size_t current = 0; size_t max = wad.NumLumps(); + size_t levels_visited = 0; + size_t levels_failed = 0; + while (current < max) { - if (wad.IsLevel(current)) + if (wad.IsLevel(current) && CheckMapInMapList(wad.LumpName(current))) { - PrintLine(LOG_NORMAL, "Processing level %zu:%s", current, wad.LumpName(current)); + level_t level; + level.lump = current; + level.map_format = wad.LevelFormat(current); + + levels_visited += 1; + auto result = BuildLevel(wad, level, in_file); + + // handle a failed map (due to lump overflow) + if (result == BUILD_LumpOverflow) + { + levels_failed += 1; + } + else + { + total_built_maps += 1; + } + + current = wad.LevelLastLump(current) + 1; } // TODO: process glbsp - wad.CopyLump(current); - current++; + else + { + wad.CopyLump(current); + current++; + } + } + + if (levels_visited == 0) + { + PrintLine(LOG_NORMAL, "No matching levels found"); + total_empty_files += 1; } + + total_failed_maps += levels_failed; + + if (levels_failed > 0) + { + PrintLine(LOG_NORMAL, "Failed maps: %zu (out of %zu)", levels_failed, levels_visited); + + // allow building other files + total_failed_files += 1; + } + + PrintLine(LOG_NORMAL, "Serious warnings: %zu", config.total_warnings); } if (is_same_file) @@ -350,61 +394,61 @@ static void ParseShortArgument(const char *arg) } } -static bool ProcessDebugParam(const char *param, uint32_t &debug) +static bool ProcessDebugParam(std::string param, uint32_t &debug) { - if (strcmp(param, "--debug-blockmap") == 0) + if (param == "--debug-blockmap") { debug |= DEBUG_BLOCKMAP; } - else if (strcmp(param, "--debug-reject") == 0) + else if (param == "--debug-reject") { debug |= DEBUG_REJECT; } - else if (strcmp(param, "--debug-load") == 0) + else if (param == "--debug-load") { debug |= DEBUG_LOAD; } - else if (strcmp(param, "--debug-bsp") == 0) + else if (param == "--debug-bsp") { debug |= DEBUG_BSP; } - else if (strcmp(param, "--debug-walltips") == 0) + else if (param == "--debug-walltips") { debug |= DEBUG_WALLTIPS; } - else if (strcmp(param, "--debug-polyobj") == 0) + else if (param == "--debug-polyobj") { debug |= DEBUG_POLYOBJ; } - else if (strcmp(param, "--debug-overlaps") == 0) + else if (param == "--debug-overlaps") { debug |= DEBUG_OVERLAPS; } - else if (strcmp(param, "--debug-picknode") == 0) + else if (param == "--debug-picknode") { debug |= DEBUG_PICKNODE; } - else if (strcmp(param, "--debug-split") == 0) + else if (param == "--debug-split") { debug |= DEBUG_SPLIT; } - else if (strcmp(param, "--debug-cutlist") == 0) + else if (param == "--debug-cutlist") { debug |= DEBUG_CUTLIST; } - else if (strcmp(param, "--debug-builder") == 0) + else if (param == "--debug-builder") { debug |= DEBUG_BUILDER; } - else if (strcmp(param, "--debug-sorter") == 0) + else if (param == "--debug-sorter") { debug |= DEBUG_SORTER; } - else if (strcmp(param, "--debug-subsec") == 0) + else if (param == "--debug-subsec") { debug |= DEBUG_SUBSEC; } - else if (strcmp(param, "--debug-wad") == 0) + else if (param == "--debug-wad") { debug |= DEBUG_WAD; } @@ -412,39 +456,39 @@ static bool ProcessDebugParam(const char *param, uint32_t &debug) return debug != 0; } -static int32_t ParseLongArgument(const char *name, const int32_t argc, const char *argv[]) +static int32_t ParseLongArgument(std::string name, const int32_t argc, const char *argv[]) { int32_t used = 0; - if (strcmp(name, "--help") == 0) + if (name == "--help") { opt_help = true; } - else if (strcmp(name, "--version") == 0) + else if (name == "--version") { opt_version = true; } - else if (strcmp(name, "--analysis") == 0) + else if (name == "--analysis") { config.analysis = true; } - else if (strcmp(name, "--verbose") == 0) + else if (name == "--verbose") { config.verbose = true; } - else if (strcmp(name, "--fast") == 0) + else if (name == "--fast") { config.fast = true; } - else if (strcmp(name, "--no-effects") == 0) + else if (name == "--no-effects") { config.effects = false; } - else if (strcmp(name, "--compress") == 0) + else if (name == "--compress") { config.compress = true; } - else if (strcmp(name, "--map") == 0 || strcmp(name, "--maps") == 0) + else if (name == "--map" || name == "--maps") { if (argc < 1 || argv[0][0] == '-') { @@ -455,7 +499,7 @@ static int32_t ParseLongArgument(const char *name, const int32_t argc, const cha used = 1; } - else if (strcmp(name, "--type") == 0) + else if (name == "--type") { if (argc < 1 || !isdigit(argv[0][0])) { @@ -472,7 +516,7 @@ static int32_t ParseLongArgument(const char *name, const int32_t argc, const cha config.bsp_format = static_cast(val); used = 1; } - else if (strcmp(name, "--bmap") == 0) + else if (name == "--bmap") { if (argc < 1 || !isdigit(argv[0][0])) { @@ -489,7 +533,7 @@ static int32_t ParseLongArgument(const char *name, const int32_t argc, const cha config.bmap_format = static_cast(val); used = 1; } - else if (strcmp(name, "--cost") == 0) + else if (name == "--cost") { if (argc < 1 || !isdigit(argv[0][0])) { @@ -506,14 +550,14 @@ static int32_t ParseLongArgument(const char *name, const int32_t argc, const cha config.split_cost = val; used = 1; } - else if (strcmp(name, "--polyobj") == 0) + else if (name == "--polyobj") { config.polyobj.anchor = Hexen_PolyObj_Anchor; config.polyobj.spawn = Hexen_PolyObj_Spawn; config.polyobj.spawn_crush = Hexen_PolyObj_SpawnCrush; config.polyobj.spawn_hurt = Hexen_PolyObj_SpawnHurt; } - else if (strcmp(name, "--output") == 0) + else if (name == "--output") { // this option is *only* for compatibility @@ -530,16 +574,16 @@ static int32_t ParseLongArgument(const char *name, const int32_t argc, const cha opt_output = argv[0]; used = 1; } - else if (strncmp(name, "--debug-", 8) == 0) + else if (name.starts_with("--debug-")) { - if (!ProcessDebugParam(name, config.debug)) + if (!ProcessDebugParam(name.c_str(), config.debug)) { - PrintLine(LOG_ERROR, "ERROR: unknown debug parameter '%s'", name); + PrintLine(LOG_ERROR, "ERROR: unknown debug parameter '%s'", name.c_str()); } } else { - PrintLine(LOG_ERROR, "ERROR: unknown long option: '%s'", name); + PrintLine(LOG_ERROR, "ERROR: unknown long option: '%s'", name.c_str()); } return used; @@ -555,19 +599,19 @@ void ParseCommandLine(int32_t argc, const char *argv[]) while (argc > 0) { - const char *arg = *argv++; + std::string arg = *argv++; argc--; if constexpr (MACOS) { // ignore Mac OS X garbage - if (strncmp(arg, "-psn_", 5) == 0) + if (strncmp(arg.data(), "-psn_", 5) == 0) { continue; } } - if (strlen(arg) == 0) + if (arg.empty()) { continue; } @@ -579,38 +623,38 @@ void ParseCommandLine(int32_t argc, const char *argv[]) continue; } - if (strcmp(arg, "-") == 0) + if (arg == "-") { PrintLine(LOG_ERROR, "ERROR: illegal option '-'"); } - if (strcmp(arg, "--") == 0) + if (arg == "--") { rest_are_files = true; continue; } // handle short args which are isolate and require a value - if (strcmp(arg, "-t") == 0) + if (arg == "-t") { arg = "--type"; } - if (strcmp(arg, "-c") == 0) + if (arg == "-c") { arg = "--cost"; } - if (strcmp(arg, "-m") == 0) + if (arg == "-m") { arg = "--map"; } - if (strcmp(arg, "-o") == 0) + if (arg == "-o") { arg = "--output"; } if (arg[1] != '-') { - ParseShortArgument(arg); + ParseShortArgument(arg.c_str()); continue; } @@ -655,18 +699,18 @@ int32_t main(const int32_t argc, const char *argv[]) PrintLine(LOG_ERROR, "ERROR: cannot use multiple input files with --output"); } - if (StringCaseCmp(wad_list[0], opt_output.c_str()) == 0) + if (wad_list[0] == opt_output) { PrintLine(LOG_ERROR, "ERROR: input and output files are the same"); } } // validate all filenames before processing any of them - for (const auto filename : wad_list) + for (const auto &filename : wad_list) { if (!FileExists(filename)) { - throw std::runtime_error("ERROR: no such file: " + std::string(filename)); + throw std::runtime_error("ERROR: no such file: " + filename); } } diff --git a/src/wad.cpp b/src/wad.cpp index 543ab47..4d3eac7 100644 --- a/src/wad.cpp +++ b/src/wad.cpp @@ -29,33 +29,28 @@ // Utilities //------------------------------------------------------------------------ -static size_t WhatLevelPart(const char *name) -{ - if (StringCaseCmp(name, "THINGS") == 0) return 1; - if (StringCaseCmp(name, "LINEDEFS") == 0) return 2; - if (StringCaseCmp(name, "SIDEDEFS") == 0) return 3; - if (StringCaseCmp(name, "VERTEXES") == 0) return 4; - if (StringCaseCmp(name, "SECTORS") == 0) return 5; +constexpr uint32_t HAS_THINGS = BIT(0); +constexpr uint32_t HAS_LINEDEFS = BIT(1); +constexpr uint32_t HAS_SIDEDEFS = BIT(2); +constexpr uint32_t HAS_VERTEXES = BIT(3); +constexpr uint32_t HAS_SECTORS = BIT(4); - return 0; -} +constexpr uint32_t HAS_REQUIRED_LUMPS = HAS_THINGS | HAS_LINEDEFS | HAS_SIDEDEFS | HAS_VERTEXES | HAS_SECTORS; -static bool IsLevelLump(const char *name) +static uint32_t WhatLevelPart(const std::string_view name) { - if (StringCaseCmp(name, "SEGS") == 0) return true; - if (StringCaseCmp(name, "SSECTORS") == 0) return true; - if (StringCaseCmp(name, "NODES") == 0) return true; - if (StringCaseCmp(name, "REJECT") == 0) return true; - if (StringCaseCmp(name, "BLOCKMAP") == 0) return true; - if (StringCaseCmp(name, "BEHAVIOR") == 0) return true; - if (StringCaseCmp(name, "SCRIPTS") == 0) return true; - if (StringCaseCmp(name, "LEAFS") == 0) return true; - if (StringCaseCmp(name, "LIGHTS") == 0) return true; - if (StringCaseCmp(name, "MACROS") == 0) return true; + if (name == "THINGS") return HAS_THINGS; + if (name == "LINEDEFS") return HAS_LINEDEFS; + if (name == "SIDEDEFS") return HAS_SIDEDEFS; + if (name == "VERTEXES") return HAS_VERTEXES; + if (name == "SECTORS") return HAS_SECTORS; - return WhatLevelPart(name) != 0; + return 0; } +static const std::string_view TEXTMAP = "TEXTMAP"; +static const std::string_view ENDMAP = "ENDMAP"; + //------------------------------------------------------------------------ // WAD Reading Interface //------------------------------------------------------------------------ @@ -90,9 +85,9 @@ WadIO::WadIO(std::string in_file, std::string out_file) } this->read_directory.resize(num_entries); - this->SafeRead(this->read_directory.data(), this->read_directory.size() * sizeof(WadLump)); + this->SafeRead(this->read_directory.data(), this->NumLumps() * sizeof(WadLump)); - for (size_t i = 0; i < this->read_directory.size(); ++i) + for (size_t i = 0; i < this->NumLumps(); ++i) { this->read_directory[i].pos = GetLittleEndian(this->read_directory[i].pos); this->read_directory[i].size = GetLittleEndian(this->read_directory[i].size); @@ -122,11 +117,11 @@ WadIO::~WadIO(void) this->read_directory.clear(); // Output stuff - this->SafeWrite(this->write_directory.data(), sizeof(WadLump) * this->write_directory.size()); - uint32_t num_entries = GetLittleEndian(IndexToInt(this->write_directory.size())); uint32_t dir_start = GetLittleEndian(static_cast(ftell(this->write_file))); + this->SafeWrite(this->write_directory.data(), sizeof(WadLump) * this->write_directory.size()); + fseek(this->write_file, sizeof(char[4]), SEEK_SET); this->SafeWrite(&num_entries, sizeof(num_entries)); this->SafeWrite(&dir_start, sizeof(dir_start)); @@ -151,80 +146,88 @@ size_t WadIO::NumLumps(void) const const char *WadIO::LumpName(size_t lump_index) const { + SYS_ASSERT(lump_index < this->NumLumps()); static char name[9]; strncpy(name, this->read_directory[lump_index].name, 8); name[8] = 0; return name; } -bool WadIO::IsLevel(size_t k) +bool WadIO::IsLevelLump(size_t lump_index) const +{ + SYS_ASSERT(lump_index < this->NumLumps()); + const std::string_view name = this->LumpName(lump_index); + if (name == "SEGS") return true; + if (name == "SSECTORS") return true; + if (name == "NODES") return true; + if (name == "REJECT") return true; + if (name == "BLOCKMAP") return true; + if (name == "BEHAVIOR") return true; + if (name == "SCRIPTS") return true; + if (name == "LEAFS") return true; + if (name == "LIGHTS") return true; + if (name == "MACROS") return true; + + return WhatLevelPart(name) != 0; +} + +bool WadIO::IsLevelTextmapFormat(size_t lump_index) const { - // Determine what lumps in the wad are level markers, based on the - // lumps which follow it. Store the result in the 'levels' vector. - // The test here is rather lax, since wads exist with a non-standard - // ordering of level lumps. - size_t part_mask = 0; - size_t part_count = 0; - - // Ignore non-header map lumps - // Fixes sliding window bug on single-level WADs - if (WhatLevelPart(read_directory[k].name) != 0) + if (lump_index + LL_TEXTMAP >= this->NumLumps()) { return false; } + return (TEXTMAP == this->LumpName(lump_index + LL_TEXTMAP)); +} + +bool WadIO::IsLevel(size_t lump_index) const +{ + SYS_ASSERT(lump_index < this->NumLumps()); + // check for UDMF levels - if (StringCaseCmp(read_directory[k + 1].name, "TEXTMAP")) + if (this->IsLevelTextmapFormat(lump_index)) { if (HAS_BIT(config.debug, DEBUG_WAD)) { - PrintLine(LOG_DEBUG, "[%s] Detected level : %s (UDMF)", __func__, read_directory[k].name); + PrintLine(LOG_DEBUG, "[%s] Detected level: %s (UDMF) [Index: %zu]", __func__, this->LumpName(lump_index), lump_index); } return true; } - // check whether the next four lumps are level lumps - for (size_t i = 1; i <= 4; i++) + uint32_t required_lumps = 0; + for (size_t i = 1; i < MAX_LUMPS_IN_A_LEVEL; ++i) { - if (k + i >= NumLumps()) - { - break; - } - - size_t part = WhatLevelPart(read_directory[k + i].name); - - if (part == 0) - { - break; - } - - // do not allow duplicates - if (part_mask & (1 << part)) - { - break; - } - - part_mask |= (1 << part); - part_count++; + // Haven't found all required lumps yet? Out + if (lump_index + i >= this->NumLumps()) break; + // Not map lump? Also out + if (!this->IsLevelLump(lump_index + i)) break; + // Check if this lump is a required level part + required_lumps |= WhatLevelPart(this->LumpName(lump_index + i)); } - if (part_count == 4) + // Have found all required lumps? + if (HAS_ALL(required_lumps, HAS_REQUIRED_LUMPS)) { if (HAS_BIT(config.debug, DEBUG_WAD)) { - PrintLine(LOG_DEBUG, "[%s] Detected level : %s", __func__, read_directory[k].name); + map_format_t format = this->LevelFormat(lump_index); + PrintLine(LOG_DEBUG, "[%s] Detected level: %s (%s) [Index: %zu]", __func__, this->LumpName(lump_index), + format_name[format].data(), lump_index); } return true; } - - return false; + else + { + return false; + } } -map_format_e WadIO::LevelFormat(size_t lump_index) +map_format_e WadIO::LevelFormat(size_t lump_index) const { // UDMF maps can contain BEHAVIOR or MACROS // check exclusively TEXTMAP - if (this->LevelLookupLump(lump_index, "TEXTMAP") != NO_INDEX) + if (this->IsLevelTextmapFormat(lump_index)) { return MapFormat_UDMF; } @@ -242,16 +245,17 @@ map_format_e WadIO::LevelFormat(size_t lump_index) return MapFormat_Doom; } -size_t WadIO::LevelLastLump(size_t lump_index) +size_t WadIO::LevelLastLump(size_t lump_index) const { size_t count = 1; // UDMF level? - if (lump_index + 1 < this->NumLumps() && StringCaseCmp(read_directory[lump_index + 1].name, "TEXTMAP")) + if (this->IsLevelTextmapFormat(lump_index)) { - while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps()) + while (count < MAX_LUMPS_IN_A_LEVEL // + && lump_index + count < this->NumLumps()) // { - if (StringCaseCmp(read_directory[lump_index + count].name, "ENDMAP")) + if (ENDMAP == this->LumpName(lump_index + count)) { count++; break; @@ -260,10 +264,11 @@ size_t WadIO::LevelLastLump(size_t lump_index) count++; } } - else // standard DOOM or HEXEN format + else // all other binary formats { - while (count < MAX_LUMPS_IN_A_LEVEL && lump_index + count < this->NumLumps() - && IsLevelLump(read_directory[lump_index + count].name)) + while (count < MAX_LUMPS_IN_A_LEVEL // + && lump_index + count < this->NumLumps() // + && IsLevelLump(lump_index + count)) // { count++; } @@ -273,7 +278,7 @@ size_t WadIO::LevelLastLump(size_t lump_index) } // returns a lump index, NO_INDEX if not found -size_t WadIO::LevelLookupLump(size_t lump_index, const char *name) +size_t WadIO::LevelLookupLump(size_t lump_index, std::string_view name) const { size_t finish = this->LevelLastLump(lump_index); @@ -281,7 +286,7 @@ size_t WadIO::LevelLookupLump(size_t lump_index, const char *name) { SYS_ASSERT(k < this->NumLumps()); - if (StringCaseCmp(read_directory[k].name, name) == 0) + if (name == this->LumpName(k)) { return k; } @@ -304,19 +309,19 @@ void WadIO::SafeWrite(const void *buffer, size_t size) } } -void WadIO::CreateEmptyLump(const char *name) +void WadIO::CreateEmptyLump(std::string_view name) { WadLump lump; - strncpy(lump.name, name, 8); + strncpy(lump.name, name.data(), 8); lump.pos = GetLittleEndian(static_cast(ftell(this->write_file))); lump.size = 0; this->write_directory.push_back(lump); } -void WadIO::WriteLump(const char *name, const void *data, size_t size) +void WadIO::WriteLump(std::string_view name, const void *data, size_t size) { WadLump lump; - strncpy(lump.name, name, 8); + strncpy(lump.name, name.data(), 8); lump.pos = GetLittleEndian(static_cast(ftell(this->write_file))); lump.size = IndexToInt(size); this->write_directory.push_back(lump); @@ -333,7 +338,7 @@ void WadIO::CopyLump(size_t index) // Partial writing // -void WadIO::StartWritingLump(const char *name) +void WadIO::StartWritingLump(std::string_view name) { CreateEmptyLump(name); } diff --git a/src/wad.hpp b/src/wad.hpp index d59f789..83b6f4e 100644 --- a/src/wad.hpp +++ b/src/wad.hpp @@ -74,24 +74,26 @@ struct WadIO // Read operations void SafeRead(void *buffer, size_t size); size_t NumLumps(void) const; - bool IsLevel(size_t k); - map_format_t LevelFormat(size_t lump_index); const char *LumpName(size_t lump_index) const; - size_t LevelLastLump(size_t lump_index); - size_t LevelLookupLump(size_t lump_index, const char *name); + bool IsLevelLump(size_t lump_index) const; + bool IsLevelTextmapFormat(size_t lump_index) const; + bool IsLevel(size_t lump_index) const; + map_format_t LevelFormat(size_t lump_index) const; + size_t LevelLastLump(size_t lump_index) const; + size_t LevelLookupLump(size_t lump_index, std::string_view name) const; template std::vector ReadLump(size_t lump_index); template - std::vector ReadMapLump(size_t lump_index, const char *name); + std::vector ReadMapLump(size_t lump_index, std::string_view name); // Write operations void SafeWrite(const void *buffer, size_t size); - void CreateEmptyLump(const char *name); - void WriteLump(const char *name, const void *data, size_t size); + void CreateEmptyLump(std::string_view name); + void WriteLump(std::string_view name, const void *data, size_t size); void CopyLump(size_t lump); // Partial writing - void StartWritingLump(const char *name); + void StartWritingLump(std::string_view name); void AddToLump(const void *data, size_t len); WadIO &operator<<(uint8_t value); WadIO &operator<<(uint16_t value); @@ -127,10 +129,11 @@ std::vector WadIO::ReadLump(size_t lump_index) } template -std::vector WadIO::ReadMapLump(size_t lump_index, const char *name) +std::vector WadIO::ReadMapLump(size_t lump_index, std::string_view name) { return ReadLump(this->LevelLookupLump(lump_index, name)); } + // // fuck C++ end // From 3b637d052bd2094ea24fdb480521672670051d65 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Thu, 21 May 2026 23:53:55 -0300 Subject: [PATCH 06/10] its getting there! --- src/bsp.cpp | 3 +- src/bsp.hpp | 4 +-- src/level.cpp | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/local.hpp | 1 - 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/bsp.cpp b/src/bsp.cpp index 786b88f..7b240c3 100644 --- a/src/bsp.cpp +++ b/src/bsp.cpp @@ -435,8 +435,9 @@ static void PutOneNode_DeePBSPV4(WadIO &io, node_t *node, size_t &node_cur_index void PutNodes_DeePBSPV4(WadIO &io, level_t &level) { - io.StartWritingLump("NODES"); size_t node_cur_index = 0; + io.StartWritingLump("NODES"); + io.AddToLump("xNd4\0\0\0\0", 8); if (level.root_node != nullptr) { diff --git a/src/bsp.hpp b/src/bsp.hpp index 2cf49e6..52b2586 100644 --- a/src/bsp.hpp +++ b/src/bsp.hpp @@ -191,8 +191,8 @@ void RoundOffBspTree(level_t &level); // both BLOCKAMP and REJECT exist as single lumps on all supported map formats void InitBlockmap(level_t &level); -void PutBlockmap(level_t &level); -void PutReject(level_t &level); +void PutBlockmap(WadIO &io, level_t &level); +void PutReject(WadIO &io, level_t &level); // the BSP tree lumps differ notably on each map format -- Doom/Hexen/Doom64 // have NODES, SSECTORS & SEGS, but UDMF is generally only ZNODES, and there's diff --git a/src/level.cpp b/src/level.cpp index f87c851..e159ca9 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1027,7 +1027,7 @@ void ParseUDMF(WadIO &io, level_t &level) // Check Limits // -static void CheckBinaryFormatLimits(WadIO &io, level_t &level) +static void CheckBinaryFormatLimits(level_t &level) { if (level.num_old_vert > LIMIT_VERT) { @@ -1077,7 +1077,7 @@ static bsp_format_t CheckFormatBSP(buildinfo_t &ctx, level_t &level) level_type = BSP_DeePBSPV4; } - return level_type; + return level.bsp_format = level_type; } /* ----- whole-level routines --------------------------- */ @@ -1151,7 +1151,75 @@ void LoadLevel(WadIO &io, level_t &level) } } -// TODO: map output goes here +//------------------------------------------------------------------------ +// Writing stuff +//------------------------------------------------------------------------ + +build_result_t SaveLevelDoom(WadIO &io, level_t &level) +{ + // Normal binary format limits + CheckBinaryFormatLimits(level); + + // On BSP overflow, bump to DeePBSPV4 from DoomBSP + bsp_format_t bsp_format = CheckFormatBSP(config, level); + + // Use Zlib-compressed version of ZDBSP lump formats + bool bsp_compress = config.compress; + + // Copy marker wholesale + size_t lump = level.lump; + io.CopyLump(lump); + + // Correct order is paramount + io.CopyLump(io.LevelLookupLump(lump, "THINGS")); + io.CopyLump(io.LevelLookupLump(lump, "LINEDEFS")); + io.CopyLump(io.LevelLookupLump(lump, "SIDEDEFS")); + + if (bsp_format == BSP_DoomBSP) + { + NormaliseBspTree(level); + RoundOffBspTree(level); + SortSegs(level); + + PutVertices_Integral(io, level, true); + PutSegs_DoomBSP(io, level); + PutSubsecs_DoomBSP(io, level); + PutNodes_DoomBSP(io, level); + } + else if (bsp_format == BSP_DeePBSPV4) + { + NormaliseBspTree(level); + RoundOffBspTree(level); + SortSegs(level); + + PutVertices_Integral(io, level, true); + PutSegs_DeePBSPV4(io, level); + PutSubsecs_DeePBSPV4(io, level); + PutNodes_DeePBSPV4(io, level); + } + else if (bsp_format == BSP_XNOD) + { + // ZDBSP formats do not write to VERTEXES + PutVertices_Integral(io, level, false); + io.CreateEmptyLump("SEGS"); + io.CreateEmptyLump("SSECTORS"); + PutTree_ZDBSP(io, level, "NODES", bsp_format, bsp_compress); + } + else if (bsp_format >= BSP_XGLN && bsp_format <= BSP_XGL3) + { + // ZDBSP formats do not write to VERTEXES + PutVertices_Integral(io, level, false); + io.CreateEmptyLump("SEGS"); + PutTree_ZDBSP(io, level, "SSECTORS", bsp_format, bsp_compress); + io.CreateEmptyLump("NODES"); + } + + io.CopyLump(io.LevelLookupLump(lump, "SECTORS")); + PutReject(io, level); + PutBlockmap(io, level); + + return level.overflows ? BUILD_LumpOverflow : BUILD_OK; +} //------------------------------------------------------------------------ // MAIN STUFF @@ -1212,6 +1280,8 @@ build_result_e BuildLevel(WadIO &io, level_t &level, std::string_view filename) switch (level.map_format) { case MapFormat_Doom: + ret = SaveLevelDoom(io, level); + break; case MapFormat_Hexen: case MapFormat_Doom64: // ret = SaveLevelBinaryFormat(io, level); diff --git a/src/local.hpp b/src/local.hpp index 1acfaa1..ae991f6 100644 --- a/src/local.hpp +++ b/src/local.hpp @@ -411,7 +411,6 @@ using level_t = struct level_t std::vector walltips; std::vector intercuts; bsp_format_t bsp_format = bsp_format_t::BSP_XNOD; - bool bsp_compress = false; uint8_t *reject_matrix; size_t reject_size; From d8fc8fb9b00f741ae2fe47e0fc80c2e6f61b59f9 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Fri, 22 May 2026 02:25:39 -0300 Subject: [PATCH 07/10] yaaaaaaaaaaaaaay --- src/bsp.cpp | 39 ++++++++++-------------- src/bsp.hpp | 3 +- src/level.cpp | 82 +++++++++++++++++++++++++++++++++++++++++++++------ src/wad.cpp | 14 +++++++-- src/wad.hpp | 1 + 5 files changed, 101 insertions(+), 38 deletions(-) diff --git a/src/bsp.cpp b/src/bsp.cpp index 7b240c3..0e6a892 100644 --- a/src/bsp.cpp +++ b/src/bsp.cpp @@ -102,7 +102,7 @@ static inline short_angle_t VanillaSegAngle(const seg_t *seg) return result; } -void PutVertices_Integral(WadIO &io, level_t &level, bool include_new_verts) +void PutVertices(WadIO &io, level_t &level, bool fractional, bool include_new_verts) { const size_t vert_count = include_new_verts ? level.vertices.size() : level.num_old_vert; io.StartWritingLump("VERTEXES"); @@ -110,29 +110,20 @@ void PutVertices_Integral(WadIO &io, level_t &level, bool include_new_verts) for (size_t i = 0; i < vert_count; i++) { const vertex_t *vert = level.vertices[i]; - - raw_vertex_short_t raw; - raw.x = GetLittleEndian(FloatToShort(floor(vert->x))); - raw.y = GetLittleEndian(FloatToShort(floor(vert->y))); - - io.AddToLump(&raw, sizeof(raw)); - } -} - -void PutVertices_Fractional(WadIO &io, level_t &level, bool include_new_verts) -{ - const size_t vert_count = include_new_verts ? level.vertices.size() : level.num_old_vert; - io.StartWritingLump("VERTEXES"); - - for (size_t i = 0; i < vert_count; i++) - { - const vertex_t *vert = level.vertices[i]; - - raw_vertex_fixed_t raw; - raw.x = GetLittleEndian(FloatToFixed(vert->x)); - raw.y = GetLittleEndian(FloatToFixed(vert->y)); - - io.AddToLump(&raw, sizeof(raw)); + if (fractional) + { + raw_vertex_fixed_t raw; + raw.x = GetLittleEndian(FloatToFixed(vert->x)); + raw.y = GetLittleEndian(FloatToFixed(vert->y)); + io.AddToLump(&raw, sizeof(raw)); + } + else + { + raw_vertex_short_t raw; + raw.x = GetLittleEndian(FloatToShort(floor(vert->x))); + raw.y = GetLittleEndian(FloatToShort(floor(vert->y))); + io.AddToLump(&raw, sizeof(raw)); + } } } diff --git a/src/bsp.hpp b/src/bsp.hpp index 52b2586..3de86f1 100644 --- a/src/bsp.hpp +++ b/src/bsp.hpp @@ -199,8 +199,7 @@ void PutReject(WadIO &io, level_t &level); // some format overlap between them void SortSegs(level_t &level); -void PutVertices_Integral(WadIO &io, level_t &level, bool include_new_verts); -void PutVertices_Fractional(WadIO &io, level_t &level, bool include_new_verts); +void PutVertices(WadIO &io, level_t &level, bool fractional, bool include_new_verts); void PutSegs_DoomBSP(WadIO &io, level_t &level); void PutSubsecs_DoomBSP(WadIO &io, level_t &level); diff --git a/src/level.cpp b/src/level.cpp index e159ca9..bdb8473 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1155,14 +1155,25 @@ void LoadLevel(WadIO &io, level_t &level) // Writing stuff //------------------------------------------------------------------------ -build_result_t SaveLevelDoom(WadIO &io, level_t &level) +build_result_t SaveLevelBinary(WadIO &io, level_t &level) { + auto mark = Benchmarker(__func__); + // Normal binary format limits CheckBinaryFormatLimits(level); + // Use fractional VERTEXES lump in some map formats + bool fractional = level.map_format == MapFormat_Doom64; + + // Generate LEAFS lump + bool do_leafs = level.map_format == MapFormat_Doom64; + // On BSP overflow, bump to DeePBSPV4 from DoomBSP bsp_format_t bsp_format = CheckFormatBSP(config, level); + // Only include BSP-generated vertices in non-ZDBSP lump format + bool include_new = bsp_format >= BSP_DoomBSP || bsp_format <= BSP_DeePBSPV4; + // Use Zlib-compressed version of ZDBSP lump formats bool bsp_compress = config.compress; @@ -1175,13 +1186,14 @@ build_result_t SaveLevelDoom(WadIO &io, level_t &level) io.CopyLump(io.LevelLookupLump(lump, "LINEDEFS")); io.CopyLump(io.LevelLookupLump(lump, "SIDEDEFS")); + // Do the four BSP lumps, before promptly doing the other normal lumps if (bsp_format == BSP_DoomBSP) { NormaliseBspTree(level); RoundOffBspTree(level); SortSegs(level); - PutVertices_Integral(io, level, true); + PutVertices(io, level, fractional, include_new); PutSegs_DoomBSP(io, level); PutSubsecs_DoomBSP(io, level); PutNodes_DoomBSP(io, level); @@ -1192,7 +1204,7 @@ build_result_t SaveLevelDoom(WadIO &io, level_t &level) RoundOffBspTree(level); SortSegs(level); - PutVertices_Integral(io, level, true); + PutVertices(io, level, fractional, include_new); PutSegs_DeePBSPV4(io, level); PutSubsecs_DeePBSPV4(io, level); PutNodes_DeePBSPV4(io, level); @@ -1200,7 +1212,7 @@ build_result_t SaveLevelDoom(WadIO &io, level_t &level) else if (bsp_format == BSP_XNOD) { // ZDBSP formats do not write to VERTEXES - PutVertices_Integral(io, level, false); + PutVertices(io, level, fractional, include_new); io.CreateEmptyLump("SEGS"); io.CreateEmptyLump("SSECTORS"); PutTree_ZDBSP(io, level, "NODES", bsp_format, bsp_compress); @@ -1208,19 +1220,72 @@ build_result_t SaveLevelDoom(WadIO &io, level_t &level) else if (bsp_format >= BSP_XGLN && bsp_format <= BSP_XGL3) { // ZDBSP formats do not write to VERTEXES - PutVertices_Integral(io, level, false); + PutVertices(io, level, fractional, include_new); io.CreateEmptyLump("SEGS"); PutTree_ZDBSP(io, level, "SSECTORS", bsp_format, bsp_compress); io.CreateEmptyLump("NODES"); } + // Why is SECTORS right here? T_T io.CopyLump(io.LevelLookupLump(lump, "SECTORS")); + + // Bruh PutReject(io, level); PutBlockmap(io, level); + // The better? convex subsector option + if (do_leafs && bsp_format == BSP_DoomBSP) + { + PutLeafs_DoomBSP(io, level); + } + else if (do_leafs && bsp_format == BSP_DeePBSPV4) + { + PutLeafs_DeePBSPV4(io, level); + } + + // Level scripts and stuff + if (level.map_format == MapFormat_Hexen) + { + io.CopyLump(io.LevelLookupLump(lump, "BEHAVIOR")); + io.CopyLump(io.LevelLookupLump(lump, "SCRIPTS")); + } + else if (level.map_format == MapFormat_Doom64) + { + io.CopyLump(io.LevelLookupLump(lump, "LIGHTS")); + io.CopyLump(io.LevelLookupLump(lump, "MACROS")); + io.CopyLump(io.LevelLookupLump(lump, "SCRIPTS")); + } + return level.overflows ? BUILD_LumpOverflow : BUILD_OK; } + +build_result_t SaveLevelTextMap(WadIO &io, level_t &level) +{ + auto mark = Benchmarker(__func__); + + // Copy marker wholesale + size_t lump = level.lump; + io.CopyLump(lump); + + // Always use ZGL3 for UDMF maps + PutTree_ZDBSP(io, level, "ZNODES", BSP_XGL3, true); + PutReject(io, level); + PutBlockmap(io, level); + + // Copy everything else we don't touch + io.CopyLump(io.LevelLookupLump(lump, "BEHAVIOR")); + io.CopyLump(io.LevelLookupLump(lump, "MACROS")); + io.CopyLump(io.LevelLookupLump(lump, "SCRIPTS")); + io.CopyLump(io.LevelLookupLump(lump, "DIALOGUE")); + io.CopyLump(io.LevelLookupLump(lump, "LIGHTMAP")); + + // Finish + io.CreateEmptyLump("ENDMAP"); + + return BUILD_OK; +} + //------------------------------------------------------------------------ // MAIN STUFF //------------------------------------------------------------------------ @@ -1280,14 +1345,13 @@ build_result_e BuildLevel(WadIO &io, level_t &level, std::string_view filename) switch (level.map_format) { case MapFormat_Doom: - ret = SaveLevelDoom(io, level); - break; case MapFormat_Hexen: case MapFormat_Doom64: - // ret = SaveLevelBinaryFormat(io, level); + ret = SaveLevelBinary(io, level); + break; break; case MapFormat_UDMF: - // ret = SaveLevelTextMap(io, level); + ret = SaveLevelTextMap(io, level); break; default: break; diff --git a/src/wad.cpp b/src/wad.cpp index 4d3eac7..7ad1fe0 100644 --- a/src/wad.cpp +++ b/src/wad.cpp @@ -232,12 +232,12 @@ map_format_e WadIO::LevelFormat(size_t lump_index) const return MapFormat_UDMF; } - if (this->LevelLookupLump(lump_index, "BEHAVIOR") != NO_INDEX) + if (this->LevelHasLump(lump_index, "BEHAVIOR")) { return MapFormat_Hexen; } - if (this->LevelLookupLump(lump_index, "LIGHTS") != NO_INDEX && this->LevelLookupLump(lump_index, "MACROS") != NO_INDEX) + if (this->LevelHasLump(lump_index, "LIGHTS") && this->LevelHasLump(lump_index, "MACROS")) { return MapFormat_Doom64; } @@ -295,6 +295,11 @@ size_t WadIO::LevelLookupLump(size_t lump_index, std::string_view name) const return NO_INDEX; // not found } +bool WadIO::LevelHasLump(size_t lump_index, std::string_view name) const +{ + return this->LevelLookupLump(lump_index, name) != NO_INDEX; +} + // // Write operations // @@ -331,7 +336,10 @@ void WadIO::WriteLump(std::string_view name, const void *data, size_t size) void WadIO::CopyLump(size_t index) { auto lump = this->ReadLump(index); - WriteLump(this->LumpName(index), lump.data(), lump.size()); + if (index < this->NumLumps()) + { + WriteLump(this->LumpName(index), lump.data(), lump.size()); + } } // diff --git a/src/wad.hpp b/src/wad.hpp index 83b6f4e..af4c3ef 100644 --- a/src/wad.hpp +++ b/src/wad.hpp @@ -81,6 +81,7 @@ struct WadIO map_format_t LevelFormat(size_t lump_index) const; size_t LevelLastLump(size_t lump_index) const; size_t LevelLookupLump(size_t lump_index, std::string_view name) const; + bool LevelHasLump(size_t lump_index, std::string_view name) const; template std::vector ReadLump(size_t lump_index); template From 598bb69dba6964df4f3d0073bdaff2f03ca9acbf Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Fri, 22 May 2026 19:09:28 -0300 Subject: [PATCH 08/10] finishing touches --- src/bsp.hpp | 4 --- src/level.cpp | 72 +++++++++++++++++++++++++-------------------------- src/local.hpp | 28 +++----------------- src/node.cpp | 28 ++++++++++---------- src/wad.cpp | 19 +++++++++----- src/wad.hpp | 5 ++-- 6 files changed, 69 insertions(+), 87 deletions(-) diff --git a/src/bsp.hpp b/src/bsp.hpp index 3de86f1..9eea677 100644 --- a/src/bsp.hpp +++ b/src/bsp.hpp @@ -180,10 +180,6 @@ void ClockwiseBspTree(level_t &level); // removing minisegs). void NormaliseBspTree(level_t &level); -// Mark new vertices as old for writing into the VERTEXES lump -// Needed for RoundOffBspTree and saving to Doom 64 map formats directly -void RoundOffVertices(level_t &level); - // traverse the BSP tree, doing whatever is necessary to round // vertices to integer coordinates (for example, removing segs whose // rounded coordinates degenerate to the same point). diff --git a/src/level.cpp b/src/level.cpp index bdb8473..363be9a 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -354,7 +354,7 @@ static void ValidateLinedef(level_t &level, linedef_t *line) static void GetVertices_Integral(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "VERTEXES"); + auto lump = io.ReadLevelLump(level.lump, "VERTEXES"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -373,7 +373,7 @@ static void GetVertices_Integral(WadIO &io, level_t &level) static void GetVertices_Fractional(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "VERTEXES"); + auto lump = io.ReadLevelLump(level.lump, "VERTEXES"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -393,7 +393,7 @@ static void GetVertices_Fractional(WadIO &io, level_t &level) static void GetSectors_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "SECTORS"); + auto lump = io.ReadLevelLump(level.lump, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -409,7 +409,7 @@ static void GetSectors_Doom(WadIO &io, level_t &level) static void GetThings_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "THINGS"); + auto lump = io.ReadLevelLump(level.lump, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -428,7 +428,7 @@ static void GetThings_Doom(WadIO &io, level_t &level) static void GetSidedefs_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "SIDEDEFS"); + auto lump = io.ReadLevelLump(level.lump, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -450,7 +450,7 @@ static void GetSidedefs_Doom(WadIO &io, level_t &level) static void GetLinedefs_Doom(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); + auto lump = io.ReadLevelLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -531,7 +531,7 @@ static void GetLinedefs_Doom(WadIO &io, level_t &level) static void GetThings_Hexen(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "THINGS"); + auto lump = io.ReadLevelLump(level.lump, "THINGS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -550,7 +550,7 @@ static void GetThings_Hexen(WadIO &io, level_t &level) static void GetLinedefs_Hexen(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); + auto lump = io.ReadLevelLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -596,7 +596,7 @@ static void GetLinedefs_Hexen(WadIO &io, level_t &level) static void GetSectors_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "SECTORS"); + auto lump = io.ReadLevelLump(level.lump, "SECTORS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -618,7 +618,7 @@ static void GetSectors_Doom64(WadIO &io, level_t &level) static void GetSidedefs_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "SIDEDEFS"); + auto lump = io.ReadLevelLump(level.lump, "SIDEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -638,7 +638,7 @@ static void GetSidedefs_Doom64(WadIO &io, level_t &level) static void GetLinedefs_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); + auto lump = io.ReadLevelLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -683,7 +683,7 @@ static void GetLinedefs_Doom64(WadIO &io, level_t &level) static void GetThings_Doom64(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "LINEDEFS"); + auto lump = io.ReadLevelLump(level.lump, "LINEDEFS"); if (HAS_BIT(config.debug, DEBUG_LOAD)) { @@ -1004,7 +1004,7 @@ static void ParseUDMF_Pass(level_t &level, const std::string &data, int pass) void ParseUDMF(WadIO &io, level_t &level) { - auto lump = io.ReadMapLump(level.lump, "TEXTMAP"); + auto lump = io.ReadLevelLump(level.lump, "TEXTMAP"); // load the lump into this string auto data = std::string(lump.begin(), lump.end()); @@ -1163,7 +1163,7 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) CheckBinaryFormatLimits(level); // Use fractional VERTEXES lump in some map formats - bool fractional = level.map_format == MapFormat_Doom64; + bool do_fractional = level.map_format == MapFormat_Doom64; // Generate LEAFS lump bool do_leafs = level.map_format == MapFormat_Doom64; @@ -1172,19 +1172,19 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) bsp_format_t bsp_format = CheckFormatBSP(config, level); // Only include BSP-generated vertices in non-ZDBSP lump format - bool include_new = bsp_format >= BSP_DoomBSP || bsp_format <= BSP_DeePBSPV4; + bool do_new_vert = bsp_format >= BSP_DoomBSP || bsp_format <= BSP_DeePBSPV4; // Use Zlib-compressed version of ZDBSP lump formats - bool bsp_compress = config.compress; + bool zdbsp_compress = config.compress; // Copy marker wholesale size_t lump = level.lump; io.CopyLump(lump); // Correct order is paramount - io.CopyLump(io.LevelLookupLump(lump, "THINGS")); - io.CopyLump(io.LevelLookupLump(lump, "LINEDEFS")); - io.CopyLump(io.LevelLookupLump(lump, "SIDEDEFS")); + io.LevelCopyLump(lump, "THINGS"); + io.LevelCopyLump(lump, "LINEDEFS"); + io.LevelCopyLump(lump, "SIDEDEFS"); // Do the four BSP lumps, before promptly doing the other normal lumps if (bsp_format == BSP_DoomBSP) @@ -1193,7 +1193,7 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) RoundOffBspTree(level); SortSegs(level); - PutVertices(io, level, fractional, include_new); + PutVertices(io, level, do_fractional, do_new_vert); PutSegs_DoomBSP(io, level); PutSubsecs_DoomBSP(io, level); PutNodes_DoomBSP(io, level); @@ -1204,7 +1204,7 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) RoundOffBspTree(level); SortSegs(level); - PutVertices(io, level, fractional, include_new); + PutVertices(io, level, do_fractional, do_new_vert); PutSegs_DeePBSPV4(io, level); PutSubsecs_DeePBSPV4(io, level); PutNodes_DeePBSPV4(io, level); @@ -1212,22 +1212,22 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) else if (bsp_format == BSP_XNOD) { // ZDBSP formats do not write to VERTEXES - PutVertices(io, level, fractional, include_new); + PutVertices(io, level, do_fractional, do_new_vert); io.CreateEmptyLump("SEGS"); io.CreateEmptyLump("SSECTORS"); - PutTree_ZDBSP(io, level, "NODES", bsp_format, bsp_compress); + PutTree_ZDBSP(io, level, "NODES", bsp_format, zdbsp_compress); } else if (bsp_format >= BSP_XGLN && bsp_format <= BSP_XGL3) { // ZDBSP formats do not write to VERTEXES - PutVertices(io, level, fractional, include_new); + PutVertices(io, level, do_fractional, do_new_vert); io.CreateEmptyLump("SEGS"); - PutTree_ZDBSP(io, level, "SSECTORS", bsp_format, bsp_compress); + PutTree_ZDBSP(io, level, "SSECTORS", bsp_format, zdbsp_compress); io.CreateEmptyLump("NODES"); } // Why is SECTORS right here? T_T - io.CopyLump(io.LevelLookupLump(lump, "SECTORS")); + io.LevelCopyLump(lump, "SECTORS"); // Bruh PutReject(io, level); @@ -1246,14 +1246,14 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) // Level scripts and stuff if (level.map_format == MapFormat_Hexen) { - io.CopyLump(io.LevelLookupLump(lump, "BEHAVIOR")); - io.CopyLump(io.LevelLookupLump(lump, "SCRIPTS")); + io.LevelCopyLump(lump, "BEHAVIOR"); + io.LevelCopyLump(lump, "SCRIPTS"); } else if (level.map_format == MapFormat_Doom64) { - io.CopyLump(io.LevelLookupLump(lump, "LIGHTS")); - io.CopyLump(io.LevelLookupLump(lump, "MACROS")); - io.CopyLump(io.LevelLookupLump(lump, "SCRIPTS")); + io.LevelCopyLump(lump, "LIGHTS"); + io.LevelCopyLump(lump, "MACROS"); + io.LevelCopyLump(lump, "SCRIPTS"); } return level.overflows ? BUILD_LumpOverflow : BUILD_OK; @@ -1274,11 +1274,11 @@ build_result_t SaveLevelTextMap(WadIO &io, level_t &level) PutBlockmap(io, level); // Copy everything else we don't touch - io.CopyLump(io.LevelLookupLump(lump, "BEHAVIOR")); - io.CopyLump(io.LevelLookupLump(lump, "MACROS")); - io.CopyLump(io.LevelLookupLump(lump, "SCRIPTS")); - io.CopyLump(io.LevelLookupLump(lump, "DIALOGUE")); - io.CopyLump(io.LevelLookupLump(lump, "LIGHTMAP")); + io.LevelCopyLump(lump, "BEHAVIOR"); + io.LevelCopyLump(lump, "MACROS"); + io.LevelCopyLump(lump, "SCRIPTS"); + io.LevelCopyLump(lump, "DIALOGUE"); + io.LevelCopyLump(lump, "LIGHTMAP"); // Finish io.CreateEmptyLump("ENDMAP"); diff --git a/src/local.hpp b/src/local.hpp index ae991f6..6882c0b 100644 --- a/src/local.hpp +++ b/src/local.hpp @@ -236,11 +236,6 @@ struct seg_t } }; -// compute the seg private info (psx/y, pex/y, pdx/y, etc). -void Recompute(seg_t *seg); - -int PointOnLineSide(seg_t *seg, double x, double y); - // a seg with this index is removed by SortSegs(). // it must be a very high value. static constexpr uint32_t SEG_IS_GARBAGE = (1 << 29); @@ -262,18 +257,6 @@ struct subsec_t double mid_y; }; -void AddToTail(subsec_t *subsec); - -void DetermineMiddle(subsec_t *subsec); -void ClockwiseOrder(subsec_t *subsec); -void RenumberSegs(subsec_t *subsec, size_t &cur_seg_index); - -void RoundOffSubsector(level_t &level, subsec_t *subsec); -void Normalise(subsec_t *subsec); - -void SanityCheckClosed(subsec_t *subsec); -void SanityCheckHasRealSeg(subsec_t *subsec); - struct bbox_t { int16_t minx, miny; @@ -307,8 +290,6 @@ struct node_t size_t index; }; -void SetPartition(node_t *node, const seg_t *part); - struct quadtree_c { // NOTE: not a real quadtree, division is always binary. @@ -341,11 +322,6 @@ struct quadtree_c } }; -void AddSeg(quadtree_c *quadtree, seg_t *seg); -void AddList(quadtree_c *quadtree, seg_t *list); - -void ConvertToList(quadtree_c *quadtree, seg_t **__list); - // check relationship between this box and the partition line. // returns -1 or +1 if box is definitively on a particular side, // or 0 if the line intersects or touches the box. @@ -492,6 +468,10 @@ void FreeIntersections(level_t &level); // ANALYZE : Analyzing level structures //------------------------------------------------------------------------ +// +// TODO: some of these are defined in misc, but only used elsewhere. static? +// + // detection routines void DetectOverlappingVertices(level_t &level); void DetectOverlappingLines(level_t &level); diff --git a/src/node.cpp b/src/node.cpp index 1a48c26..9c86051 100644 --- a/src/node.cpp +++ b/src/node.cpp @@ -85,7 +85,7 @@ struct eval_info_t // // Fill in the fields 'angle', 'len', 'pdx', 'pdy', etc... // -void Recompute(seg_t *seg) +static void Recompute(seg_t *seg) { seg->psx = seg->start->x; seg->psy = seg->start->y; @@ -1031,7 +1031,7 @@ void AddMinisegs(level_t &level, intersection_t *cut_list, seg_t *part, seg_t ** // Rewritten by Andrew Apted (-AJA-), 1999-2000. // -void SetPartition(node_t *node, const seg_t *part) +static void SetPartition(node_t *node, const seg_t *part) { SYS_ASSERT(part->linedef); @@ -1064,7 +1064,7 @@ void SetPartition(node_t *node, const seg_t *part) // // Returns -1 for left, +1 for right, or 0 for intersect. // -int PointOnLineSide(const seg_t *seg, double x, double y) +static int PointOnLineSide(const seg_t *seg, double x, double y) { double perp = seg->PerpDist(x, y); @@ -1114,7 +1114,7 @@ quadtree_c::~quadtree_c(void) } } -void AddSeg(quadtree_c *quadtree, seg_t *seg) +static void AddSeg(quadtree_c *quadtree, seg_t *seg) { // update seg counts if (seg->linedef != nullptr) @@ -1169,7 +1169,7 @@ void AddSeg(quadtree_c *quadtree, seg_t *seg) seg->quad = quadtree; } -void AddList(quadtree_c *quadtree, seg_t *new_list) +static void AddList(quadtree_c *quadtree, seg_t *new_list) { while (new_list != nullptr) { @@ -1180,7 +1180,7 @@ void AddList(quadtree_c *quadtree, seg_t *new_list) } } -void ConvertToList(quadtree_c *quadtree, seg_t **_list) +static void ConvertToList(quadtree_c *quadtree, seg_t **_list) { while (quadtree->list != nullptr) { @@ -1358,7 +1358,7 @@ quadtree_c *TreeFromSegList(seg_t *list, const bbox_t *bounds) return tree; } -void DetermineMiddle(subsec_t *subsec) +static void DetermineMiddle(subsec_t *subsec) { subsec->mid_x = 0; subsec->mid_y = 0; @@ -1381,7 +1381,7 @@ void DetermineMiddle(subsec_t *subsec) } } -void AddToTail(subsec_t *subsec, seg_t *seg) +static void AddToTail(subsec_t *subsec, seg_t *seg) { seg->next = nullptr; @@ -1400,7 +1400,7 @@ void AddToTail(subsec_t *subsec, seg_t *seg) tail->next = seg; } -void ClockwiseOrder(subsec_t *subsec) +static void ClockwiseOrder(subsec_t *subsec) { seg_t *seg; @@ -1495,7 +1495,7 @@ void ClockwiseOrder(subsec_t *subsec) } } -void SanityCheckClosed(subsec_t *subsec) +static void SanityCheckClosed(subsec_t *subsec) { int gaps = 0; int total = 0; @@ -1536,7 +1536,7 @@ void SanityCheckClosed(subsec_t *subsec) } } -void SanityCheckHasRealSeg(subsec_t *subsec) +static void SanityCheckHasRealSeg(subsec_t *subsec) { for (seg_t *seg = subsec->seg_list; seg; seg = seg->next) { @@ -1550,7 +1550,7 @@ void SanityCheckHasRealSeg(subsec_t *subsec) subsec->mid_y); } -void RenumberSegs(subsec_t *subsec, size_t &cur_seg_index) +static void RenumberSegs(subsec_t *subsec, size_t &cur_seg_index) { if (HAS_BIT(config.debug, DEBUG_SUBSEC)) { @@ -1710,7 +1710,7 @@ void ClockwiseBspTree(level_t &level) } } -void Normalise(subsec_t *subsec) +static void Normalise(subsec_t *subsec) { // use head + tail to maintain same order of segs seg_t *new_head = nullptr; @@ -1779,7 +1779,7 @@ void NormaliseBspTree(level_t &level) } } -void RoundOffSubsector(level_t &level, subsec_t *subsec) +static void RoundOffSubsector(level_t &level, subsec_t *subsec) { // use head + tail to maintain same order of segs seg_t *new_head = nullptr; diff --git a/src/wad.cpp b/src/wad.cpp index 7ad1fe0..fa2f3af 100644 --- a/src/wad.cpp +++ b/src/wad.cpp @@ -338,17 +338,22 @@ void WadIO::CopyLump(size_t index) auto lump = this->ReadLump(index); if (index < this->NumLumps()) { - WriteLump(this->LumpName(index), lump.data(), lump.size()); + this->WriteLump(this->LumpName(index), lump.data(), lump.size()); } } +void WadIO::LevelCopyLump(size_t lump, std::string_view name) +{ + this->CopyLump(this->LevelLookupLump(lump, name)); +} + // // Partial writing // void WadIO::StartWritingLump(std::string_view name) { - CreateEmptyLump(name); + this->CreateEmptyLump(name); } void WadIO::AddToLump(const void *data, size_t len) @@ -360,35 +365,35 @@ void WadIO::AddToLump(const void *data, size_t len) WadIO &WadIO::operator<<(uint8_t value) { - AddToLumpZ(&value, sizeof(uint8_t)); + this->AddToLumpZ(&value, sizeof(uint8_t)); return *this; } WadIO &WadIO::operator<<(uint16_t value) { value = GetLittleEndian(value); - AddToLumpZ(reinterpret_cast(&value), sizeof(uint16_t)); + this->AddToLumpZ(reinterpret_cast(&value), sizeof(uint16_t)); return *this; } WadIO &WadIO::operator<<(uint32_t value) { value = GetLittleEndian(value); - AddToLumpZ(reinterpret_cast(&value), sizeof(uint32_t)); + this->AddToLumpZ(reinterpret_cast(&value), sizeof(uint32_t)); return *this; } WadIO &WadIO::operator<<(int16_t value) { value = GetLittleEndian(value); - AddToLumpZ(reinterpret_cast(&value), sizeof(int16_t)); + this->AddToLumpZ(reinterpret_cast(&value), sizeof(int16_t)); return *this; } WadIO &WadIO::operator<<(fixed_t value) { value = GetLittleEndian(value); - AddToLumpZ(reinterpret_cast(&value), sizeof(fixed_t)); + this->AddToLumpZ(reinterpret_cast(&value), sizeof(fixed_t)); return *this; } diff --git a/src/wad.hpp b/src/wad.hpp index af4c3ef..a8fdecd 100644 --- a/src/wad.hpp +++ b/src/wad.hpp @@ -85,13 +85,14 @@ struct WadIO template std::vector ReadLump(size_t lump_index); template - std::vector ReadMapLump(size_t lump_index, std::string_view name); + std::vector ReadLevelLump(size_t lump_index, std::string_view name); // Write operations void SafeWrite(const void *buffer, size_t size); void CreateEmptyLump(std::string_view name); void WriteLump(std::string_view name, const void *data, size_t size); void CopyLump(size_t lump); + void LevelCopyLump(size_t lump, std::string_view name); // Partial writing void StartWritingLump(std::string_view name); @@ -130,7 +131,7 @@ std::vector WadIO::ReadLump(size_t lump_index) } template -std::vector WadIO::ReadMapLump(size_t lump_index, std::string_view name) +std::vector WadIO::ReadLevelLump(size_t lump_index, std::string_view name) { return ReadLump(this->LevelLookupLump(lump_index, name)); } From 54ae3285e1f5013a2d73e3d4f59771d60c2150e3 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Fri, 22 May 2026 20:04:25 -0300 Subject: [PATCH 09/10] added missing fractional check --- src/level.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/level.cpp b/src/level.cpp index 363be9a..9744388 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1190,7 +1190,7 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) if (bsp_format == BSP_DoomBSP) { NormaliseBspTree(level); - RoundOffBspTree(level); + if (!do_fractional) RoundOffBspTree(level); SortSegs(level); PutVertices(io, level, do_fractional, do_new_vert); @@ -1201,7 +1201,7 @@ build_result_t SaveLevelBinary(WadIO &io, level_t &level) else if (bsp_format == BSP_DeePBSPV4) { NormaliseBspTree(level); - RoundOffBspTree(level); + if (!do_fractional) RoundOffBspTree(level); SortSegs(level); PutVertices(io, level, do_fractional, do_new_vert); From b3772f8caed42f28edfc3e7c5be67668b3895541 Mon Sep 17 00:00:00 2001 From: "Guilherme M. Miranda" Date: Fri, 22 May 2026 20:19:16 -0300 Subject: [PATCH 10/10] duh. --- src/level.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/level.cpp b/src/level.cpp index 9744388..a56f6aa 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1268,6 +1268,9 @@ build_result_t SaveLevelTextMap(WadIO &io, level_t &level) size_t lump = level.lump; io.CopyLump(lump); + // duh. + io.LevelCopyLump(lump, "TEXTMAP"); + // Always use ZGL3 for UDMF maps PutTree_ZDBSP(io, level, "ZNODES", BSP_XGL3, true); PutReject(io, level);