200 lines
6.3 KiB
C++
200 lines
6.3 KiB
C++
/* Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
See file, 'COPYING', for details.
|
|
*/
|
|
|
|
#include <common/prtfile.hh>
|
|
|
|
#include <common/log.hh>
|
|
#include <common/fs.hh>
|
|
#include <common/bspfile.hh>
|
|
#include <common/ostream.hh>
|
|
|
|
#include <fstream>
|
|
|
|
constexpr const char *PORTALFILE = "PRT1";
|
|
constexpr const char *PORTALFILE2 = "PRT2";
|
|
constexpr const char *PORTALFILEAM = "PRT1-AM";
|
|
|
|
constexpr size_t PRT_MAX_WINDING = 64;
|
|
|
|
prtfile_t LoadPrtFile(const fs::path &name, const bspversion_t *loadversion)
|
|
{
|
|
std::ifstream f(name);
|
|
|
|
/*
|
|
* Parse the portal file header
|
|
*/
|
|
std::string magic;
|
|
std::getline(f, magic);
|
|
if (magic.empty()) {
|
|
FError("unknown header/empty portal file {}\n", name);
|
|
}
|
|
|
|
prtfile_t result{};
|
|
int numportals;
|
|
|
|
if (magic == PORTALFILE) {
|
|
f >> result.portalleafs >> numportals;
|
|
|
|
if (f.bad())
|
|
FError("unable to parse {} header\n", PORTALFILE);
|
|
|
|
if (loadversion->game->id == GAME_QUAKE_II) {
|
|
// since q2bsp has native cluster support, we shouldn't look at portalleafs_real at all.
|
|
result.portalleafs_real = 0;
|
|
} else {
|
|
result.portalleafs_real = result.portalleafs;
|
|
}
|
|
} else if (magic == PORTALFILE2) {
|
|
if (loadversion->game->id == GAME_QUAKE_II) {
|
|
FError("{} can not be used with Q2\n", PORTALFILE2);
|
|
}
|
|
f >> result.portalleafs_real >> result.portalleafs >> numportals;
|
|
|
|
if (f.bad())
|
|
FError("unable to parse {} header\n", PORTALFILE);
|
|
} else if (magic == PORTALFILEAM) {
|
|
if (loadversion->game->id == GAME_QUAKE_II) {
|
|
FError("{} can not be used with Q2\n", PORTALFILEAM);
|
|
}
|
|
f >> result.portalleafs >> numportals >> result.portalleafs_real;
|
|
|
|
if (f.bad())
|
|
FError("unable to parse {} header\n", PORTALFILE);
|
|
} else {
|
|
FError("unknown header: {}\n", magic);
|
|
}
|
|
|
|
for (int i = 0; i < numportals; i++) {
|
|
prtfile_portal_t p;
|
|
int numpoints;
|
|
|
|
f >> numpoints >> p.leafnums[0] >> p.leafnums[1];
|
|
if (f.bad())
|
|
FError("reading portal {}", i);
|
|
if (numpoints > PRT_MAX_WINDING)
|
|
FError("portal {} has too many points", i);
|
|
if ((unsigned)p.leafnums[0] > (unsigned)result.portalleafs ||
|
|
(unsigned)p.leafnums[1] > (unsigned)result.portalleafs)
|
|
FError("out of bounds leaf in portal {}", i);
|
|
|
|
auto &w = p.winding;
|
|
w.resize(numpoints);
|
|
|
|
for (int j = 0; j < numpoints; j++) {
|
|
while (!f.bad() && f.get() != '(')
|
|
;
|
|
|
|
f >> w[j][0] >> w[j][1] >> w[j][2];
|
|
|
|
while (!f.bad() && f.get() != ')')
|
|
;
|
|
|
|
if (f.bad())
|
|
FError("reading portal {}", i);
|
|
}
|
|
|
|
result.portals.push_back(std::move(p));
|
|
}
|
|
|
|
// Q2 doesn't need this, it's PRT1 has the data we need
|
|
if (loadversion->game->id == GAME_QUAKE_II) {
|
|
return result;
|
|
}
|
|
|
|
// No clusters
|
|
if (result.portalleafs == result.portalleafs_real) {
|
|
// e.g. Quake 1, PRT1 (no func_detail).
|
|
// Assign the identity cluster numbers for consistency
|
|
|
|
result.dleafinfos.resize(result.portalleafs + 1);
|
|
|
|
for (int i = 0; i < result.portalleafs; i++) {
|
|
result.dleafinfos[i + 1].cluster = i;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (magic == PORTALFILE2) {
|
|
result.dleafinfos.resize(result.portalleafs_real + 1);
|
|
|
|
int i;
|
|
for (i = 0; i < result.portalleafs; i++) {
|
|
while (1) {
|
|
int leafnum;
|
|
f >> leafnum;
|
|
if (f.bad() || f.eof())
|
|
break;
|
|
if (leafnum < 0)
|
|
break;
|
|
if (leafnum >= result.portalleafs_real)
|
|
FError("Invalid leaf number in cluster map ({} >= {})", leafnum, result.portalleafs_real);
|
|
result.dleafinfos[leafnum + 1].cluster = i;
|
|
}
|
|
if (f.bad() || f.eof())
|
|
break;
|
|
}
|
|
if (i < result.portalleafs)
|
|
FError("Couldn't read cluster map ({} / {})\n", i, result.portalleafs);
|
|
} else if (magic == PORTALFILEAM) {
|
|
result.dleafinfos.resize(result.portalleafs + 1);
|
|
|
|
for (int i = 0; i < result.portalleafs_real; i++) {
|
|
int clusternum;
|
|
f >> clusternum;
|
|
if (f.bad() || f.eof()) {
|
|
Error("Unexpected end of cluster map\n");
|
|
}
|
|
if (clusternum < 0 || clusternum >= result.portalleafs) {
|
|
FError("Invalid cluster number {} in cluster map, number of clusters: {}\n", clusternum,
|
|
result.portalleafs);
|
|
}
|
|
result.dleafinfos[i + 1].cluster = clusternum;
|
|
}
|
|
} else {
|
|
FError("Unknown header {}\n", magic);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void WriteDebugPortal(const polylib::winding_t &w, std::ofstream &portalFile)
|
|
{
|
|
ewt::print(portalFile, "{} {} {} ", w.size(), 0, 0);
|
|
for (int i = 0; i < w.size(); i++) {
|
|
ewt::print(portalFile, "({} {} {}) ", w.at(i)[0], w.at(i)[1], w.at(i)[2]);
|
|
}
|
|
ewt::print(portalFile, "\n");
|
|
}
|
|
|
|
void WriteDebugPortals(const std::vector<polylib::winding_t> &portals, fs::path name)
|
|
{
|
|
size_t portal_count = portals.size();
|
|
|
|
std::ofstream portal_file(name, std::ios_base::out);
|
|
if (!portal_file)
|
|
FError("Failed to open {}: {}", name, strerror(errno));
|
|
|
|
ewt::print(portal_file, "PRT1\n");
|
|
ewt::print(portal_file, "{}\n", 0);
|
|
ewt::print(portal_file, "{}\n", portal_count);
|
|
for (auto &p : portals) {
|
|
WriteDebugPortal(p, portal_file);
|
|
}
|
|
}
|