Lotta more file stuff. Have to think about fscanf though.

This commit is contained in:
Jonathan 2021-09-19 22:20:39 -04:00
parent 326626f127
commit 49fd62b9f3
25 changed files with 373 additions and 484 deletions

View File

@ -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");

View File

@ -35,6 +35,7 @@
#include <tuple>
#include <fmt/format.h>
#include <fmt/ostream.h>
#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)

View File

@ -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)

View File

@ -28,6 +28,8 @@
#include <light/ltface.hh>
#include <common/bsputils.hh>
#include <fmt/ostream.h>
entdict_t::entdict_t(std::initializer_list<keyvalue_t> l) : keyvalues(l) { }
entdict_t::entdict_t() = default;
@ -170,15 +172,15 @@ std::vector<entdict_t> EntData_Parse(const char *entdata)
*/
std::string EntData_Write(const std::vector<entdict_t> &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)

View File

@ -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

View File

@ -20,7 +20,7 @@
#include <common/qvec.hh>
#include <cmath> // for NAN
#include <sstream>
#include <fmt/format.h>
/*
* 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]);
}

View File

@ -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<FILE, decltype(&fclose)>;
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<typename ...Args>
inline void SafePrint(const qfile_t &f, const char *fmt, const Args &...args)
{
if (fmt::fprintf(f.get(), fmt, std::forward<const Args &>(args)...) < 0)
FError("Error writing to file");
}
long LoadFilePak(std::filesystem::path &filename, void *destptr);
long LoadFile(const std::filesystem::path &filename, void *destptr);

View File

@ -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;

View File

@ -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]);
}
};

View File

@ -22,17 +22,11 @@
#pragma once
#include <qbsp/parser.hh>
#include "common/cmdlib.hh"
#include <optional>
#include <vector>
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<std::string, std::string, case_insensitive_less> epairs;
// order of parse
std::vector<std::string> 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<const brush_t *> &list);
void WriteBspBrushMap(const std::filesystem::path &name, const std::vector<const brush_t *> &list);
bool IsValidTextureProjection(const qvec3f &faceNormal, const qvec3f &s_vec, const qvec3f &t_vec);

View File

@ -24,6 +24,7 @@
#include <unordered_map>
#include <vector>
#include <list>
#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<std::string, lumpinfo_t, case_insensitive_hash, case_insensitive_equal> lumps;
std::unordered_map<std::string, texture_t, case_insensitive_hash, case_insensitive_equal> textures;
FILE *file;
~wad_t()
{
if (file) {
fclose(file);
}
}
qfile_t file = { nullptr, nullptr };
};
// Q1 miptex format

View File

@ -19,7 +19,7 @@
#include <algorithm>
#include <cstring>
#include <sstream>
#include <fstream>
#include <common/cmdlib.hh>
#include <light/light.hh>
@ -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<char>(c));
s += static_cast<char>(c);
}
}
return ss.str();
return s;
}
/*
@ -1264,10 +1256,10 @@ void WriteEntitiesToString(const globalconfig_t &cfg, mbsp_t *bsp)
static std::vector<light_t> 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);
}
}

View File

@ -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
}

View File

@ -21,6 +21,8 @@
#include <cassert>
//#include <cstdio>
#include <iostream>
#include <fstream>
#include <fmt/ostream.h>
#include <light/light.hh>
#include <light/phong.hh>
@ -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);
}

View File

@ -31,6 +31,8 @@
#include <cassert>
#include <cmath>
#include <algorithm>
#include <fstream>
#include <fmt/ostream.h>
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<uint8_t> 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<qvec3f> 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<uint8_t> 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<qvec4f> LightmapColorsToGLMVector(const lightsurf_t *lightsurf, const lightmap_t *lm)

View File

@ -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; i<winding->numpoints; 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; i<winding->numpoints; 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<winding_t *> &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];

View File

@ -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;

View File

@ -22,29 +22,27 @@
#include <qbsp/wad.hh>
#include <unordered_set>
#include <fstream>
#include <fmt/ostream.h>
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<const face_t *> &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<const face
int vertcount = 0;
for (const face_t *face : faces) {
ExportObjFace(objfile, mtlfile, face, &vertcount);
ExportObjFace(objfile, face, &vertcount);
}
fclose(objfile);
fclose(mtlfile);
}
void ExportObj_Brushes(const std::string &filesuffix, const std::vector<const brush_t *> &brushes)

View File

@ -25,11 +25,12 @@
#include <cstring>
#include <string>
#include <sstream>
#include <memory>
#include <list>
#include <utility>
#include <optional>
#include <fstream>
#include <fmt/ostream.h>
#include <qbsp/qbsp.hh>
#include <qbsp/parser.hh>
@ -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<double>(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<std::pair<std::string, std::string>> 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<const brush_t *> &list)
void WriteBspBrushMap(const std::filesystem::path &name, const std::vector<const brush_t *> &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<const brush_t *> &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");
}
/*

View File

@ -25,6 +25,8 @@
#include <set>
#include <list>
#include <utility>
#include <fstream>
#include <fmt/ostream.h>
/*
===========
@ -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<std::vector<portal_t *>, 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<std::vector<portal_t *>, 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<std::vector<portal_t *>, node_t *> &le
VectorCopy(currpt, prevpt);
}
fclose(ptsfile);
LogPrint("Leak file written to {}\n", options.szBSPName);
}

View File

@ -20,6 +20,8 @@
*/
// portals.c
#include <fstream>
#include <fmt/ostream.h>
#include <qbsp/qbsp.hh>
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);
}
//=============================================================================

View File

@ -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<uint8_t> 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");
}

View File

@ -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<int>(map.exported_texinfos.size()));
fclose(texinfofile);
}
template<class C>

View File

@ -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);

View File

@ -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);
}
/*