// DR. ROBOTNIK'S RING RACERS //----------------------------------------------------------------------------- // Copyright (C) 2025 by James Robert Roman // Copyright (C) 2025 by Kart Krew // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- #include #include #include #include #include #include #include #include #include "core/hash_map.hpp" #include "core/string.h" #include "core/vector.hpp" #include "cxxutil.hpp" #include "doomstat.h" #include "r_textures.h" #include "w_wad.h" namespace { srb2::HashMap> g_dups; std::map> g_warnings; std::thread g_dups_thread; srb2::String key8char(const char cstr[8]) { std::string_view view(cstr, 8); srb2::String key; view = view.substr(0, view.find('\0')); // terminate by '\0' key.reserve(view.size()); std::transform( view.cbegin(), view.cend(), std::back_inserter(key), [](unsigned char c) { return std::toupper(c); } ); return key; } srb2::String texture_location(const texture_t& tex) { if (tex.type == TEXTURETYPE_SINGLEPATCH) { SRB2_ASSERT(tex.patchcount == 1); const texpatch_t& texpat = tex.patches[0]; const wadfile_t& wad = *wadfiles[texpat.wad]; return srb2::format( "'{}/{}'", std::filesystem::path(wad.filename).filename().string(), wad.lumpinfo[texpat.lump].fullname ); } else { return "TEXTURES"; } } void print_dup(const texture_t* tex) { // Do not use CONS_Alert because it is not thread-safe CONS_Printf( "\x82" "WARNING:" "\x80 duplicate texture '%.8s' (%s)\n", tex->name, texture_location(*tex).c_str() ); } }; // namespace void R_CheckTextureDuplicates(INT32 start, INT32 end) { SRB2_ASSERT(start >= 0); SRB2_ASSERT(end <= numtextures); auto find_dup = [end](const texture_t* t1, int32_t idx) { while (++idx < end) { const texture_t* t2 = textures[idx]; if (t1->hash == t2->hash && !strncmp(t1->name, t2->name, 8)) { break; } } return idx; }; auto collate_dups = [end, find_dup](int32_t start) { const texture_t* t1 = textures[start]; const srb2::String key = key8char(t1->name); if (g_dups.find(key) != g_dups.end()) { return; } int32_t idx = find_dup(t1, start); if (idx < end) { srb2::Vector& v = g_dups[key]; v.push_back(textures[start]); do { v.push_back(textures[idx]); } while ((idx = find_dup(t1, idx)) < end); } }; auto worker = [start, end, collate_dups] { for (int32_t i = start; i < end; ++i) { collate_dups(i); } }; SRB2_ASSERT(g_dups_thread.joinable() == false); g_dups_thread = std::thread(worker); } void R_PrintTextureDuplicates(void) { if (g_dups_thread.joinable()) { g_dups_thread.join(); } for (auto [key, v] : g_dups) { std::for_each(v.cbegin(), v.cend(), print_dup); } g_dups = {}; R_PrintTextureWarnings(); } void R_InsertTextureWarning(const char *header, const char *warning) { g_warnings[header].push_back(warning); } void R_PrintTextureWarnings(void) { if (g_dups_thread.joinable()) { return; } for (auto [header, v] : g_warnings) { CONS_Alert(CONS_WARNING, "\n%s", header.c_str()); for (const srb2::String& warning : v) { CONS_Printf("%s\n", warning.c_str()); } } g_warnings = {}; }