Skip to content

Commit

Permalink
fix(bsppp): add L4D2 bsp support, fix writing lumps that initially do…
Browse files Browse the repository at this point in the history
… not exist
  • Loading branch information
craftablescience committed Jul 18, 2024
1 parent 4eccd88 commit 127d9fc
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 28 deletions.
9 changes: 7 additions & 2 deletions include/bsppp/bsppp.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ enum class BSPLump : int32_t {
};
static_assert(static_cast<int32_t>(BSPLump::COUNT) == 64, "Incorrect lump count!");

constexpr auto BSP_LUMP_COUNT = static_cast<int32_t>(BSPLump::COUNT);

class BSP {
struct Lump {
/// Byte offset into file
Expand All @@ -115,7 +117,7 @@ class BSP {
/// Version of the BSP file
int32_t version;
/// Lump metadata
std::array<Lump, static_cast<uint32_t>(BSPLump::COUNT)> lumps;
std::array<Lump, BSP_LUMP_COUNT> lumps;
/// Map version number
int32_t mapRevision;
};
Expand Down Expand Up @@ -147,10 +149,13 @@ class BSP {
void writeHeader() const;

/// If the lump is too big where it is, shift it to the end of the file, otherwise its fine
void moveLumpToWritableSpace(BSPLump lumpIndex, int newSize);
void moveLumpToWritableSpace(BSPLump lumpIndex, int32_t newSize);

std::string path;
Header header{};

// Slightly different header just to be quirky and special
bool isL4D2 = false;
};

} // namespace bsppp
103 changes: 77 additions & 26 deletions src/bsppp/bsppp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,50 @@ BSP::BSP(std::string path_)
this->path.clear();
return;
}
reader
.read(this->header.version)
.read(this->header.lumps)
.read(this->header.mapRevision);
this->header.version = reader.read<int32_t>();

// Contagion funny
if (this->header.version == 27) {
reader.skip_in<int32_t>();
}

reader >> this->header.lumps;

// If no offsets are larger than 1024 (less than the size of the BSP header, but greater than any lump version),
// it's probably a L4D2 BSP and those are lump versions!
if (this->header.version == 21) {
int i;
for (i = 0; i < BSP_LUMP_COUNT; i++) {
if (this->header.lumps[i].offset > 1024) {
break;
}
}
this->isL4D2 = i == BSP_LUMP_COUNT;
if (this->isL4D2) {
// Swap offset and version
for (i = 0; i < BSP_LUMP_COUNT; i++) {
std::swap(this->header.lumps[i].offset, this->header.lumps[i].version);
std::swap(this->header.lumps[i].offset, this->header.lumps[i].length);
}
}
}

reader >> this->header.mapRevision;
}

BSP::operator bool() const {
return !this->path.empty();
}

BSP BSP::create(std::string path, int32_t version, int32_t mapRevision) {
FileStream writer{path, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
writer << BSP_SIGNATURE << BSP::Header{
.version = version,
.lumps = {},
.mapRevision = mapRevision,
};
{
FileStream writer{path, FileStream::OPT_TRUNCATE | FileStream::OPT_CREATE_IF_NONEXISTENT};
writer << BSP_SIGNATURE << BSP::Header{
.version = version,
.lumps = {},
.mapRevision = mapRevision,
};
}
return BSP{std::move(path)};
}

Expand Down Expand Up @@ -85,14 +112,7 @@ void BSP::writeLump(BSPLump lumpIndex, const std::vector<std::byte>& data) {
this->moveLumpToWritableSpace(lumpIndex, static_cast<int>(data.size()));

FileStream writer{this->path, FileStream::OPT_READ | FileStream::OPT_WRITE};
writer
.seek_out(0)
.write(BSP_SIGNATURE)
.write(this->header.version)
.write(this->header.lumps)
.write(this->header.mapRevision)
.seek_out(this->header.lumps[static_cast<uint32_t>(lumpIndex)].offset)
.write(data);
writer.seek_out(this->header.lumps[static_cast<uint32_t>(lumpIndex)].offset).write(data);
}

void BSP::createLumpPatchFile(BSPLump lumpIndex) const {
Expand Down Expand Up @@ -125,21 +145,52 @@ void BSP::createLumpPatchFile(BSPLump lumpIndex) const {

void BSP::writeHeader() const {
FileStream writer{this->path, FileStream::OPT_READ | FileStream::OPT_WRITE};
writer
.seek_out(0)
.write(BSP_SIGNATURE)
.write(this->header.version)
.write(this->header.lumps)
.write(this->header.mapRevision);
writer.seek_out(0) << BSP_SIGNATURE << this->header.version;

// Contagion funny
if (this->header.version == 27) {
writer.write<int32_t>(0);
}

if (!this->isL4D2) {
writer << this->header.lumps;
} else {
for (int i = 0; i < static_cast<int32_t>(BSPLump::COUNT); i++) {
writer << this->header.lumps[i].version << this->header.lumps[i].offset << this->header.lumps[i].length << this->header.lumps[i].fourCC;
}
}

writer << this->header.mapRevision;
}

void BSP::moveLumpToWritableSpace(BSPLump lumpIndex, int newSize) {
void BSP::moveLumpToWritableSpace(BSPLump lumpIndex, int32_t newSize) {
auto lumpToMove = static_cast<uint32_t>(lumpIndex);
this->header.lumps[lumpToMove].length = newSize;

// If the lump doesn't exist, put it at the end of the file
if (!this->hasLump(lumpIndex)) {
int lastLumpOffset = 0, lastLumpLength = 0;
for (const Lump& lump : this->header.lumps) {
if (lastLumpOffset < this->header.lumps[lumpToMove].offset) {
lastLumpOffset = lump.offset;
lastLumpLength = lump.length;
}
}
if (lastLumpOffset == 0) {
// Whole file is full of empty lumps
this->header.lumps[lumpToMove].offset = sizeof(Header);
} else {
this->header.lumps[lumpToMove].offset = lastLumpOffset + lastLumpLength;
}
this->header.lumps[lumpToMove].length = newSize;

this->writeHeader();
return;
}

// If the moving lump is at the end of the file we just overwrite it, otherwise we have to shift some lumps over
std::vector<int> lumpsAfterMovingLumpIndices;
for (int i = 0; i < this->header.lumps.size(); i++) {
for (int i = 0; i < BSP_LUMP_COUNT; i++) {
if (this->header.lumps[i].offset > this->header.lumps[lumpToMove].offset) {
lumpsAfterMovingLumpIndices.push_back(i);
}
Expand Down

0 comments on commit 127d9fc

Please sign in to comment.