diff --git a/bsputil/bsputil.cc b/bsputil/bsputil.cc index 6630e215..7cee8fe4 100644 --- a/bsputil/bsputil.cc +++ b/bsputil/bsputil.cc @@ -562,11 +562,11 @@ int main(int argc, char **argv) f << bsp.dentdata; - f.close(); - if (!f) Error("{}", strerror(errno)); + f.close(); + printf("done.\n"); } else if (!strcmp(argv[i], "--extract-textures")) { source.replace_extension(".wad"); diff --git a/bsputil/decompile.cpp b/bsputil/decompile.cpp index eacd20e0..5a1a5818 100644 --- a/bsputil/decompile.cpp +++ b/bsputil/decompile.cpp @@ -35,6 +35,7 @@ #include #include +#include #include "tbb/parallel_for.h" @@ -757,7 +758,7 @@ static void DecompileEntity( } // First, print the key/values for this entity - file << '{' << std::endl; + fmt::print(file, "{\n"); for (const auto &keyValue : dict) { if (keyValue.first == "model" && !keyValue.second.empty() && keyValue.second[0] == '*') { // strip "model" "*NNN" key/values @@ -769,7 +770,7 @@ static void DecompileEntity( continue; } - file << "\"" << keyValue.first << "\" \"" << keyValue.second << "\"" << std::endl; + fmt::print(file, "\"{}\" \"{}\"\n", keyValue.first, keyValue.second); } // Print brushes if any @@ -802,7 +803,7 @@ static void DecompileEntity( } } - file << "}" << std::endl; + fmt::print(file, "}\n"); } void DecompileBSP(const mbsp_t *bsp, const decomp_options &options, std::ofstream &file) diff --git a/common/cmdlib.cc b/common/cmdlib.cc index bd3d8e8b..678ae442 100644 --- a/common/cmdlib.cc +++ b/common/cmdlib.cc @@ -230,13 +230,13 @@ int Q_strncasecmp(const char *s1, const char *s2, int n) if (c2 >= 'a' && c2 <= 'z') c2 -= ('a' - 'A'); if (c1 != c2) - return -1; /* strings not equal */ + return c1 < c2 ? -1 : 1; /* strings not equal */ } if (!c1) return 0; /* strings are equal */ } - return -1; + return 0; } int Q_strcasecmp(const char *s1, const char *s2) @@ -296,7 +296,11 @@ qfile_t SafeOpenWrite(const std::filesystem::path &filename) { FILE *f; +#ifdef _WIN32 + f = _wfopen(filename.c_str(), L"wb"); +#else f = fopen(filename.string().c_str(), "wb"); +#endif if (!f) FError("Error opening {}: {}", filename, strerror(errno)); @@ -304,28 +308,41 @@ qfile_t SafeOpenWrite(const std::filesystem::path &filename) return { f, fclose }; } -qfile_t SafeOpenRead(const std::filesystem::path &filename) +qfile_t SafeOpenRead(const std::filesystem::path &filename, bool must_exist) { FILE *f; +#ifdef _WIN32 + f = _wfopen(filename.c_str(), L"rb"); +#else f = fopen(filename.string().c_str(), "rb"); +#endif if (!f) - FError("Error opening {}: {}", filename, strerror(errno)); + { + if (must_exist) + FError("Error opening {}: {}", filename, strerror(errno)); + + return { nullptr, nullptr }; + } return { f, fclose }; } -void SafeRead(const qfile_t &f, void *buffer, int count) +size_t SafeRead(const qfile_t &f, void *buffer, size_t count) { if (fread(buffer, 1, count, f.get()) != (size_t)count) FError("File read failure"); + + return count; } -void SafeWrite(const qfile_t &f, const void *buffer, int count) +size_t SafeWrite(const qfile_t &f, const void *buffer, size_t count) { if (fwrite(buffer, 1, count, f.get()) != (size_t)count) FError("File write failure"); + + return count; } void SafeSeek(const qfile_t &f, long offset, int32_t origin) @@ -364,7 +381,7 @@ long LoadFilePak(std::filesystem::path &filename, void *destptr) // check if we have a .pak file in this path for (auto p = filename.parent_path(); !p.empty() && p != p.root_path(); p = p.parent_path()) { if (p.extension() == ".pak") { - auto file = SafeOpenRead(p); + qfile_t file = SafeOpenRead(p); // false positive if (!file) diff --git a/common/entdata.cc b/common/entdata.cc index f26e5f9f..3d61c7f3 100644 --- a/common/entdata.cc +++ b/common/entdata.cc @@ -28,6 +28,8 @@ #include #include +#include + entdict_t::entdict_t(std::initializer_list l) : keyvalues(l) { } entdict_t::entdict_t() = default; @@ -170,15 +172,15 @@ std::vector EntData_Parse(const char *entdata) */ std::string EntData_Write(const std::vector &ents) { - std::stringstream out; + std::string out; for (const auto &ent : ents) { - out << "{\n"; + out += "{\n"; for (const auto &epair : ent) { - out << "\"" << epair.first << "\" \"" << epair.second << "\"\n"; + fmt::format_to(std::back_inserter(out), "\"{}\" \"{}\"\n", epair.first, epair.second); } - out << "}\n"; + out += "}\n"; } - return out.str(); + return out; } std::string EntDict_StringForKey(const entdict_t &dict, const std::string key) diff --git a/common/mathlib.cc b/common/mathlib.cc index 507f9dc0..99f7f9be 100644 --- a/common/mathlib.cc +++ b/common/mathlib.cc @@ -56,11 +56,7 @@ bool SetPlanePts(const vec3_t planepts[3], vec3_t &normal, vec_t *dist) */ std::string VecStr(const vec3_t vec) { - char buf[128]; - - snprintf(buf, sizeof(buf), "%i %i %i", (int)vec[0], (int)vec[1], (int)vec[2]); - - return buf; + return fmt::format("{} {} {}", (int32_t) vec[0], (int32_t) vec[1], (int32_t) vec[2]); } std::string // mxd @@ -73,11 +69,7 @@ VecStr(const qvec3f vec) std::string VecStrf(const vec3_t vec) { - char buf[128]; - - snprintf(buf, sizeof(buf), "%.2f %.2f %.2f", vec[0], vec[1], vec[2]); - - return buf; + return fmt::format("{:.2} {:.2} {:.2}", vec[0], vec[1], vec[2]); } std::string // mxd diff --git a/common/qvec.cc b/common/qvec.cc index 27bb3e26..693b7487 100644 --- a/common/qvec.cc +++ b/common/qvec.cc @@ -20,7 +20,7 @@ #include #include // for NAN -#include +#include /* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) @@ -143,7 +143,5 @@ qmat2x2f qv::inverse(const qmat2x2f &m) std::string qv::to_string(const qvec<3, float> &v1) { - std::stringstream ss; - ss << v1[0] << " " << v1[1] << " " << v1[2]; - return ss.str(); + return fmt::format("{} {} {}", v1[0], v1[1], v1[2]); } diff --git a/include/common/cmdlib.hh b/include/common/cmdlib.hh index 2fb6b34e..cffa9c82 100644 --- a/include/common/cmdlib.hh +++ b/include/common/cmdlib.hh @@ -60,6 +60,38 @@ extern std::filesystem::path qdir, // c:/Quake/, c:/Hexen II/ etc. bool string_iequals(const std::string &a, const std::string &b); // mxd +struct case_insensitive_hash +{ + std::size_t operator()(const std::string &s) const noexcept + { + std::size_t hash = 0x811c9dc5; + constexpr std::size_t prime = 0x1000193; + + for (auto &c : s) { + hash ^= tolower(c); + hash *= prime; + } + + return hash; + } +}; + +struct case_insensitive_equal +{ + bool operator()(const std::string &l, const std::string &r) const noexcept + { + return Q_strcasecmp(l.c_str(), r.c_str()) == 0; + } +}; + +struct case_insensitive_less +{ + bool operator()(const std::string &l, const std::string &r) const noexcept + { + return Q_strcasecmp(l.c_str(), r.c_str()) < 0; + } +}; + void SetQdirFromPath(const std::string &basedirname, std::filesystem::path path); // Returns the path itself if it has an extension already, otherwise @@ -106,17 +138,11 @@ int CheckParm(const char *check); using qfile_t = std::unique_ptr; qfile_t SafeOpenWrite(const std::filesystem::path &filename); -qfile_t SafeOpenRead(const std::filesystem::path &filename); -void SafeRead(const qfile_t &f, void *buffer, int count); -void SafeWrite(const qfile_t &f, const void *buffer, int count); +qfile_t SafeOpenRead(const std::filesystem::path &filename, bool must_exist = false); +size_t SafeRead(const qfile_t &f, void *buffer, size_t count); +size_t SafeWrite(const qfile_t &f, const void *buffer, size_t count); void SafeSeek(const qfile_t &f, long offset, int32_t origin); long SafeTell(const qfile_t &f); -template -inline void SafePrint(const qfile_t &f, const char *fmt, const Args &...args) -{ - if (fmt::fprintf(f.get(), fmt, std::forward(args)...) < 0) - FError("Error writing to file"); -} long LoadFilePak(std::filesystem::path &filename, void *destptr); long LoadFile(const std::filesystem::path &filename, void *destptr); diff --git a/include/common/polylib.hh b/include/common/polylib.hh index 28664816..097b221b 100644 --- a/include/common/polylib.hh +++ b/include/common/polylib.hh @@ -349,7 +349,7 @@ public: for (; j < 3; j++) if (p1[j] > bogus_range || p1[j] < -bogus_range) - FError("BOGUS_RANGE: %f", p1[j]); + FError("BOGUS_RANGE: {}", p1[j]); /* check the point is on the face plane */ vec_t d = DotProduct(&p1[0], face.normal) - face.dist; diff --git a/include/light/settings.hh b/include/light/settings.hh index c717353b..54b1dc0e 100644 --- a/include/light/settings.hh +++ b/include/light/settings.hh @@ -287,9 +287,7 @@ public: virtual std::string stringValue() const { - std::stringstream ss; - ss << _value[0] << " " << _value[1] << " " << _value[2]; - return ss.str(); + return fmt::format("{} {} {}", _value[0], _value[1], _value[2]); } }; diff --git a/include/qbsp/map.hh b/include/qbsp/map.hh index a33a374e..f971d7b8 100644 --- a/include/qbsp/map.hh +++ b/include/qbsp/map.hh @@ -22,17 +22,11 @@ #pragma once #include +#include "common/cmdlib.hh" #include #include -struct epair_t -{ - epair_t *next; - char *key; - char *value; -}; - struct mapface_t { qbsp_plane_t plane; @@ -96,7 +90,11 @@ public: // Temporary lists used to build `brushes` in the correct order. brush_t *solid, *sky, *detail, *detail_illusionary, *detail_fence, *liquid; - epair_t *epairs; + // tree of key/value pairs + std::map epairs; + // order of parse + std::vector epair_order; + vec3_t mins, maxs; brush_t *brushes; /* NULL terminated list */ int numbrushes; @@ -108,7 +106,7 @@ public: mapentity_t() : firstmapbrush(0), nummapbrushes(0), solid(nullptr), sky(nullptr), detail(nullptr), - detail_illusionary(nullptr), detail_fence(nullptr), liquid(nullptr), epairs(nullptr), brushes(nullptr), + detail_illusionary(nullptr), detail_fence(nullptr), liquid(nullptr), epairs(), brushes(nullptr), numbrushes(0), firstoutputfacenumber(-1), outputmodelnumber(-1) { VectorSet(origin, 0, 0, 0); @@ -252,6 +250,6 @@ void ExportObj_Surfaces(const std::string &filesuffix, const surface_t *surfaces void ExportObj_Nodes(const std::string &filesuffix, const node_t *nodes); void ExportObj_Marksurfaces(const std::string &filesuffix, const node_t *nodes); -void WriteBspBrushMap(const char *name, const std::vector &list); +void WriteBspBrushMap(const std::filesystem::path &name, const std::vector &list); bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec); diff --git a/include/qbsp/wad.hh b/include/qbsp/wad.hh index 9fd1cc3c..6bad19a6 100644 --- a/include/qbsp/wad.hh +++ b/include/qbsp/wad.hh @@ -24,6 +24,7 @@ #include #include #include +#include "common/cmdlib.hh" // Texture data stored for quick searching struct texture_t @@ -51,44 +52,13 @@ struct lumpinfo_t char name[16]; // must be null terminated }; -struct case_insensitive_hash -{ - std::size_t operator()(const std::string &s) const noexcept - { - std::size_t hash = 0x811c9dc5; - constexpr std::size_t prime = 0x1000193; - - for (auto &c : s) { - hash ^= tolower(c); - hash *= prime; - } - - return hash; - } -}; - -struct case_insensitive_equal -{ - bool operator()(const std::string &l, const std::string &r) const noexcept - { - return Q_strcasecmp(l.c_str(), r.c_str()) == 0; - } -}; - struct wad_t { wadinfo_t header; int version; std::unordered_map lumps; std::unordered_map textures; - FILE *file; - - ~wad_t() - { - if (file) { - fclose(file); - } - } + qfile_t file = { nullptr, nullptr }; }; // Q1 miptex format diff --git a/light/entities.cc b/light/entities.cc index 03672e33..3f942258 100644 --- a/light/entities.cc +++ b/light/entities.cc @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include @@ -154,25 +154,15 @@ static std::string EntDict_PrettyDescription(const mbsp_t *bsp, const entdict_t { // get the submodel's bbox if it's a brush entity if (bsp != nullptr && EntDict_StringForKey(entity, "origin") == "" && EntDict_StringForKey(entity, "model") != "") { - const std::string submodel_str = EntDict_StringForKey(entity, "model"); const dmodel_t *info = BSP_DModelForModelString(bsp, submodel_str); if (info) { - std::stringstream s; - s << "brush entity with mins ("; - s << VecStrf(info->mins); - s << ") maxs ("; - s << VecStrf(info->maxs); - s << ") (" << EntDict_StringForKey(entity, "classname") << ")"; - return s.str(); + return fmt::format("brush entity with mins ({}) maxs ({}) ({})", VecStrf(info->mins), VecStrf(info->maxs), EntDict_StringForKey(entity, "classname")); } } - std::stringstream s; - s << "entity at (" << EntDict_StringForKey(entity, "origin") << ") (" << EntDict_StringForKey(entity, "classname") - << ")"; - return s.str(); + return fmt::format("entity at ({}) ({})", EntDict_StringForKey(entity, "origin"), EntDict_StringForKey(entity, "classname")); } bool EntDict_CheckNoEmptyValues(const mbsp_t *bsp, const entdict_t &entdict) @@ -875,7 +865,9 @@ static rgba_miptex_t *FindProjectionTexture(const mbsp_t *bsp, const char *texna static std::string ParseEscapeSequences(const std::string &input) { - std::stringstream ss; + std::string s; + s.reserve(input.size()); + bool bold = false; for (size_t i = 0; i < input.length(); i++) { @@ -887,10 +879,10 @@ static std::string ParseEscapeSequences(const std::string &input) if (bold) { c |= 128; } - ss.put(static_cast(c)); + s += static_cast(c); } } - return ss.str(); + return s; } /* @@ -1264,10 +1256,10 @@ void WriteEntitiesToString(const globalconfig_t &cfg, mbsp_t *bsp) static std::vector surfacelight_templates; -static FILE *surflights_dump_file; +static std::ofstream surflights_dump_file; static std::filesystem::path surflights_dump_filename; -static void SurfLights_WriteEntityToFile(FILE *f, light_t *entity, const vec3_t pos) +static void SurfLights_WriteEntityToFile(light_t *entity, const vec3_t pos) { Q_assert(entity->epairs != nullptr); @@ -1275,8 +1267,7 @@ static void SurfLights_WriteEntityToFile(FILE *f, light_t *entity, const vec3_t EntDict_RemoveValueForKey(epairs, "_surface"); epairs.set("origin", VecStr(pos)); - std::string entstring = EntData_Write({epairs}); - fwrite(entstring.data(), 1, entstring.size(), f); + surflights_dump_file << EntData_Write({epairs}); } static void CreateSurfaceLight(const vec3_t &origin, const vec3_t &normal, const light_t *surflight_template) @@ -1296,7 +1287,7 @@ static void CreateSurfaceLight(const vec3_t &origin, const vec3_t &normal, const /* export it to a map file for debugging */ if (surflight_dump) { - SurfLights_WriteEntityToFile(surflights_dump_file, &entity, origin); + SurfLights_WriteEntityToFile(&entity, origin); } all_lights.push_back(entity); @@ -1463,39 +1454,39 @@ static void GL_SubdivideSurface(const bsp2_dface_t *face, const modelinfo_t *fac } bool ParseLightsFile(const std::filesystem::path &fname) -{ // note: this creates dupes. super bright light! (and super slow, too) - light_t l; - char buf[1024]; - char gah[256]; - const char *t; - float r, g, b; - FILE *f = fopen(fname.string().c_str(), "r"); +{ + // note: this creates dupes. super bright light! (and super slow, too) + std::string buf; + std::ifstream f(fname); + if (!f) return false; - while (!feof(f)) { - fgets(buf, sizeof(buf), f); - t = buf; - t = COM_Parse(buf); + while (!f.eof()) + { + std::getline(f, buf); + + const char *t = COM_Parse(buf.c_str()); + if (!t) continue; - entdict_t d = {}; - d.set("_surface", std::string(com_token)); + + entdict_t d {}; + d.set("_surface", com_token); t = COM_Parse(t); - r = atof(com_token); + float r = atof(com_token); t = COM_Parse(t); - g = atof(com_token); + float g = atof(com_token); t = COM_Parse(t); - b = atof(com_token); - snprintf(gah, sizeof(gah), "%f %f %f", r, g, b); - d.set("_color", std::string(gah)); + float b = atof(com_token); + d.set("_color", fmt::format("{} {} {}", r, g, b)); t = COM_Parse(t); - d.set("light", std::string(com_token)); + d.set("light", com_token); // might be hdr rgbi values here radlights.push_back(d); } - fclose(f); + return true; } @@ -1531,7 +1522,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp) if (surflight_dump) { surflights_dump_filename = mapfilename; surflights_dump_filename.replace_filename(surflights_dump_filename.filename().string() + "-surflights").replace_extension("map"); - surflights_dump_file = fopen(surflights_dump_filename.string().c_str(), "w"); + surflights_dump_file.open(surflights_dump_filename); } /* Create the surface lights */ @@ -1575,7 +1566,7 @@ static void MakeSurfaceLights(const mbsp_t *bsp) } if (surflights_dump_file) { - fclose(surflights_dump_file); + surflights_dump_file.close(); fmt::print("wrote surface lights to '{}'\n", surflights_dump_filename); } } diff --git a/light/imglib.cc b/light/imglib.cc index d0bd7e42..46ba4859 100644 --- a/light/imglib.cc +++ b/light/imglib.cc @@ -307,25 +307,25 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width int row, column; TargaHeader targa_header; - FILE *fin = fopen(filename.string().c_str(), "rb"); + qfile_t fin = SafeOpenRead(filename); if (!fin) { FLogPrint("Failed to load '{}'. File does not exist.\n", filename); return false; // mxd } - targa_header.id_length = fgetc(fin); - targa_header.colormap_type = fgetc(fin); - targa_header.image_type = fgetc(fin); + targa_header.id_length = fgetc(fin.get()); + targa_header.colormap_type = fgetc(fin.get()); + targa_header.image_type = fgetc(fin.get()); - targa_header.colormap_index = fgetLittleShort(fin); - targa_header.colormap_length = fgetLittleShort(fin); - targa_header.colormap_size = fgetc(fin); - targa_header.x_origin = fgetLittleShort(fin); - targa_header.y_origin = fgetLittleShort(fin); - targa_header.width = fgetLittleShort(fin); - targa_header.height = fgetLittleShort(fin); - targa_header.pixel_size = fgetc(fin); - targa_header.attributes = fgetc(fin); + targa_header.colormap_index = fgetLittleShort(fin.get()); + targa_header.colormap_length = fgetLittleShort(fin.get()); + targa_header.colormap_size = fgetc(fin.get()); + targa_header.x_origin = fgetLittleShort(fin.get()); + targa_header.y_origin = fgetLittleShort(fin.get()); + targa_header.width = fgetLittleShort(fin.get()); + targa_header.height = fgetLittleShort(fin.get()); + targa_header.pixel_size = fgetc(fin.get()); + targa_header.attributes = fgetc(fin.get()); if (targa_header.image_type != 2 && targa_header.image_type != 10) { FLogPrint("Failed to load '{}'. Only type 2 and 10 targa RGB images supported.\n", filename); @@ -350,7 +350,7 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width *pixels = targa_rgba; if (targa_header.id_length != 0) - fseek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment + SafeSeek(fin, targa_header.id_length, SEEK_CUR); // skip TARGA image comment if (targa_header.image_type == 2) { // Uncompressed, RGB images for (row = rows - 1; row >= 0; row--) { @@ -359,19 +359,19 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width unsigned char red, green, blue, alphabyte; switch (targa_header.pixel_size) { case 24: - blue = getc(fin); - green = getc(fin); - red = getc(fin); + blue = getc(fin.get()); + green = getc(fin.get()); + red = getc(fin.get()); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: - blue = getc(fin); - green = getc(fin); - red = getc(fin); - alphabyte = getc(fin); + blue = getc(fin.get()); + green = getc(fin.get()); + red = getc(fin.get()); + alphabyte = getc(fin.get()); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; @@ -388,21 +388,21 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width for (row = rows - 1; row >= 0; row--) { pixbuf = targa_rgba + row * columns * 4; for (column = 0; column < columns;) { - const unsigned char packetHeader = getc(fin); + const unsigned char packetHeader = getc(fin.get()); const unsigned char packetSize = 1 + (packetHeader & 0x7f); if (packetHeader & 0x80) { // run-length packet switch (targa_header.pixel_size) { case 24: - blue = getc(fin); - green = getc(fin); - red = getc(fin); + blue = getc(fin.get()); + green = getc(fin.get()); + red = getc(fin.get()); alphabyte = 255; break; case 32: - blue = getc(fin); - green = getc(fin); - red = getc(fin); - alphabyte = getc(fin); + blue = getc(fin.get()); + green = getc(fin.get()); + red = getc(fin.get()); + alphabyte = getc(fin.get()); break; default: FLogPrint("unsupported pixel size: {}\n", targa_header.pixel_size); // mxd @@ -428,19 +428,19 @@ bool LoadTGA(const std::filesystem::path &filename, uint8_t **pixels, int *width for (j = 0; j < packetSize; j++) { switch (targa_header.pixel_size) { case 24: - blue = getc(fin); - green = getc(fin); - red = getc(fin); + blue = getc(fin.get()); + green = getc(fin.get()); + red = getc(fin.get()); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; *pixbuf++ = 255; break; case 32: - blue = getc(fin); - green = getc(fin); - red = getc(fin); - alphabyte = getc(fin); + blue = getc(fin.get()); + green = getc(fin.get()); + red = getc(fin.get()); + alphabyte = getc(fin.get()); *pixbuf++ = red; *pixbuf++ = green; *pixbuf++ = blue; @@ -466,8 +466,6 @@ breakOut:; } } - fclose(fin); - return true; // mxd } diff --git a/light/light.cc b/light/light.cc index eb5a739a..8c558821 100644 --- a/light/light.cc +++ b/light/light.cc @@ -21,6 +21,8 @@ #include //#include #include +#include +#include #include #include @@ -341,9 +343,7 @@ static void FindModelInfo(const mbsp_t *bsp, const char *lmscaleoverride) modelinfo.push_back(info); /* Find the entity for the model */ - std::stringstream ss; - ss << "*" << i; - std::string modelname = ss.str(); + std::string modelname = fmt::format("*{}", i); const entdict_t *entdict = FindEntDictWithKeyPair("model", modelname); if (entdict == nullptr) @@ -501,7 +501,8 @@ static void LoadExtendedTexinfoFlags(const std::filesystem::path &sourcefilename std::filesystem::path filename(sourcefilename); filename.replace_extension("texinfo"); - FILE *texinfofile = fopen(filename.string().c_str(), "rt"); + qfile_t texinfofile = SafeOpenRead(filename); + if (!texinfofile) return; @@ -509,58 +510,56 @@ static void LoadExtendedTexinfoFlags(const std::filesystem::path &sourcefilename extended_flags_header_t header; - if (fread(&header, 1, sizeof(extended_flags_header_t), texinfofile) != sizeof(extended_flags_header_t) || + if (SafeRead(texinfofile, &header, sizeof(extended_flags_header_t)) != sizeof(extended_flags_header_t) || header.num_texinfo != bsp->numtexinfo || header.surfflags_size != sizeof(surfflags_t) || - fread(extended_texinfo_flags, sizeof(surfflags_t), header.num_texinfo, texinfofile) != header.num_texinfo) { + SafeRead(texinfofile, extended_texinfo_flags, sizeof(surfflags_t) * header.num_texinfo) != (sizeof(surfflags_t) * header.num_texinfo)) { LogPrint("WARNING: Extended texinfo flags in {} does not match bsp, ignoring\n", filename); - fclose(texinfofile); memset(extended_texinfo_flags, 0, bsp->numtexinfo * sizeof(uint32_t)); return; } - - fclose(texinfofile); } // obj -static FILE *InitObjFile(const std::filesystem::path &filename) +static qfile_t InitObjFile(const std::filesystem::path &filename) { std::filesystem::path objfilename(filename); objfilename.replace_extension("obj"); - FILE *objfile = fopen(objfilename.string().c_str(), "wt"); + qfile_t objfile = SafeOpenWrite(objfilename); + if (!objfile) FError("Failed to open {}: {}", objfilename, strerror(errno)); return objfile; } -static void ExportObjFace(FILE *f, const mbsp_t *bsp, const bsp2_dface_t *face, int *vertcount) +static void ExportObjFace(std::ofstream &f, const mbsp_t *bsp, const bsp2_dface_t *face, int *vertcount) { // export the vertices and uvs for (int i = 0; i < face->numedges; i++) { const int vertnum = Face_VertexAtIndex(bsp, face, i); const qvec3f normal = GetSurfaceVertexNormal(bsp, face, i); const float *pos = bsp->dvertexes[vertnum].point; - fprintf(f, "v %.9g %.9g %.9g\n", pos[0], pos[1], pos[2]); - fprintf(f, "vn %.9g %.9g %.9g\n", normal[0], normal[1], normal[2]); + fmt::print(f, "v {:.9} {:.9} {:.9}\n", pos[0], pos[1], pos[2]); + fmt::print(f, "vn {:.9} {:.9} {:.9}\n", normal[0], normal[1], normal[2]); } - fprintf(f, "f"); + f << "f"; for (int i = 0; i < face->numedges; i++) { // .obj vertexes start from 1 // .obj faces are CCW, quake is CW, so reverse the order const int vertindex = *vertcount + (face->numedges - 1 - i) + 1; - fprintf(f, " %d//%d", vertindex, vertindex); + fmt::print(f, " {}//{}", vertindex, vertindex); } - fprintf(f, "\n"); + f << '\n'; *vertcount += face->numedges; } static void ExportObj(const std::filesystem::path &filename, const mbsp_t *bsp) { - FILE *objfile = InitObjFile(filename); + std::ofstream objfile(filename); int vertcount = 0; const int start = bsp->dmodels[0].firstface; @@ -570,8 +569,6 @@ static void ExportObj(const std::filesystem::path &filename, const mbsp_t *bsp) ExportObjFace(objfile, bsp, BSP_GetFace(bsp, i), &vertcount); } - fclose(objfile); - LogPrint("Wrote {}\n", filename); } diff --git a/light/ltface.cc b/light/ltface.cc index 4be420c2..1ec1997a 100644 --- a/light/ltface.cc +++ b/light/ltface.cc @@ -31,6 +31,8 @@ #include #include #include +#include +#include using namespace std; @@ -547,7 +549,7 @@ position_t CalcPointNormal(const mbsp_t *bsp, const bsp2_dface_t *face, const qv // Dump points to a .map file static void CalcPoints_Debug(const lightsurf_t *surf, const mbsp_t *bsp) { - FILE *f = fopen("calcpoints.map", "w"); + std::ofstream f("calcpoints.map"); for (int t = 0; t < surf->height; t++) { for (int s = 0; s < surf->width; s++) { @@ -555,20 +557,18 @@ static void CalcPoints_Debug(const lightsurf_t *surf, const mbsp_t *bsp) const vec_t *point = surf->points[i]; const qvec3f mangle = mangle_from_vec(surf->normals[i]); - fprintf(f, "{\n"); - fprintf(f, "\"classname\" \"light\"\n"); - fprintf(f, "\"origin\" \"%f %f %f\"\n", point[0], point[1], point[2]); - fprintf(f, "\"mangle\" \"%f %f %f\"\n", mangle[0], mangle[1], mangle[2]); - fprintf(f, "\"face\" \"%d\"\n", surf->realfacenums[i]); - fprintf(f, "\"occluded\" \"%d\"\n", (int)surf->occluded[i]); - fprintf(f, "\"s\" \"%d\"\n", s); - fprintf(f, "\"t\" \"%d\"\n", t); - fprintf(f, "}\n"); + f << "{\n"; + f << "\"classname\" \"light\"\n"; + fmt::print(f, "\"origin\" \"{} {} {}\"\n", point[0], point[1], point[2]); + fmt::print(f, "\"mangle\" \"{} {} {}\"\n", mangle[0], mangle[1], mangle[2]); + fmt::print(f, "\"face\" \"{}\"\n", surf->realfacenums[i]); + fmt::print(f, "\"occluded\" \"{}\"\n", (int)surf->occluded[i]); + fmt::print(f, "\"s\" \"{}\"\n", s); + fmt::print(f, "\"t\" \"{}\"\n", t); + f << "}\n"; } } - fclose(f); - LogPrint("wrote face {}'s sample points ({}x{}) to calcpoints.map\n", Face_GetNum(bsp, surf->face), surf->width, surf->height); @@ -2644,16 +2644,14 @@ static float Lightmap_MaxBrightness(const lightmap_t *lm, const lightsurf_t *lig return maxb; } -static void WritePPM(std::string fname, int width, int height, const uint8_t *rgbdata) +static void WritePPM(const std::filesystem::path &fname, int width, int height, const uint8_t *rgbdata) { - FILE *file = fopen(fname.c_str(), "wb"); + qfile_t file = SafeOpenWrite(fname); // see: http://netpbm.sourceforge.net/doc/ppm.html - fprintf(file, "P6 %d %d 255 ", width, height); + fmt::print(file.get(), "P6 {} {} 255 ", width, height); int bytes = width * height * 3; - Q_assert(bytes == fwrite(rgbdata, 1, bytes, file)); - - fclose(file); + Q_assert(bytes == SafeWrite(file, rgbdata, bytes)); } static void DumpFullSizeLightmap(const mbsp_t *bsp, const lightsurf_t *lightsurf) @@ -2662,9 +2660,6 @@ static void DumpFullSizeLightmap(const mbsp_t *bsp, const lightsurf_t *lightsurf if (lm != nullptr) { int fnum = Face_GetNum(bsp, lightsurf->face); - char fname[1024]; - sprintf(fname, "face%04d.ppm", fnum); - std::vector rgbdata; for (int i = 0; i < lightsurf->numpoints; i++) { const vec_t *color = lm->samples[i].color; @@ -2679,7 +2674,7 @@ static void DumpFullSizeLightmap(const mbsp_t *bsp, const lightsurf_t *lightsurf Q_assert(lightsurf->numpoints == (oversampled_height * oversampled_width)); - WritePPM(std::string{fname}, oversampled_width, oversampled_height, rgbdata.data()); + WritePPM(fmt::format("face{:04}.ppm", fnum), oversampled_width, oversampled_height, rgbdata.data()); } } @@ -2702,8 +2697,6 @@ static void DumpGLMVector(std::string fname, std::vector vec, int width, static void DumpDownscaledLightmap(const mbsp_t *bsp, const bsp2_dface_t *face, int w, int h, const vec3_t *colors) { int fnum = Face_GetNum(bsp, face); - char fname[1024]; - sprintf(fname, "face-small%04d.ppm", fnum); std::vector rgbdata; for (int i = 0; i < (w * h); i++) { @@ -2713,7 +2706,7 @@ static void DumpDownscaledLightmap(const mbsp_t *bsp, const bsp2_dface_t *face, } } - WritePPM(std::string{fname}, w, h, rgbdata.data()); + WritePPM(fmt::format("face-small{:04}.ppm", fnum), w, h, rgbdata.data()); } static std::vector LightmapColorsToGLMVector(const lightsurf_t *lightsurf, const lightmap_t *lm) diff --git a/light/trace_embree.cc b/light/trace_embree.cc index 2af35c7c..3fa45df7 100644 --- a/light/trace_embree.cc +++ b/light/trace_embree.cc @@ -422,64 +422,6 @@ static void Embree_FilterFuncN(const struct RTCFilterFunctionNArguments *args) // building faces for skip-textured bmodels -#if 0 - -static FILE * -InitObjFile(const char *filename) -{ - FILE *objfile; - char objfilename[1024]; - strcpy(objfilename, filename); - StripExtension(objfilename); - DefaultExtension(objfilename, ".obj"); - - objfile = fopen(objfilename, "wt"); - if (!objfile) - FError("Failed to open {}: {}", objfilename, strerror(errno)); - - return objfile; -} - -static void -ExportObjFace(FILE *f, const winding_t *winding, int *vertcount) -{ -// plane_t plane; -// WindingPlane(winding, plane.normal, &plane.dist); - - // export the vertices and uvs - for (int i=0; inumpoints; i++) - { - fprintf(f, "v %.9g %.9g %.9g\n", winding->p[i][0], winding->p[i][1], winding->p[i][2]); -// fprintf(f, "vn %.9g %.9g %.9g\n", plane.normal[0], plane.normal[1], plane.normal[2]); - } - - fprintf(f, "f"); - for (int i=0; inumpoints; i++) { - // .obj vertexes start from 1 - // .obj faces are CCW, quake is CW, so reverse the order - const int vertindex = *vertcount + (winding->numpoints - 1 - i) + 1; - fprintf(f, " %d//%d", vertindex, vertindex); - } - fprintf(f, "\n"); - - *vertcount += winding->numpoints; -} - -static void -ExportObj(const char *filename, const vector &windings) -{ - FILE *objfile = InitObjFile(filename); - int vertcount = 0; - - for (const auto &winding : windings) { - ExportObjFace(objfile, winding, &vertcount); - } - - fclose(objfile); -} - -#endif - plane_t Node_Plane(const mbsp_t *bsp, const bsp2_dnode_t *node, bool side) { const dplane_t *dplane = &bsp->dplanes[node->planenum]; diff --git a/qbsp/brush.cc b/qbsp/brush.cc index 97c13a07..e4493e4c 100644 --- a/qbsp/brush.cc +++ b/qbsp/brush.cc @@ -1202,9 +1202,7 @@ void Brush_LoadEntity(mapentity_t *dst, const mapentity_t *src, const int hullnu VectorAdd(brush->mins, brush->maxs, origin); VectorScale(origin, 0.5, origin); - char value[1024]; - snprintf(value, sizeof(value), "%.2f %.2f %.2f", origin[0], origin[1], origin[2]); - SetKeyValue(dst, "origin", value); + SetKeyValue(dst, "origin", VecStrf(origin).c_str()); VectorCopy(origin, rotate_offset); rottype = rotation_t::origin_brush; diff --git a/qbsp/exportobj.cc b/qbsp/exportobj.cc index a22f859c..2207daa4 100644 --- a/qbsp/exportobj.cc +++ b/qbsp/exportobj.cc @@ -22,29 +22,27 @@ #include #include +#include +#include -static FILE *InitObjFile(const std::string &filesuffix) +static std::ofstream InitObjFile(const std::string &filesuffix) { - FILE *objfile; - std::filesystem::path name = options.szBSPName; name.replace_filename(options.szBSPName.filename().string() + "_" + filesuffix).replace_extension("obj"); - objfile = fopen(name.string().c_str(), "wt"); + std::ofstream objfile(name); if (!objfile) FError("Failed to open {}: {}", options.szBSPName, strerror(errno)); return objfile; } -static FILE *InitMtlFile(const std::string &filesuffix) +static std::ofstream InitMtlFile(const std::string &filesuffix) { - FILE *file; - std::filesystem::path name = options.szBSPName; name.replace_filename(options.szBSPName.filename().string() + "_" + filesuffix).replace_extension("mtl"); - file = fopen(name.string().c_str(), "wt"); + std::ofstream file(name); if (!file) FError("Failed to open {}: {}", options.szBSPName, strerror(errno)); @@ -61,7 +59,7 @@ static void GetUV(const mtexinfo_t *texinfo, const vec_t *pos, const int width, height; } -static void ExportObjFace(FILE *f, FILE *mtlF, const face_t *face, int *vertcount) +static void ExportObjFace(std::ofstream &f, const face_t *face, int *vertcount) { const mtexinfo_t &texinfo = map.mtexinfos.at(face->texinfo); const char *texname = map.miptexTextureName(texinfo.miptex).c_str(); @@ -73,42 +71,41 @@ static void ExportObjFace(FILE *f, FILE *mtlF, const face_t *face, int *vertcoun // export the vertices and uvs for (int i = 0; i < face->w.numpoints; i++) { const vec_t *pos = face->w.points[i]; - fprintf(f, "v %.9g %.9g %.9g\n", pos[0], pos[1], pos[2]); + fmt::print(f, "v {:.9} {:.9} {:.9}\n", pos[0], pos[1], pos[2]); vec_t u, v; GetUV(&texinfo, pos, width, height, &u, &v); // not sure why -v is needed, .obj uses (0, 0) in the top left apparently? - fprintf(f, "vt %.9g %.9g\n", u, -v); + fmt::print(f, "vt {:.9} {:.9}\n", u, -v); } - // fprintf(f, "usemtl %s\n", texname); - fprintf(f, "usemtl contents%d_%d\n", face->contents[1].native, face->contents->extended); - fprintf(f, "f"); + fmt::print(f, "usemtl contents{}_{}\n", face->contents[0].native, face->contents[0].extended); + f << 'f'; for (int i = 0; i < face->w.numpoints; i++) { // .obj vertexes start from 1 // .obj faces are CCW, quake is CW, so reverse the order const int vertindex = *vertcount + (face->w.numpoints - 1 - i) + 1; - fprintf(f, " %d/%d", vertindex, vertindex); + fmt::print(f, " {}/{}", vertindex, vertindex); } - fprintf(f, "\n"); + f << '\n'; *vertcount += face->w.numpoints; } -static void WriteContentsMaterial(FILE *mtlf, contentflags_t contents, float r, float g, float b) +static void WriteContentsMaterial(std::ofstream &mtlf, contentflags_t contents, float r, float g, float b) { - fprintf(mtlf, "newmtl contents%d_%d\n", contents.native, contents.extended); - fprintf(mtlf, "Ka 0 0 0\n"); - fprintf(mtlf, "Kd %f %f %f\n", r, g, b); - fprintf(mtlf, "Ks 0 0 0\n"); - fprintf(mtlf, "illum 0\n"); + fmt::print(mtlf, "newmtl contents{}_{}\n", contents.native, contents.extended); + mtlf << "Ka 0 0 0\n"; + fmt::print(mtlf, "Kd {} {} {}\n", r, g, b); + mtlf << "Ks 0 0 0\n"; + mtlf << "illum 0\n"; } void ExportObj_Faces(const std::string &filesuffix, const std::vector &faces) { - FILE *objfile = InitObjFile(filesuffix); - FILE *mtlfile = InitMtlFile(filesuffix); + std::ofstream objfile = InitObjFile(filesuffix); + std::ofstream mtlfile = InitMtlFile(filesuffix); WriteContentsMaterial(mtlfile, {}, 0, 0, 0); WriteContentsMaterial(mtlfile, {CONTENTS_EMPTY}, 0, 1, 0); @@ -126,11 +123,8 @@ void ExportObj_Faces(const std::string &filesuffix, const std::vector &brushes) diff --git a/qbsp/map.cc b/qbsp/map.cc index d8f1efba..9fbe145f 100644 --- a/qbsp/map.cc +++ b/qbsp/map.cc @@ -25,11 +25,12 @@ #include #include -#include #include #include #include #include +#include +#include #include #include @@ -482,35 +483,42 @@ static surfflags_t SurfFlagsForEntity(const mtexinfo_t &texinfo, const mapentity static void ParseEpair(parser_t *parser, mapentity_t *entity) { - epair_t *epair = new epair_t { }; - epair->next = entity->epairs; - entity->epairs = epair; - if (strlen(parser->token) >= MAX_KEY - 1) goto parse_error; - epair->key = copystring(parser->token); - ParseToken(parser, PARSE_SAMELINE); - if (strlen(parser->token) >= MAX_VALUE - 1) - goto parse_error; - epair->value = copystring(parser->token); - if (!Q_strcasecmp(epair->key, "origin")) { - GetVectorForKey(entity, epair->key, entity->origin); - } else if (!Q_strcasecmp(epair->key, "classname")) { - if (!Q_strcasecmp(epair->value, "info_player_start")) { - // Quake II uses multiple starts for level transitions/backtracking. - // TODO: instead, this should check targetnames. There should only be - // one info_player_start per targetname in Q2. - if (options.target_game->id != GAME_QUAKE_II && (rgfStartSpots & info_player_start)) - LogPrint("WARNING: Multiple info_player_start entities\n"); - rgfStartSpots |= info_player_start; - } else if (!Q_strcasecmp(epair->value, "info_player_deathmatch")) { - rgfStartSpots |= info_player_deathmatch; - } else if (!Q_strcasecmp(epair->value, "info_player_coop")) { - rgfStartSpots |= info_player_coop; + { + std::string key(parser->token); + + ParseToken(parser, PARSE_SAMELINE); + + if (strlen(parser->token) >= MAX_VALUE - 1) + goto parse_error; + + { + std::string &value = (entity->epairs[key] = parser->token); + + if (!Q_strcasecmp(key.c_str(), "origin")) { + GetVectorForKey(entity, key.c_str(), entity->origin); + } else if (!Q_strcasecmp(key.c_str(), "classname")) { + if (!Q_strcasecmp(value.c_str(), "info_player_start")) { + // Quake II uses multiple starts for level transitions/backtracking. + // TODO: instead, this should check targetnames. There should only be + // one info_player_start per targetname in Q2. + if (options.target_game->id != GAME_QUAKE_II && (rgfStartSpots & info_player_start)) + LogPrint("WARNING: Multiple info_player_start entities\n"); + rgfStartSpots |= info_player_start; + } else if (!Q_strcasecmp(value.c_str(), "info_player_deathmatch")) { + rgfStartSpots |= info_player_deathmatch; + } else if (!Q_strcasecmp(value.c_str(), "info_player_coop")) { + rgfStartSpots |= info_player_coop; + } + } + + entity->epair_order.push_back(std::move(key)); + + return; } } - return; parse_error: FError("line {}: Entity key or value too long", parser->linenum); @@ -1988,7 +1996,7 @@ void LoadMapFile(void) } } // Remove dummy entity inserted above - assert(map.entities.back().epairs == nullptr); + assert(!map.entities.back().epairs.size()); assert(map.entities.back().numbrushes == 0); map.entities.pop_back(); @@ -2046,20 +2054,20 @@ static texdef_valve_t TexDef_BSPToValve(const stvecs &in_vecs) return res; } -static void fprintDoubleAndSpc(FILE *f, double v) +static void fprintDoubleAndSpc(std::ofstream &f, double v) { int rounded = rint(v); if (static_cast(rounded) == v) { - fprintf(f, "%d ", rounded); + fmt::print(f, "{} ", rounded); } else if (std::isfinite(v)) { - fprintf(f, "%0.17g ", v); + fmt::print(f, "{:0.17} ", v); } else { printf("WARNING: suppressing nan or infinity\n"); - fprintf(f, "0 "); + f << "0 "; } } -static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t format) +static void ConvertMapFace(std::ofstream &f, const mapface_t &mapface, const conversion_t format) { EnsureTexturesLoaded(); const texture_t *texture = WADList_GetTexture(mapface.texname.c_str()); @@ -2068,11 +2076,11 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t // Write plane points for (int i = 0; i < 3; i++) { - fprintf(f, " ( "); + f << " ( "; for (int j = 0; j < 3; j++) { fprintDoubleAndSpc(f, mapface.planepts[i][j]); } - fprintf(f, ") "); + f << ") "; } switch (format) { @@ -2081,7 +2089,7 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t const texdef_quake_ed_t quakeed = TexDef_BSPToQuakeEd(mapface.plane, texture, texinfo.vecs, mapface.planepts); - fprintf(f, "%s ", mapface.texname.c_str()); + fmt::print(f, "{} ", mapface.texname); fprintDoubleAndSpc(f, quakeed.shift[0]); fprintDoubleAndSpc(f, quakeed.shift[1]); fprintDoubleAndSpc(f, quakeed.rotate); @@ -2089,7 +2097,8 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t fprintDoubleAndSpc(f, quakeed.scale[1]); if (format == conversion_t::quake2) { - fprintf(f, "0 0 0"); + // TODO?? + f << "0 0 0"; } break; @@ -2097,19 +2106,21 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t case conversion_t::valve: { const texdef_valve_t valve = TexDef_BSPToValve(texinfo.vecs); - fprintf(f, "%s [ ", mapface.texname.c_str()); + fmt::print(f, "{} [ ", mapface.texname); fprintDoubleAndSpc(f, valve.axis[0][0]); fprintDoubleAndSpc(f, valve.axis[0][1]); fprintDoubleAndSpc(f, valve.axis[0][2]); fprintDoubleAndSpc(f, valve.shift[0]); - fprintf(f, "] [ "); + f << "] [ "; fprintDoubleAndSpc(f, valve.axis[1][0]); fprintDoubleAndSpc(f, valve.axis[1][1]); fprintDoubleAndSpc(f, valve.axis[1][2]); fprintDoubleAndSpc(f, valve.shift[1]); - fprintf(f, "] 0 "); + f << "] 0 "; fprintDoubleAndSpc(f, valve.scale[0]); fprintDoubleAndSpc(f, valve.scale[1]); + + // TODO: Q2 break; } case conversion_t::bp: { @@ -2118,58 +2129,53 @@ static void ConvertMapFace(FILE *f, const mapface_t &mapface, const conversion_t texSize[1] = texture ? texture->height : 64; const texdef_brush_primitives_t bp = TexDef_BSPToBrushPrimitives(mapface.plane, texSize, texinfo.vecs); - fprintf(f, "( ( "); + f << "( ( "; fprintDoubleAndSpc(f, bp.texMat[0][0]); fprintDoubleAndSpc(f, bp.texMat[0][1]); fprintDoubleAndSpc(f, bp.texMat[0][2]); - fprintf(f, ") ( "); + f << ") ( "; fprintDoubleAndSpc(f, bp.texMat[1][0]); fprintDoubleAndSpc(f, bp.texMat[1][1]); fprintDoubleAndSpc(f, bp.texMat[1][2]); // N.B.: always print the Q2/Q3 flags - fprintf(f, ") ) %s 0 0 0", mapface.texname.c_str()); + fmt::print(f, ") ) {} 0 0 0", mapface.texname); break; } default: FError("Internal error: unknown texcoord_style_t\n"); } - fprintf(f, "\n"); + f << '\n'; } -static void ConvertMapBrush(FILE *f, const mapbrush_t &mapbrush, const conversion_t format) +static void ConvertMapBrush(std::ofstream &f, const mapbrush_t &mapbrush, const conversion_t format) { - fprintf(f, "{\n"); + f << "{\n"; if (format == conversion_t::bp) { - fprintf(f, "brushDef\n"); - fprintf(f, "{\n"); + f << "brushDef\n"; + f << "{\n"; } for (int i = 0; i < mapbrush.numfaces; i++) { ConvertMapFace(f, mapbrush.face(i), format); } if (format == conversion_t::bp) { - fprintf(f, "}\n"); + f << "}\n"; } - fprintf(f, "}\n"); + f << "}\n"; } -static void ConvertEntity(FILE *f, const mapentity_t *entity, const conversion_t format) +static void ConvertEntity(std::ofstream &f, const mapentity_t *entity, const conversion_t format) { - fprintf(f, "{\n"); + f << "{\n"; - // put the epairs in a temporary list to reverse the order, so we can print them in the same order as the .MAP file - std::list> epairs; - for (const epair_t *epair = entity->epairs; epair; epair = epair->next) { - epairs.push_front(std::make_pair(std::string(epair->key), std::string(epair->value))); - } - for (const auto &epair : epairs) { - fprintf(f, "\"%s\" \"%s\"\n", epair.first.c_str(), epair.second.c_str()); + for (const auto &key : entity->epair_order) { + fmt::print(f, "\"{}\" \"{}\"\n", key, entity->epairs.at(key)); } for (int i = 0; i < entity->nummapbrushes; i++) { ConvertMapBrush(f, entity->mapbrush(i), format); } - fprintf(f, "}\n"); + f << "}\n"; } void ConvertMapFile(void) @@ -2189,16 +2195,15 @@ void ConvertMapFile(void) std::filesystem::path filename = options.szBSPName; filename.replace_filename(options.szBSPName.filename().string() + append); - FILE *f = fopen(filename.string().c_str(), "wb"); - if (f == nullptr) + std::ofstream f(filename); + + if (!f) FError("Couldn't open file\n"); for (const mapentity_t &entity : map.entities) { ConvertEntity(f, &entity, options.convertMapFormat); } - fclose(f); - LogPrint("Conversion saved to {}\n", filename); options.fVerbose = false; @@ -2206,39 +2211,23 @@ void ConvertMapFile(void) void PrintEntity(const mapentity_t *entity) { - epair_t *epair; - - for (epair = entity->epairs; epair; epair = epair->next) - LogPrint(LOG_STAT, " {:20} : {}\n", epair->key, epair->value); + for (auto &epair : entity->epairs) + LogPrint(LOG_STAT, " {:20} : {}\n", epair.first, epair.second); } const char *ValueForKey(const mapentity_t *entity, const char *key) { - const epair_t *ep; + auto it = entity->epairs.find(key); - for (ep = entity->epairs; ep; ep = ep->next) - if (!Q_strcasecmp(ep->key, key)) - return ep->value; + if (it == entity->epairs.end()) + return ""; - return ""; + return it->second.c_str(); } void SetKeyValue(mapentity_t *entity, const char *key, const char *value) { - epair_t *ep; - - for (ep = entity->epairs; ep; ep = ep->next) - if (!Q_strcasecmp(ep->key, key)) { - delete[] ep->value; /* FIXME */ - ep->value = copystring(value); - return; - } - - ep = new epair_t { }; - ep->next = entity->epairs; - entity->epairs = ep; - ep->key = copystring(key); - ep->value = copystring(value); + entity->epairs[key] = value; } /** @@ -2262,32 +2251,31 @@ int GetVectorForKey(const mapentity_t *entity, const char *szKey, vec3_t vec) void WriteEntitiesToString(void) { - epair_t *ep; int i; mapentity_t *entity; - std::stringstream ss; + std::string ss; for (i = 0; i < map.numentities(); i++) { entity = &map.entities.at(i); /* Check if entity needs to be removed */ - if (!entity->epairs || IsWorldBrushEntity(entity)) { + if (!entity->epairs.size() || IsWorldBrushEntity(entity)) { continue; } - ss << "{\n"; + ss += "{\n"; - for (ep = entity->epairs; ep; ep = ep->next) { + for (auto &ep : entity->epairs) { // Limit on Quake's strings of 128 bytes // TODO: Warn when limit is exceeded - ss << "\"" << std::string(ep->key) << "\" \"" << std::string(ep->value) << "\"\n"; + fmt::format_to(std::back_inserter(ss), "\"{}\" \"{}\"\n", ep.first, ep.second); } - ss << "}\n"; + ss += "}\n"; } - map.exported_entities = ss.str(); + map.exported_entities = std::move(ss); } //==================================================================== @@ -2299,19 +2287,18 @@ WriteBspBrushMap from q3map ================== */ -void WriteBspBrushMap(const char *name, const std::vector &list) +void WriteBspBrushMap(const std::filesystem::path &name, const std::vector &list) { - FILE *f; - LogPrint("writing {}\n", name); - f = fopen(name, "wb"); + std::ofstream f(name); + if (!f) FError("Can't write {}", name); - fprintf(f, "{\n\"classname\" \"worldspawn\"\n"); + fmt::print(f, "{\n\"classname\" \"worldspawn\"\n"); for (const brush_t *brush : list) { - fprintf(f, "{\n"); + fmt::print(f, "{\n"); for (const face_t *face = brush->faces; face; face = face->next) { // FIXME: Factor out this mess qbsp_plane_t plane = map.planes.at(face->planenum); @@ -2322,19 +2309,19 @@ void WriteBspBrushMap(const char *name, const std::vector &list winding_t *w = BaseWindingForPlane(&plane); - fprintf(f, "( %g %g %g ) ", w->points[0][0], w->points[0][1], w->points[0][2]); - fprintf(f, "( %g %g %g ) ", w->points[1][0], w->points[1][1], w->points[1][2]); - fprintf(f, "( %g %g %g ) ", w->points[2][0], w->points[2][1], w->points[2][2]); + fmt::print(f, "( {} {} {} ) ", w->points[0][0], w->points[0][1], w->points[0][2]); + fmt::print(f, "( {} {} {} ) ", w->points[1][0], w->points[1][1], w->points[1][2]); + fmt::print(f, "( {} {} {} ) ", w->points[2][0], w->points[2][1], w->points[2][2]); - fprintf(f, "notexture 0 0 0 1 1\n"); + fmt::print(f, "notexture 0 0 0 1 1\n"); free(w); } - fprintf(f, "}\n"); - } - fprintf(f, "}\n"); - fclose(f); + fmt::print(f, "}\n"); + } + + fmt::print(f, "}\n"); } /* diff --git a/qbsp/outside.cc b/qbsp/outside.cc index 4caca224..9be6d557 100644 --- a/qbsp/outside.cc +++ b/qbsp/outside.cc @@ -25,6 +25,8 @@ #include #include #include +#include +#include /* =========== @@ -45,13 +47,11 @@ node_t *PointInLeaf(node_t *node, const vec3_t point) return node; } -static FILE *InitPtsFile(void) +static std::ofstream InitPtsFile(void) { - FILE *ptsfile; - options.szBSPName.replace_extension("pts"); - ptsfile = fopen(options.szBSPName.string().c_str(), "wt"); + std::ofstream ptsfile(options.szBSPName); if (!ptsfile) FError("Failed to open {}: {}", options.szBSPName, strerror(errno)); @@ -191,7 +191,7 @@ static std::pair, node_t *> MakeLeakLine(node_t *outleaf WriteLeakTrail =============== */ -static void WriteLeakTrail(FILE *leakfile, const vec3_t point1, const vec3_t point2) +static void WriteLeakTrail(std::ofstream &leakfile, const vec3_t point1, const vec3_t point2) { vec3_t vector, trail; vec_t dist; @@ -201,7 +201,7 @@ static void WriteLeakTrail(FILE *leakfile, const vec3_t point1, const vec3_t poi VectorCopy(point1, trail); while (dist > options.dxLeakDist) { - fprintf(leakfile, "%f %f %f\n", trail[0], trail[1], trail[2]); + fmt::print(leakfile, "{} {} {}\n", trail[0], trail[1], trail[2]); VectorMA(trail, options.dxLeakDist, vector, trail); dist -= options.dxLeakDist; } @@ -209,7 +209,7 @@ static void WriteLeakTrail(FILE *leakfile, const vec3_t point1, const vec3_t poi static void WriteLeakLine(const std::pair, node_t *> &leakline) { - FILE *ptsfile = InitPtsFile(); + std::ofstream ptsfile = InitPtsFile(); vec3_t prevpt, currpt; VectorCopy(leakline.second->occupant->origin, prevpt); @@ -224,7 +224,6 @@ static void WriteLeakLine(const std::pair, node_t *> &le VectorCopy(currpt, prevpt); } - fclose(ptsfile); LogPrint("Leak file written to {}\n", options.szBSPName); } diff --git a/qbsp/portals.cc b/qbsp/portals.cc index 30937b6e..f7f7ae87 100644 --- a/qbsp/portals.cc +++ b/qbsp/portals.cc @@ -20,6 +20,8 @@ */ // portals.c +#include +#include #include node_t outside_node; // portals outside the world face this @@ -44,12 +46,12 @@ PORTAL FILE GENERATION static void PlaneFromWinding(const winding_t *w, qbsp_plane_t *plane); -static void WriteFloat(FILE *portalFile, vec_t v) +static void WriteFloat(std::ofstream &portalFile, vec_t v) { if (fabs(v - Q_rint(v)) < ZERO_EPSILON) - fprintf(portalFile, "%d ", (int)Q_rint(v)); + fmt::print(portalFile, "{} ", (int)Q_rint(v)); else - fprintf(portalFile, "%f ", v); + fmt::print(portalFile, "{} ", v); } static contentflags_t ClusterContents(const node_t *node) @@ -96,7 +98,7 @@ static bool PortalThru(const portal_t *p) return options.target_game->portal_can_see_through(contents0, contents1); } -static void WritePortals_r(node_t *node, FILE *portalFile, bool clusters) +static void WritePortals_r(node_t *node, std::ofstream &portalFile, bool clusters) { const portal_t *p, *next; const winding_t *w; @@ -131,22 +133,22 @@ static void WritePortals_r(node_t *node, FILE *portalFile, bool clusters) pl = &map.planes[p->planenum]; PlaneFromWinding(w, &plane2); if (DotProduct(pl->normal, plane2.normal) < 1.0 - ANGLEEPSILON) - fprintf(portalFile, "%d %d %d ", w->numpoints, back, front); + fmt::print(portalFile, "{} {} {} ", w->numpoints, back, front); else - fprintf(portalFile, "%d %d %d ", w->numpoints, front, back); + fmt::print(portalFile, "{} {} {} ", w->numpoints, front, back); for (i = 0; i < w->numpoints; i++) { - fprintf(portalFile, "("); + fmt::print(portalFile, "("); WriteFloat(portalFile, w->points[i][0]); WriteFloat(portalFile, w->points[i][1]); WriteFloat(portalFile, w->points[i][2]); - fprintf(portalFile, ") "); + fmt::print(portalFile, ") "); } - fprintf(portalFile, "\n"); + fmt::print(portalFile, "\n"); } } -static int WriteClusters_r(node_t *node, FILE *portalFile, int viscluster) +static int WriteClusters_r(node_t *node, std::ofstream &portalFile, int viscluster) { if (node->planenum != PLANENUM_LEAF) { viscluster = WriteClusters_r(node->children[0], portalFile, viscluster); @@ -158,7 +160,7 @@ static int WriteClusters_r(node_t *node, FILE *portalFile, int viscluster) /* If we're in the next cluster, start a new line */ if (node->viscluster != viscluster) { - fprintf(portalFile, "-1\n"); + fmt::print(portalFile, "-1\n"); viscluster++; } @@ -166,7 +168,7 @@ static int WriteClusters_r(node_t *node, FILE *portalFile, int viscluster) if (node->viscluster != viscluster) FError("Internal error: Detail cluster mismatch"); - fprintf(portalFile, "%d ", node->visleafnum); + fmt::print(portalFile, "%d ", node->visleafnum); return viscluster; } @@ -232,7 +234,6 @@ WritePortalfile static void WritePortalfile(node_t *headnode, portal_state_t *state) { int check; - FILE *portalFile; /* * Set the visleafnum and viscluster field in every leaf and count the @@ -247,38 +248,36 @@ static void WritePortalfile(node_t *headnode, portal_state_t *state) // write the file options.szBSPName.replace_extension("prt"); - portalFile = fopen(options.szBSPName.string().c_str(), "wt"); + std::ofstream portalFile(options.szBSPName); if (!portalFile) FError("Failed to open {}: {}", options.szBSPName, strerror(errno)); /* If no detail clusters, just use a normal PRT1 format */ if (!state->uses_detail) { - fprintf(portalFile, "PRT1\n"); - fprintf(portalFile, "%d\n", state->num_visleafs); - fprintf(portalFile, "%d\n", state->num_visportals); + fmt::print(portalFile, "PRT1\n"); + fmt::print(portalFile, "{}\n", state->num_visleafs); + fmt::print(portalFile, "{}\n", state->num_visportals); WritePortals_r(headnode, portalFile, false); } else { if (options.fForcePRT1) { /* Write a PRT1 file for loading in the map editor. Vis will reject it. */ - fprintf(portalFile, "PRT1\n"); - fprintf(portalFile, "%d\n", state->num_visclusters); - fprintf(portalFile, "%d\n", state->num_visportals); + fmt::print(portalFile, "PRT1\n"); + fmt::print(portalFile, "{}\n", state->num_visclusters); + fmt::print(portalFile, "{}\n", state->num_visportals); WritePortals_r(headnode, portalFile, true); } else { /* Write a PRT2 */ - fprintf(portalFile, "PRT2\n"); - fprintf(portalFile, "%d\n", state->num_visleafs); - fprintf(portalFile, "%d\n", state->num_visclusters); - fprintf(portalFile, "%d\n", state->num_visportals); + fmt::print(portalFile, "PRT2\n"); + fmt::print(portalFile, "{}\n", state->num_visleafs); + fmt::print(portalFile, "{}\n", state->num_visclusters); + fmt::print(portalFile, "{}\n", state->num_visportals); WritePortals_r(headnode, portalFile, true); check = WriteClusters_r(headnode, portalFile, 0); if (check != state->num_visclusters - 1) FError("Internal error: Detail cluster mismatch"); - fprintf(portalFile, "-1\n"); + fmt::print(portalFile, "-1\n"); } } - - fclose(portalFile); } //============================================================================= diff --git a/qbsp/wad.cc b/qbsp/wad.cc index 9b344d9b..411876eb 100644 --- a/qbsp/wad.cc +++ b/qbsp/wad.cc @@ -64,7 +64,7 @@ static bool WAD_LoadInfo(wad_t &wad, bool external) external |= options.fNoTextures; - len = fread(hdr, 1, sizeof(wadinfo_t), wad.file); + len = SafeRead(wad.file, hdr, sizeof(wadinfo_t)); if (len != sizeof(wadinfo_t)) return false; @@ -76,21 +76,21 @@ static bool WAD_LoadInfo(wad_t &wad, bool external) if (!wad.version) return false; - fseek(wad.file, hdr->infotableofs, SEEK_SET); + SafeSeek(wad.file, hdr->infotableofs, SEEK_SET); wad.lumps.reserve(wad.header.numlumps); /* Get the dimensions and make a texture_t */ for (i = 0; i < wad.header.numlumps; i++) { lumpinfo_t lump; - len = fread(&lump, 1, sizeof(lump), wad.file); + len = SafeRead(wad.file, &lump, sizeof(lump)); if (len != sizeof(lump)) return false; - auto restore_pos = ftell(wad.file); + auto restore_pos = SafeTell(wad.file); - fseek(wad.file, lump.filepos, SEEK_SET); - len = fread(&miptex, 1, sizeof(miptex), wad.file); + SafeSeek(wad.file, lump.filepos, SEEK_SET); + len = SafeRead(wad.file, &miptex, sizeof(miptex)); if (len == sizeof(miptex)) { int w = LittleLong(miptex.width); @@ -116,7 +116,7 @@ static bool WAD_LoadInfo(wad_t &wad, bool external) } else lump.size = 0; - fseek(wad.file, restore_pos, SEEK_SET); + SafeSeek(wad.file, restore_pos, SEEK_SET); wad.lumps.insert({lump.name, lump}); } @@ -128,20 +128,19 @@ static void WADList_OpenWad(const std::filesystem::path &fpath, bool external) { wad_t wad; - wad.file = fopen(fpath.string().c_str(), "rb"); + wad.file = SafeOpenRead(fpath); if (wad.file) { if (options.fVerbose) LogPrint("Opened WAD: {}\n", fpath); if (WAD_LoadInfo(wad, external)) { - wadlist.push_front(wad); - wad.file = nullptr; // wadlist now owns this file handle + wadlist.emplace_front(std::move(wad)); return; } LogPrint("WARNING: {} isn't a wadfile\n", fpath); - fclose(wad.file); + wad.file.reset(); } else { // Message? } @@ -207,10 +206,10 @@ static int WAD_LoadLump(const wad_t &wad, const char *name, uint8_t *dest) auto &lump = it->second; - fseek(wad.file, lump.filepos, SEEK_SET); + SafeSeek(wad.file, lump.filepos, SEEK_SET); if (lump.disksize == sizeof(dmiptex_t)) { - size = fread(dest, 1, sizeof(dmiptex_t), wad.file); + size = SafeRead(wad.file, dest, sizeof(dmiptex_t)); if (size != sizeof(dmiptex_t)) FError("Failure reading from file"); for (i = 0; i < MIPLEVELS; i++) @@ -221,7 +220,7 @@ static int WAD_LoadLump(const wad_t &wad, const char *name, uint8_t *dest) if (lump.size != lump.disksize) { LogPrint("Texture {} is {} bytes in wad, packed to {} bytes in bsp\n", name, lump.disksize, lump.size); std::vector data(lump.disksize); - size = fread(data.data(), 1, lump.disksize, wad.file); + size = SafeRead(wad.file, data.data(), lump.disksize); if (size != lump.disksize) FError("Failure reading from file"); auto out = (dmiptex_t *)dest; @@ -249,7 +248,7 @@ static int WAD_LoadLump(const wad_t &wad, const char *name, uint8_t *dest) memcpy(dest + palofs + 2, thepalette, 3 * 256); // FIXME: quake palette or something. } } else { - size = fread(dest, 1, lump.disksize, wad.file); + size = SafeRead(wad.file, dest, lump.disksize); if (size != lump.disksize) FError("Failure reading from file"); } diff --git a/qbsp/writebsp.cc b/qbsp/writebsp.cc index 2dbe50c7..2afabd86 100644 --- a/qbsp/writebsp.cc +++ b/qbsp/writebsp.cc @@ -345,10 +345,9 @@ static void WriteExtendedTexinfoFlags(void) std::sort(texinfos_sorted.begin(), texinfos_sorted.end(), [](const mtexinfo_t &a, const mtexinfo_t &b) { return a.outputnum < b.outputnum; }); - FILE *texinfofile; options.szBSPName.replace_extension("texinfo"); - texinfofile = fopen(options.szBSPName.string().c_str(), "wt"); + qfile_t texinfofile = SafeOpenWrite(options.szBSPName); if (!texinfofile) FError("Failed to open {}: {}", options.szBSPName, strerror(errno)); @@ -357,7 +356,7 @@ static void WriteExtendedTexinfoFlags(void) header.num_texinfo = map.exported_texinfos.size(); header.surfflags_size = sizeof(surfflags_t); - fwrite(&header, 1, sizeof(header), texinfofile); + SafeWrite(texinfofile, &header, sizeof(header)); int count = 0; for (const auto &tx : texinfos_sorted) { @@ -366,12 +365,10 @@ static void WriteExtendedTexinfoFlags(void) Q_assert(count == tx.outputnum.value()); // check we are outputting them in the proper sequence - fwrite(&tx.flags, 1, sizeof(tx.flags), texinfofile); + SafeWrite(texinfofile, &tx.flags, sizeof(tx.flags)); count++; } Q_assert(count == static_cast(map.exported_texinfos.size())); - - fclose(texinfofile); } template diff --git a/vis/state.cc b/vis/state.cc index e22616fd..9665112c 100644 --- a/vis/state.cc +++ b/vis/state.cc @@ -222,7 +222,7 @@ bool LoadVisState(void) return false; } - auto infile = SafeOpenRead(statefile.string().c_str()); + auto infile = SafeOpenRead(statefile, true); SafeRead(infile, &state, sizeof(state)); state.version = LittleLong(state.version); diff --git a/vis/vis.cc b/vis/vis.cc index 8a29b44c..e8a14592 100644 --- a/vis/vis.cc +++ b/vis/vis.cc @@ -998,32 +998,27 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) portal_t *p; leaf_t *l; char magic[80]; - FILE *f; + qfile_t f { nullptr, nullptr }; int numpoints; winding_t *w; int leafnums[2]; plane_t plane; if (name == "-") - f = stdin; + f = { stdin, nullptr }; else { - f = fopen(name.string().c_str(), "r"); - if (!f) { - FLogPrint("couldn't read {}\n", name); - LogPrint("No vising performed.\n"); - exit(1); - } + f = SafeOpenRead(name, true); } /* * Parse the portal file header */ - count = fscanf(f, "%79s\n", magic); + count = fscanf(f.get(), "%79s\n", magic); if (count != 1) FError("unknown header: {}\n", magic); if (!strcmp(magic, PORTALFILE)) { - count = fscanf(f, "%i\n%i\n", &portalleafs, &numportals); + count = fscanf(f.get(), "%i\n%i\n", &portalleafs, &numportals); if (count != 2) FError("unable to parse {} HEADER\n", PORTALFILE); @@ -1038,14 +1033,14 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) LogPrint("{:6} portals\n", numportals); } } else if (!strcmp(magic, PORTALFILE2)) { - count = fscanf(f, "%i\n%i\n%i\n", &portalleafs_real, &portalleafs, &numportals); + count = fscanf(f.get(), "%i\n%i\n%i\n", &portalleafs_real, &portalleafs, &numportals); if (count != 3) FError("unable to parse {} HEADER\n", PORTALFILE); LogPrint("{:6} leafs\n", portalleafs_real); LogPrint("{:6} clusters\n", portalleafs); LogPrint("{:6} portals\n", numportals); } else if (!strcmp(magic, PORTALFILEAM)) { - count = fscanf(f, "%i\n%i\n%i\n", &portalleafs, &numportals, &portalleafs_real); + count = fscanf(f.get(), "%i\n%i\n%i\n", &portalleafs, &numportals, &portalleafs_real); if (count != 3) FError("unable to parse {} HEADER\n", PORTALFILE); LogPrint("{:6} leafs\n", portalleafs_real); @@ -1080,7 +1075,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) vismap_end = vismap + MAX_MAP_VISIBILITY; for (i = 0, p = portals; i < numportals; i++) { - if (fscanf(f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3) + if (fscanf(f.get(), "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1]) != 3) FError("reading portal {}", i); if (numpoints > MAX_WINDING) FError("portal {} has too many points", i); @@ -1091,10 +1086,10 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) w->numpoints = numpoints; for (j = 0; j < numpoints; j++) { - if (fscanf(f, "(%lf %lf %lf ) ", &w->points[j][0], &w->points[j][1], &w->points[j][2]) != 3) + if (fscanf(f.get(), "(%lf %lf %lf ) ", &w->points[j][0], &w->points[j][1], &w->points[j][2]) != 3) FError("reading portal {}", i); } - fscanf(f, "\n"); + fscanf(f.get(), "\n"); // calc plane PlaneFromWinding(w, &plane); @@ -1146,7 +1141,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) for (i = 0; i < portalleafs; i++) { while (1) { int leafnum; - count = fscanf(f, "%i", &leafnum); + count = fscanf(f.get(), "%i", &leafnum); if (!count || count == EOF) break; if (leafnum < 0) @@ -1163,7 +1158,7 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) } else if (!strcmp(magic, PORTALFILEAM)) { for (i = 0; i < portalleafs_real; i++) { int clusternum; - count = fscanf(f, "%i", &clusternum); + count = fscanf(f.get(), "%i", &clusternum); if (!count || count == EOF) { Error("Unexpected end of cluster map\n"); } @@ -1177,8 +1172,6 @@ static void LoadPortals(const std::filesystem::path &name, mbsp_t *bsp) FError("Unknown header {}\n", magic); } } - - fclose(f); } /*