Zonebuilder update for iw5xport compat (#750)

Co-authored-by: Louvenarde <louve@louve.systems>
Co-authored-by: Roxanne <roxanne@thegamebakers.com>
Co-authored-by: FutureRave <edoardo.sanguineti222@gmail.com>
This commit is contained in:
Louve 2023-02-04 18:29:32 +01:00 committed by GitHub
parent 14b3610fee
commit eccdf2e25e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 2803 additions and 647 deletions

View File

@ -1,7 +1,7 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "IFxEffectDef.hpp" #include "IFxEffectDef.hpp"
#define IW4X_FX_VERSION 1 #define IW4X_FX_VERSION 2
namespace Assets namespace Assets
{ {
@ -65,132 +65,139 @@ namespace Assets
void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{ {
Components::FileSystem::File fxFile(std::format("fx/{}.iw4xFx", name)); Components::FileSystem::File fxFile(std::format("fx/{}.iw4xFx", name));
if (!fxFile.exists())
if (fxFile.exists())
{ {
Utils::Stream::Reader buffer(builder->getAllocator(), fxFile.getBuffer()); return;
}
__int64 magic = buffer.read<__int64>(); Utils::Stream::Reader buffer(builder->getAllocator(), fxFile.getBuffer());
if (std::memcmp(&magic, "IW4xFx ", 8))
auto magic = buffer.read<std::int64_t>();
if (std::memcmp(&magic, "IW4xFx ", 8) != 0)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, header is invalid!", name);
}
int version = buffer.read<int>();
if (version > IW4X_FX_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, expected version is {}, but it was {}!", name, IW4X_FX_VERSION, version);
}
auto* asset = buffer.readObject<Game::FxEffectDef>();
header->fx = asset;
if (asset->name)
{
asset->name = buffer.readCString();
}
if (asset->elemDefs)
{
asset->elemDefs = buffer.readArray<Game::FxElemDef>(asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot);
for (auto i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i)
{ {
Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, header is invalid!", name); auto* elemDef = &asset->elemDefs[i];
}
int version = buffer.read<int>(); if (elemDef->velSamples)
if (version != IW4X_FX_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, expected version is {}, but it was {}!", name, IW4X_FX_VERSION, version);
}
Game::FxEffectDef* asset = buffer.readObject<Game::FxEffectDef>();
header->fx = asset;
if (asset->name)
{
asset->name = buffer.readCString();
}
if (asset->elemDefs)
{
asset->elemDefs = buffer.readArray<Game::FxElemDef>(asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot);
for (int i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i)
{ {
Game::FxElemDef* elemDef = &asset->elemDefs[i]; elemDef->velSamples = buffer.readArray<Game::FxElemVelStateSample>(elemDef->velIntervalCount + 1);
}
if (elemDef->velSamples) if (elemDef->visSamples)
{ {
elemDef->velSamples = buffer.readArray<Game::FxElemVelStateSample>(elemDef->velIntervalCount + 1); elemDef->visSamples = buffer.readArray<Game::FxElemVisStateSample>(elemDef->visStateIntervalCount + 1);
} }
if (elemDef->visSamples) // Save_FxElemDefVisuals
{
if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL)
{ {
elemDef->visSamples = buffer.readArray<Game::FxElemVisStateSample>(elemDef->visStateIntervalCount + 1); if (elemDef->visuals.markArray)
}
// Save_FxElemDefVisuals
{
if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL)
{ {
if (elemDef->visuals.markArray) elemDef->visuals.markArray = buffer.readArray<Game::FxElemMarkVisuals>(elemDef->visualCount);
for (char j = 0; j < elemDef->visualCount; ++j)
{ {
elemDef->visuals.markArray = buffer.readArray<Game::FxElemMarkVisuals>(elemDef->visualCount); if (elemDef->visuals.markArray[j].materials[0])
for (char j = 0; j < elemDef->visualCount; ++j)
{ {
if (elemDef->visuals.markArray[j].materials[0]) elemDef->visuals.markArray[j].materials[0] = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, buffer.readString(), builder).material;
{ }
elemDef->visuals.markArray[j].materials[0] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, buffer.readString().data(), builder).material;
}
if (elemDef->visuals.markArray[j].materials[1]) if (elemDef->visuals.markArray[j].materials[1])
{ {
elemDef->visuals.markArray[j].materials[1] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, buffer.readString().data(), builder).material; elemDef->visuals.markArray[j].materials[1] = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, buffer.readString(), builder).material;
}
} }
} }
} }
else if (elemDef->visualCount > 1) }
else if (elemDef->visualCount > 1)
{
if (elemDef->visuals.array)
{ {
if (elemDef->visuals.array) elemDef->visuals.array = buffer.readArray<Game::FxElemVisuals>(elemDef->visualCount);
{
elemDef->visuals.array = buffer.readArray<Game::FxElemVisuals>(elemDef->visualCount);
for (char j = 0; j < elemDef->visualCount; ++j) for (char j = 0; j < elemDef->visualCount; ++j)
{
this->loadFxElemVisuals(&elemDef->visuals.array[j], elemDef->elemType, builder, &buffer);
}
}
}
else if (elemDef->visualCount == 1)
{
this->loadFxElemVisuals(&elemDef->visuals.instance, elemDef->elemType, builder, &buffer);
}
}
if (elemDef->effectOnImpact.handle)
{
elemDef->effectOnImpact.handle = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_FX, buffer.readString(), builder).fx;
}
if (elemDef->effectOnDeath.handle)
{
elemDef->effectOnDeath.handle = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_FX, buffer.readString(), builder).fx;
}
if (elemDef->effectEmitted.handle)
{
elemDef->effectEmitted.handle = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_FX, buffer.readString(), builder).fx;
}
// Save_FxElemExtendedDefPtr
{
if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL)
{
// Save_FxTrailDef
{
if (elemDef->extended.trailDef)
{
auto* trailDef = buffer.readObject<Game::FxTrailDef>();
elemDef->extended.trailDef = trailDef;
if (trailDef->verts)
{ {
this->loadFxElemVisuals(&elemDef->visuals.array[j], elemDef->elemType, builder, &buffer); trailDef->verts = buffer.readArray<Game::FxTrailVertex>(trailDef->vertCount);
}
if (trailDef->inds)
{
trailDef->inds = buffer.readArray<unsigned short>(trailDef->indCount);
} }
} }
} }
else }
else if (version >= 2)
{
if (elemDef->elemType == Game::FX_ELEM_TYPE_SPARK_FOUNTAIN)
{ {
this->loadFxElemVisuals(&elemDef->visuals.instance, elemDef->elemType, builder, &buffer); if (elemDef->extended.sparkFountainDef)
}
}
if (elemDef->effectOnImpact.handle)
{
elemDef->effectOnImpact.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString().data(), builder).fx;
}
if (elemDef->effectOnDeath.handle)
{
elemDef->effectOnDeath.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString().data(), builder).fx;
}
if (elemDef->effectEmitted.handle)
{
elemDef->effectEmitted.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString().data(), builder).fx;
}
// Save_FxElemExtendedDefPtr
{
if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL)
{
// Save_FxTrailDef
{ {
if (elemDef->extended.trailDef) elemDef->extended.sparkFountainDef = buffer.readObject<Game::FxSparkFountainDef>();
{
Game::FxTrailDef* trailDef = buffer.readObject<Game::FxTrailDef>();
elemDef->extended.trailDef = trailDef;
if (trailDef->verts)
{
trailDef->verts = buffer.readArray<Game::FxTrailVertex>(trailDef->vertCount);
}
if (trailDef->inds)
{
trailDef->inds = buffer.readArray<unsigned short>(trailDef->indCount);
}
}
} }
} }
else if (elemDef->extended.trailDef)
{
Components::Logger::Error(Game::ERR_FATAL, "Fx element of type {} has traildef, that's impossible?\n", elemDef->elemType);
}
} }
} }
} }
@ -270,7 +277,7 @@ namespace Assets
// TODO: Convert editor fx to real fx // TODO: Convert editor fx to real fx
} }
#else #else
(name); (void)name;
#endif #endif
} }

View File

@ -12,13 +12,13 @@ namespace Assets
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
private: private:
void markFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder); static void markFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder);
void saveFxElemVisuals(Game::FxElemVisuals* visuals, Game::FxElemVisuals* destVisuals, char elemType, Components::ZoneBuilder::Zone* builder); static void saveFxElemVisuals(Game::FxElemVisuals* visuals, Game::FxElemVisuals* destVisuals, char elemType, Components::ZoneBuilder::Zone* builder);
void loadEfx(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); static void loadEfx(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder); void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
void loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); static void loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader);
}; };
} }

View File

@ -1,6 +1,11 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include <json.hpp>
#include "IFxWorld.hpp" #include "IFxWorld.hpp"
#define IW4X_FXWORLD_VERSION 1
namespace Assets namespace Assets
{ {
void IFxWorld::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) void IFxWorld::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
@ -187,6 +192,161 @@ namespace Assets
} }
void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
if (!header->fxWorld) loadFromDisk(header, name, builder);
if (!header->fxWorld) generate(header, name, builder);
assert(header->fxWorld);
}
void IFxWorld::loadFromDisk(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder)
{
std::string name = _name;
Utils::String::Replace(name, "maps/mp/", "");
Utils::String::Replace(name, ".d3dbsp", "");
Components::FileSystem::File fxWorldFile(std::format("fxworld/{}.iw4x.json", name));
if (!fxWorldFile.exists())
{
return;
}
nlohmann::json fxWorldJson;
try
{
fxWorldJson = nlohmann::json::parse(fxWorldFile.getBuffer());
}
catch (const std::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for gameworld {}! Error message: {}", name, e.what());
return;
}
if (!fxWorldJson.is_object())
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid FXWORLD JSON for {}\n", name);
return;
}
auto version = fxWorldJson["version"].is_number() ? fxWorldJson["version"].get<int>() : 0;
if (version != IW4X_FXWORLD_VERSION)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid FXWORLD json version for {}, expected {} and got {}\n", name, IW4X_FXWORLD_VERSION, version);
return;
}
auto map = builder->getAllocator()->allocate<Game::FxWorld>();
map->name = builder->getAllocator()->duplicateString(_name);
try
{
auto glassSys = &map->glassSys;
auto glassSysJson = fxWorldJson["glassSys"];
glassSys->time = glassSysJson["time"].get<int>();
glassSys->prevTime = glassSysJson["prevTime"].get<int>();
glassSys->defCount = glassSysJson["defCount"].get<unsigned int>();
glassSys->pieceLimit = glassSysJson["pieceLimit"].get<unsigned int>();
glassSys->pieceWordCount = glassSysJson["pieceWordCount"].get<unsigned int>();
glassSys->initPieceCount = glassSysJson["initPieceCount"].get<unsigned int>();
glassSys->cellCount = glassSysJson["cellCount"].get<unsigned int>();
glassSys->activePieceCount = glassSysJson["activePieceCount"].get<unsigned int>();
glassSys->firstFreePiece = glassSysJson["firstFreePiece"].get<unsigned int>();
glassSys->geoDataLimit = glassSysJson["geoDataLimit"].get<unsigned int>();
glassSys->geoDataCount = glassSysJson["geoDataCount"].get<unsigned int>();
glassSys->initGeoDataCount = glassSysJson["initGeoDataCount"].get<unsigned int>();
auto i = 0;
glassSys->defs = builder->getAllocator()->allocateArray<Game::FxGlassDef>(glassSys->defCount);
for (auto member : glassSysJson["defs"])
{
auto def = &glassSys->defs[i];
def->halfThickness = member["halfThickness"].get<float>();
auto xy = 0;
for (auto x = 0; x < 2; x++)
{
for (auto y = 0; y < 2; y++)
{
def->texVecs[x][y] = member["texVecs"][xy].get<float>();
xy++;
}
}
def->color.packed = member["color"].get<int>();
auto matShateredName = member["materialShattered"].get<std::string>();
auto matName = member["material"].get<std::string>();
auto physPresetName = member["physPreset"].get<std::string>();
def->material = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, matName, builder).material;
def->materialShattered = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, matShateredName, builder).material;
def->physPreset = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_PHYSPRESET, physPresetName, builder ).physPreset;
assert(def->material);
assert(def->materialShattered);
assert(def->physPreset);
++i;
}
i = 0;
glassSys->initPieceStates = builder->getAllocator()->allocateArray<Game::FxGlassInitPieceState>(glassSys->initPieceCount);
for (auto member : glassSysJson["initPieceStates"])
{
auto initial = &glassSys->initPieceStates[i];
for (int j = 0; j < ARRAYSIZE(initial->frame.quat); j++)
{
initial->frame.quat[j] = member["frame"]["quat"][j].get<float>();
}
for (int j = 0; j < ARRAYSIZE(initial->frame.origin); j++)
{
initial->frame.origin[j] = member["frame"]["origin"][j].get<float>();
}
initial->radius = member["radius"].get<float>();
initial->texCoordOrigin[0] = member["texCoordOrigin"][0].get<float>();
initial->texCoordOrigin[1] = member["texCoordOrigin"][1].get<float>();
initial->supportMask = member["supportMask"].get<int>();
initial->areaX2 = member["areaX2"].get<float>();
initial->defIndex = member["defIndex"].get<char>();
initial->vertCount = member["vertCount"].get<char>();
initial->fanDataCount = member["fanDataCount"].get<char>();
++i;
}
i = 0;
glassSys->initGeoData = builder->getAllocator()->allocateArray<Game::FxGlassGeometryData>(glassSys->initGeoDataCount);
for (auto member : glassSysJson["initGeoData"])
{
auto data = &glassSys->initGeoData[i];
data->anonymous[0] = member[0];
data->anonymous[1] = member[1];
++i;
}
}
catch (const nlohmann::json::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed FXWORLD JSON for {}! Error message: {}\n", name, e.what());
return;
}
map->glassSys.piecePlaces = builder->getAllocator()->allocateArray<Game::FxGlassPiecePlace>(map->glassSys.pieceLimit);
map->glassSys.pieceStates = builder->getAllocator()->allocateArray<Game::FxGlassPieceState>(map->glassSys.pieceLimit);
map->glassSys.pieceDynamics = builder->getAllocator()->allocateArray<Game::FxGlassPieceDynamics>(map->glassSys.pieceLimit);
map->glassSys.geoData = builder->getAllocator()->allocateArray<Game::FxGlassGeometryData>(map->glassSys.geoDataLimit);
map->glassSys.isInUse = builder->getAllocator()->allocateArray<unsigned int>(map->glassSys.pieceWordCount);
map->glassSys.cellBits = builder->getAllocator()->allocateArray<unsigned int>(map->glassSys.pieceWordCount * map->glassSys.cellCount);
map->glassSys.visData = builder->getAllocator()->allocateArray<char>((map->glassSys.pieceLimit + 15) & 0xFFFFFFF0); // ugh
map->glassSys.linkOrg = reinterpret_cast<float(*)[3]>(builder->getAllocator()->allocateArray<float>(map->glassSys.pieceLimit));
map->glassSys.halfThickness = builder->getAllocator()->allocateArray<float>(map->glassSys.pieceLimit * 3);
map->glassSys.lightingHandles = builder->getAllocator()->allocateArray<unsigned short>(map->glassSys.initPieceCount);
header->fxWorld = map;
}
void IFxWorld::generate(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{ {
Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld; Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld;
if (map) return; if (map) return;
@ -194,7 +354,7 @@ namespace Assets
// Generate // Generate
map = builder->getAllocator()->allocate<Game::FxWorld>(); map = builder->getAllocator()->allocate<Game::FxWorld>();
map->name = builder->getAllocator()->duplicateString(name); map->name = builder->getAllocator()->duplicateString(name);
// No glass for you! // No glass for you!
ZeroMemory(&map->glassSys, sizeof(map->glassSys)); ZeroMemory(&map->glassSys, sizeof(map->glassSys));

View File

@ -10,5 +10,7 @@ namespace Assets
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
void generate(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
}; };
} }

View File

@ -153,7 +153,7 @@ namespace Assets
{ {
nlohmann::json::array_t jsonPiecesIndices = jsonGlassName["piecesIndices"]; nlohmann::json::array_t jsonPiecesIndices = jsonGlassName["piecesIndices"];
glassData->glassNames[i].pieceCount = static_cast<unsigned short>(jsonPiecesIndices.size()); glassData->glassNames[i].pieceCount = static_cast<unsigned short>(jsonPiecesIndices.size());
glassData->glassNames[i].pieceIndices = builder->getAllocator()->allocateArray<unsigned short>(glassData->glassNames[i].pieceCount);
for (size_t j = 0; j < glassData->glassNames[i].pieceCount; j++) for (size_t j = 0; j < glassData->glassNames[i].pieceCount; j++)
{ {
glassData->glassNames[i].pieceIndices[j] = jsonPiecesIndices[j].get<unsigned short>(); glassData->glassNames[i].pieceIndices[j] = jsonPiecesIndices[j].get<unsigned short>();
@ -162,9 +162,9 @@ namespace Assets
} }
} }
if (gameWorldJson["glassPieces"].is_array()) if (jsonGlassData["glassPieces"].is_array())
{ {
nlohmann::json::array_t glassPieces = gameWorldJson["glassPieces"]; nlohmann::json::array_t glassPieces = jsonGlassData["glassPieces"];
glassData->pieceCount = glassPieces.size(); glassData->pieceCount = glassPieces.size();
glassData->glassPieces = builder->getAllocator()->allocateArray<Game::G_GlassPiece>(glassData->pieceCount); glassData->glassPieces = builder->getAllocator()->allocateArray<Game::G_GlassPiece>(glassData->pieceCount);

View File

@ -3,27 +3,6 @@
#define IW4X_GFXMAP_VERSION 1 #define IW4X_GFXMAP_VERSION 1
// The xmodel vehicle_small_hatch_green_destructible_mp causes EXTREME lag
// when placed in the world, for reasons unknown.
//
// Something happens with the SModelSurfIterator which makes it load garbage
// as an XSurface in the middle of otherwise valid surfaces. This bug is very
// easy to reproduce with an empty map and just this car in the middle
//
// As of know we do not know why the iterator corruption occurs or what causes
// it. It doesn't seem linked to the SModel, nor to the materials or techsets,
// nor to the sortkeys, nor to the tilemode, boneinfo, and so on. So for now
// and to make it work for majority of users, we just swap the car. (no, using
// the identical car from iw4's favela_escape doesn't work either!)
//
// Two other models have this problem: ch_apartment_9story_noentry_02 and
// ch_apartment_5story_noentry_01
// But these exist in mp_vacant in slightly different versions, and can be
// swapped safely by deleting the two .iw4XModel files and requiring mp_vacant
// or a minimal zone containing just these two models.
//
#define SWAP_GREEN_VEHICLE_XMODEL 1
namespace Assets namespace Assets
{ {
void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
@ -44,11 +23,12 @@ namespace Assets
for (unsigned int i = 0; i < world->surfaceCount; ++i) for (unsigned int i = 0; i < world->surfaceCount; ++i)
{ {
Game::GfxSurface* surface = &asset->surfaces[i]; auto* surface = &asset->surfaces[i];
if (surface->material) if (surface->material)
{ {
world->dpvs.surfaces[i].material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader->readString().data(), builder).material; auto materialName = reader->readString();
world->dpvs.surfaces[i].material = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, materialName, builder).material;
assert(world->dpvs.surfaces[i].material);
} }
} }
} }
@ -69,8 +49,19 @@ namespace Assets
if (model->model) if (model->model)
{ {
auto name = reader->readString(); auto name = reader->readString();
while (name.ends_with("."))
{
// Happens with some flowers in mp_paris
// I'm not confident this will work on every map
// But regardless Game FS does not support having a file terminated with "."
// Probably an artist made a typo in MW3...
// Example: "foliage_gardenflowers_red_bright..iw4xModel"
name = name.substr(0, name.size() - 1);
}
assert(!name.empty());
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, name.data(), builder).model; model->model = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_XMODEL, name, builder).model;
assert(model->model); assert(model->model);
} }
@ -86,7 +77,7 @@ namespace Assets
for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i)
{ {
asset->reflectionProbes[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; asset->reflectionProbes[i] = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
} }
} }
@ -101,28 +92,27 @@ namespace Assets
for (int i = 0; i < asset->lightmapCount; ++i) for (int i = 0; i < asset->lightmapCount; ++i)
{ {
Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; auto* lightmapArray = &asset->lightmaps[i];
if (lightmapArray->primary) if (lightmapArray->primary)
{ {
lightmapArray->primary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; lightmapArray->primary = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
} }
if (lightmapArray->secondary) if (lightmapArray->secondary)
{ {
lightmapArray->secondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; lightmapArray->secondary = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
} }
} }
} }
if (asset->lightmapOverridePrimary) if (asset->lightmapOverridePrimary)
{ {
asset->lightmapOverridePrimary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; asset->lightmapOverridePrimary = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
} }
if (asset->lightmapOverrideSecondary) if (asset->lightmapOverrideSecondary)
{ {
asset->lightmapOverrideSecondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; asset->lightmapOverrideSecondary = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
} }
// saveGfxWorldVertexData // saveGfxWorldVertexData
@ -156,295 +146,305 @@ namespace Assets
Components::FileSystem::File mapFile(std::format("gfxworld/{}.iw4xGfxWorld", name)); Components::FileSystem::File mapFile(std::format("gfxworld/{}.iw4xGfxWorld", name));
if (mapFile.exists()) if (!mapFile.exists())
{ {
Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer()); return;
}
__int64 magic = reader.read<__int64>(); Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer());
if (std::memcmp(&magic, "IW4xGfxW", 8))
auto magic = reader.read<std::int64_t>();
if (std::memcmp(&magic, "IW4xGfxW", 8) != 0)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, header is invalid!", name);
}
int version = reader.read<int>();
if (version != IW4X_GFXMAP_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, expected version is {}, but it was {}!", name, IW4X_GFXMAP_VERSION, version);
}
auto* asset = reader.readObject<Game::GfxWorld>();
header->gfxWorld = asset;
if (asset->name)
{
asset->name = reader.readCString();
}
if (asset->baseName)
{
asset->baseName = reader.readCString();
}
if (asset->skies)
{
asset->skies = reader.readArray<Game::GfxSky>(asset->skyCount);
for (int i = 0; i < asset->skyCount; ++i)
{ {
Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, header is invalid!", name); auto* sky = &asset->skies[i];
}
int version = reader.read<int>(); if (sky->skyStartSurfs)
if (version != IW4X_GFXMAP_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, expected version is {}, but it was {}!", name, IW4X_GFXMAP_VERSION, version);
}
Game::GfxWorld* asset = reader.readObject<Game::GfxWorld>();
header->gfxWorld = asset;
if (asset->name)
{
asset->name = reader.readCString();
}
if (asset->baseName)
{
asset->baseName = reader.readCString();
}
if (asset->skies)
{
asset->skies = reader.readArray<Game::GfxSky>(asset->skyCount);
for (int i = 0; i < asset->skyCount; ++i)
{ {
Game::GfxSky* sky = &asset->skies[i]; sky->skyStartSurfs = reader.readArray<int>(sky->skySurfCount);
}
if (sky->skyStartSurfs) if (sky->skyImage)
{
sky->skyImage = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_IMAGE, reader.readString(), builder).image;
}
}
}
// GfxWorldDpvsPlanes
{
if (asset->dpvsPlanes.planes)
{
asset->dpvsPlanes.planes = reader.readArray<Game::cplane_s>(asset->planeCount);
auto clip = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, asset->name, builder).clipMap;
if (clip)
{
assert(clip->planeCount == static_cast<unsigned int>(asset->planeCount));
for (size_t i = 0; i < clip->planeCount; i++)
{ {
sky->skyStartSurfs = reader.readArray<int>(sky->skySurfCount); assert(!std::memcmp(&clip->planes[i], &asset->dpvsPlanes.planes[i], sizeof Game::cplane_s));
} }
if (sky->skyImage) asset->dpvsPlanes.planes = clip->planes;
{ }
sky->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; else
} {
Components::Logger::Error(Game::ERR_FATAL, "GfxWorld dpvs planes not mapped. This shouldn't happen. Make sure to load the ClipMap first!\n");
} }
} }
// GfxWorldDpvsPlanes if (asset->dpvsPlanes.nodes)
{ {
if (asset->dpvsPlanes.planes) asset->dpvsPlanes.nodes = reader.readArray<unsigned short>(asset->nodeCount);
{
void* oldPtr = asset->dpvsPlanes.planes;
asset->dpvsPlanes.planes = reader.readArray<Game::cplane_s>(asset->planeCount);
if (builder->getAllocator()->isPointerMapped(oldPtr))
{
asset->dpvsPlanes.planes = builder->getAllocator()->getPointer<Game::cplane_s>(oldPtr);
}
else
{
builder->getAllocator()->mapPointer(oldPtr, asset->dpvsPlanes.planes);
Components::Logger::Print("GfxWorld dpvs planes not mapped. This shouldn't happen. Make sure to load the ClipMap first!\n");
}
}
if (asset->dpvsPlanes.nodes)
{
asset->dpvsPlanes.nodes = reader.readArray<unsigned short>(asset->nodeCount);
}
} }
}
auto cellCount = asset->dpvsPlanes.cellCount;
int cellCount = asset->dpvsPlanes.cellCount; if (asset->aabbTreeCounts)
{
asset->aabbTreeCounts = reader.readArray<Game::GfxCellTreeCount>(cellCount);
}
if (asset->aabbTreeCounts) if (asset->aabbTrees)
{
asset->aabbTrees = reader.readArray<Game::GfxCellTree>(cellCount);
for (auto i = 0; i < cellCount; ++i)
{ {
asset->aabbTreeCounts = reader.readArray<Game::GfxCellTreeCount>(cellCount); auto* cellTree = &asset->aabbTrees[i];
}
if (asset->aabbTrees) if (cellTree->aabbTree)
{
asset->aabbTrees = reader.readArray<Game::GfxCellTree>(cellCount);
for (int i = 0; i < cellCount; ++i)
{ {
Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; cellTree->aabbTree = reader.readArray<Game::GfxAabbTree>(asset->aabbTreeCounts[i].aabbTreeCount);
if (cellTree->aabbTree) for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j)
{ {
cellTree->aabbTree = reader.readArray<Game::GfxAabbTree>(asset->aabbTreeCounts[i].aabbTreeCount); auto* aabbTree = &cellTree->aabbTree[j];
for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) if (aabbTree->smodelIndexes)
{ {
Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; auto* oldPointer = aabbTree->smodelIndexes;
if (builder->getAllocator()->isPointerMapped(oldPointer))
if (aabbTree->smodelIndexes)
{ {
unsigned short* oldPointer = aabbTree->smodelIndexes; // We still have to read it
if(builder->getAllocator()->isPointerMapped(oldPointer)) reader.readArray<unsigned short>(aabbTree->smodelIndexCount);
{
// We still have to read it
reader.readArray<unsigned short>(aabbTree->smodelIndexCount);
aabbTree->smodelIndexes = builder->getAllocator()->getPointer<unsigned short>(oldPointer); aabbTree->smodelIndexes = builder->getAllocator()->getPointer<unsigned short>(oldPointer);
} }
else else
{ {
aabbTree->smodelIndexes = reader.readArray<unsigned short>(aabbTree->smodelIndexCount); aabbTree->smodelIndexes = reader.readArray<unsigned short>(aabbTree->smodelIndexCount);
for (unsigned short k = 0; k < aabbTree->smodelIndexCount; ++k) for (unsigned short k = 0; k < aabbTree->smodelIndexCount; ++k)
{ {
builder->getAllocator()->mapPointer(&oldPointer[k], &aabbTree->smodelIndexes[k]); builder->getAllocator()->mapPointer(&oldPointer[k], &aabbTree->smodelIndexes[k]);
}
} }
} }
} }
} }
} }
} }
}
if (asset->cells) if (asset->cells)
{
asset->cells = reader.readArray<Game::GfxCell>(cellCount);
for (auto i = 0; i < cellCount; ++i)
{ {
asset->cells = reader.readArray<Game::GfxCell>(cellCount); auto* cell = &asset->cells[i];
for (int i = 0; i < cellCount; ++i) if (cell->portals)
{ {
Game::GfxCell* cell = &asset->cells[i]; cell->portals = reader.readArray<Game::GfxPortal>(cell->portalCount);
if (cell->portals) for (auto j = 0; j < cell->portalCount; ++j)
{ {
cell->portals = reader.readArray<Game::GfxPortal>(cell->portalCount); auto* portal = &cell->portals[j];
if (portal->vertices)
for (int j = 0; j < cell->portalCount; ++j)
{ {
Game::GfxPortal* portal = &cell->portals[j]; portal->vertices = reader.readArray<Game::vec3_t>(portal->vertexCount);
if (portal->vertices)
{
portal->vertices = reader.readArray<Game::vec3_t>(portal->vertexCount);
}
} }
} }
}
if (cell->reflectionProbes) if (cell->reflectionProbes)
{
cell->reflectionProbes = reader.readArray<char>(cell->reflectionProbeCount);
}
}
}
this->loadGfxWorldDraw(&asset->draw, builder, &reader);
// GfxLightGrid
{
if (asset->lightGrid.rowDataStart)
{
asset->lightGrid.rowDataStart = reader.readArray<unsigned short>((asset->lightGrid.maxs[asset->lightGrid.rowAxis] - asset->lightGrid.mins[asset->lightGrid.rowAxis]) + 1);
}
if (asset->lightGrid.rawRowData)
{
asset->lightGrid.rawRowData = reader.readArray<char>(asset->lightGrid.rawRowDataSize);
}
if (asset->lightGrid.entries)
{
asset->lightGrid.entries = reader.readArray<Game::GfxLightGridEntry>(asset->lightGrid.entryCount);
}
if (asset->lightGrid.colors)
{
asset->lightGrid.colors = reader.readArray<Game::GfxLightGridColors>(asset->lightGrid.colorCount);
}
}
if (asset->models)
{
asset->models = reader.readArray<Game::GfxBrushModel>(asset->modelCount);
}
if (asset->materialMemory)
{
asset->materialMemory = reader.readArray<Game::MaterialMemory>(asset->materialMemoryCount);
for (auto i = 0; i < asset->materialMemoryCount; ++i)
{
auto* materialMemory = &asset->materialMemory[i];
if (materialMemory->material)
{
auto materialName = reader.readString();
materialMemory->material = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, materialName, builder).material;
assert(materialMemory->material);
}
}
}
if (asset->sun.spriteMaterial)
{
auto materialName = reader.readString();
asset->sun.spriteMaterial = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, materialName, builder).material;
assert(asset->sun.spriteMaterial);
}
if (asset->sun.flareMaterial)
{
auto materialName = reader.readString();
asset->sun.flareMaterial = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, materialName, builder).material;
assert(asset->sun.flareMaterial);
}
if (asset->outdoorImage)
{
auto materialName = reader.readString();
asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_IMAGE, materialName, builder).image;
assert(asset->outdoorImage);
}
if (asset->primaryLightCount > 0)
{
Utils::Stream::ClearPointer(&asset->primaryLightEntityShadowVis);
}
if (asset->dpvsDyn.dynEntClientCount[0] > 0)
{
Utils::Stream::ClearPointer(&asset->sceneDynModel);
Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[0]);
Utils::Stream::ClearPointer(&asset->nonSunPrimaryLightForModelDynEnt);
}
if (asset->dpvsDyn.dynEntClientCount[1] > 0)
{
Utils::Stream::ClearPointer(&asset->sceneDynBrush);
Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[1]);
}
if (asset->shadowGeom)
{
asset->shadowGeom = reader.readArray<Game::GfxShadowGeometry>(asset->primaryLightCount);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{
auto* shadowGeometry = &asset->shadowGeom[i];
if (shadowGeometry->sortedSurfIndex)
{
shadowGeometry->sortedSurfIndex = reader.readArray<unsigned short>(shadowGeometry->surfaceCount);
}
if (shadowGeometry->smodelIndex)
{
shadowGeometry->smodelIndex = reader.readArray<unsigned short>(shadowGeometry->smodelCount);
}
}
}
if (asset->lightRegion)
{
asset->lightRegion = reader.readArray<Game::GfxLightRegion>(asset->primaryLightCount);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{
auto* lightRegion = &asset->lightRegion[i];
if (lightRegion->hulls)
{
lightRegion->hulls = reader.readArray<Game::GfxLightRegionHull>(lightRegion->hullCount);
for (unsigned int j = 0; j < lightRegion->hullCount; ++j)
{ {
cell->reflectionProbes = reader.readArray<char>(cell->reflectionProbeCount); auto* lightRegionHull = &lightRegion->hulls[j];
} if (lightRegionHull->axis)
}
}
this->loadGfxWorldDraw(&asset->draw, builder, &reader);
// GfxLightGrid
{
if (asset->lightGrid.rowDataStart)
{
asset->lightGrid.rowDataStart = reader.readArray<unsigned short>((asset->lightGrid.maxs[asset->lightGrid.rowAxis] - asset->lightGrid.mins[asset->lightGrid.rowAxis]) + 1);
}
if (asset->lightGrid.rawRowData)
{
asset->lightGrid.rawRowData = reader.readArray<char>(asset->lightGrid.rawRowDataSize);
}
if (asset->lightGrid.entries)
{
asset->lightGrid.entries = reader.readArray<Game::GfxLightGridEntry>(asset->lightGrid.entryCount);
}
if (asset->lightGrid.colors)
{
asset->lightGrid.colors = reader.readArray<Game::GfxLightGridColors>(asset->lightGrid.colorCount);
}
}
if (asset->models)
{
asset->models = reader.readArray<Game::GfxBrushModel>(asset->modelCount);
}
if (asset->materialMemory)
{
asset->materialMemory = reader.readArray<Game::MaterialMemory>(asset->materialMemoryCount);
for (int i = 0; i < asset->materialMemoryCount; ++i)
{
Game::MaterialMemory* materialMemory = &asset->materialMemory[i];
if (materialMemory->material)
{
materialMemory->material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material;
}
}
}
if (asset->sun.spriteMaterial)
{
asset->sun.spriteMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material;
}
if (asset->sun.flareMaterial)
{
asset->sun.flareMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material;
}
if (asset->outdoorImage)
{
asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image;
}
if (asset->primaryLightCount > 0)
{
Utils::Stream::ClearPointer(&asset->primaryLightEntityShadowVis);
}
if (asset->dpvsDyn.dynEntClientCount[0] > 0)
{
Utils::Stream::ClearPointer(&asset->sceneDynModel);
Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[0]);
Utils::Stream::ClearPointer(&asset->nonSunPrimaryLightForModelDynEnt);
}
if (asset->dpvsDyn.dynEntClientCount[1] > 0)
{
Utils::Stream::ClearPointer(&asset->sceneDynBrush);
Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[1]);
}
if (asset->shadowGeom)
{
asset->shadowGeom = reader.readArray<Game::GfxShadowGeometry>(asset->primaryLightCount);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{
Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i];
if (shadowGeometry->sortedSurfIndex)
{
shadowGeometry->sortedSurfIndex = reader.readArray<unsigned short>(shadowGeometry->surfaceCount);
}
if (shadowGeometry->smodelIndex)
{
shadowGeometry->smodelIndex = reader.readArray<unsigned short>(shadowGeometry->smodelCount);
}
}
}
if (asset->lightRegion)
{
asset->lightRegion = reader.readArray<Game::GfxLightRegion>(asset->primaryLightCount);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{
Game::GfxLightRegion* lightRegion = &asset->lightRegion[i];
if (lightRegion->hulls)
{
lightRegion->hulls = reader.readArray<Game::GfxLightRegionHull>(lightRegion->hullCount);
for (unsigned int j = 0; j < lightRegion->hullCount; ++j)
{ {
Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; lightRegionHull->axis = reader.readArray<Game::GfxLightRegionAxis>(lightRegionHull->axisCount);
if (lightRegionHull->axis)
{
lightRegionHull->axis = reader.readArray<Game::GfxLightRegionAxis>(lightRegionHull->axisCount);
}
} }
} }
} }
} }
}
this->loadGfxWorldDpvsStatic(asset, &asset->dpvs, builder, &reader); this->loadGfxWorldDpvsStatic(asset, &asset->dpvs, builder, &reader);
// Obsolete, IW3 has no support for that // Obsolete, IW3 has no support for that
if (asset->heroOnlyLights) if (asset->heroOnlyLights)
{ {
asset->heroOnlyLights = reader.readArray<Game::GfxHeroOnlyLight>(asset->heroOnlyLightCount); asset->heroOnlyLights = reader.readArray<Game::GfxHeroOnlyLight>(asset->heroOnlyLightCount);
}
} }
} }
void IGfxWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) void IGfxWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{ {
Game::GfxWorld* asset = header.gfxWorld; auto* asset = header.gfxWorld;
if (asset->draw.reflectionProbes) if (asset->draw.reflectionProbes)
{ {
for (unsigned int i = 0; i < asset->draw.reflectionProbeCount; ++i) for (unsigned int i = 0; i < asset->draw.reflectionProbeCount; ++i)
@ -455,7 +455,7 @@ namespace Assets
if (asset->draw.lightmaps) if (asset->draw.lightmaps)
{ {
for (int i = 0; i < asset->draw.lightmapCount; ++i) for (auto i = 0; i < asset->draw.lightmapCount; ++i)
{ {
if (asset->draw.lightmaps[i].primary) if (asset->draw.lightmaps[i].primary)
{ {
@ -599,7 +599,7 @@ namespace Assets
{ {
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxImage** imageDest = buffer->dest<Game::GfxImage*>(); auto** imageDest = buffer->dest<Game::GfxImage*>();
buffer->saveArray(asset->reflectionProbes, asset->reflectionProbeCount); buffer->saveArray(asset->reflectionProbes, asset->reflectionProbeCount);
for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i)
@ -648,13 +648,13 @@ namespace Assets
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxLightmapArray* lightmapArrayDestTable = buffer->dest<Game::GfxLightmapArray>(); auto* lightmapArrayDestTable = buffer->dest<Game::GfxLightmapArray>();
buffer->saveArray(asset->lightmaps, asset->lightmapCount); buffer->saveArray(asset->lightmaps, asset->lightmapCount);
for (int i = 0; i < asset->lightmapCount; ++i) for (int i = 0; i < asset->lightmapCount; ++i)
{ {
Game::GfxLightmapArray* lightmapArrayDest = &lightmapArrayDestTable[i]; auto* lightmapArrayDest = &lightmapArrayDestTable[i];
Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; auto* lightmapArray = &asset->lightmaps[i];
if (lightmapArray->primary) if (lightmapArray->primary)
{ {
@ -854,13 +854,13 @@ namespace Assets
SaveLogEnter("GfxSurface"); SaveLogEnter("GfxSurface");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxSurface* destSurfaceTable = buffer->dest<Game::GfxSurface>(); auto* destSurfaceTable = buffer->dest<Game::GfxSurface>();
buffer->saveArray(asset->surfaces, world->surfaceCount); buffer->saveArray(asset->surfaces, world->surfaceCount);
for (unsigned int i = 0; i < world->surfaceCount; ++i) for (unsigned int i = 0; i < world->surfaceCount; ++i)
{ {
Game::GfxSurface* surface = &asset->surfaces[i]; auto* surface = &asset->surfaces[i];
Game::GfxSurface* destSurface = &destSurfaceTable[i]; auto* destSurface = &destSurfaceTable[i];
if (surface->material) if (surface->material)
{ {
@ -890,13 +890,13 @@ namespace Assets
SaveLogEnter("GfxStaticModelDrawInst"); SaveLogEnter("GfxStaticModelDrawInst");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxStaticModelDrawInst* destModelTable = buffer->dest<Game::GfxStaticModelDrawInst>(); auto* destModelTable = buffer->dest<Game::GfxStaticModelDrawInst>();
buffer->saveArray(asset->smodelDrawInsts, asset->smodelCount); buffer->saveArray(asset->smodelDrawInsts, asset->smodelCount);
for (unsigned int i = 0; i < asset->smodelCount; ++i) for (unsigned int i = 0; i < asset->smodelCount; ++i)
{ {
Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i]; auto* model = &asset->smodelDrawInsts[i];
Game::GfxStaticModelDrawInst* destModel = &destModelTable[i]; auto* destModel = &destModelTable[i];
if (model->model) if (model->model)
{ {
@ -986,8 +986,8 @@ namespace Assets
Utils::Stream* buffer = builder->getBuffer(); Utils::Stream* buffer = builder->getBuffer();
SaveLogEnter("GfxWorld"); SaveLogEnter("GfxWorld");
Game::GfxWorld* asset = header.gfxWorld; auto* asset = header.gfxWorld;
Game::GfxWorld* dest = buffer->dest<Game::GfxWorld>(); auto* dest = buffer->dest<Game::GfxWorld>();
buffer->save(asset); buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
@ -1012,13 +1012,13 @@ namespace Assets
SaveLogEnter("GfxSky"); SaveLogEnter("GfxSky");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxSky* destSkyTable = buffer->dest<Game::GfxSky>(); auto* destSkyTable = buffer->dest<Game::GfxSky>();
buffer->saveArray(asset->skies, asset->skyCount); buffer->saveArray(asset->skies, asset->skyCount);
for (int i = 0; i < asset->skyCount; ++i) for (int i = 0; i < asset->skyCount; ++i)
{ {
Game::GfxSky* destSky = &destSkyTable[i]; auto* destSky = &destSkyTable[i];
Game::GfxSky* sky = &asset->skies[i]; auto* sky = &asset->skies[i];
if (sky->skyStartSurfs) if (sky->skyStartSurfs)
{ {
@ -1059,13 +1059,13 @@ namespace Assets
SaveLogEnter("GfxCellTree"); SaveLogEnter("GfxCellTree");
buffer->align(Utils::Stream::ALIGN_128); buffer->align(Utils::Stream::ALIGN_128);
Game::GfxCellTree* destCellTreeTable = buffer->dest<Game::GfxCellTree>(); auto* destCellTreeTable = buffer->dest<Game::GfxCellTree>();
buffer->saveArray(asset->aabbTrees, cellCount); buffer->saveArray(asset->aabbTrees, cellCount);
for (int i = 0; i < cellCount; ++i) for (int i = 0; i < cellCount; ++i)
{ {
Game::GfxCellTree* destCellTree = &destCellTreeTable[i]; auto* destCellTree = &destCellTreeTable[i];
Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; auto* cellTree = &asset->aabbTrees[i];
if (cellTree->aabbTree) if (cellTree->aabbTree)
{ {
@ -1073,7 +1073,7 @@ namespace Assets
SaveLogEnter("GfxAabbTree"); SaveLogEnter("GfxAabbTree");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxAabbTree* destAabbTreeTable = buffer->dest<Game::GfxAabbTree>(); auto* destAabbTreeTable = buffer->dest<Game::GfxAabbTree>();
buffer->saveArray(cellTree->aabbTree, asset->aabbTreeCounts[i].aabbTreeCount); buffer->saveArray(cellTree->aabbTree, asset->aabbTreeCounts[i].aabbTreeCount);
// ok this one is based on some assumptions because the actual count is this // ok this one is based on some assumptions because the actual count is this
@ -1083,8 +1083,8 @@ namespace Assets
for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j)
{ {
Game::GfxAabbTree* destAabbTree = &destAabbTreeTable[j]; auto* destAabbTree = &destAabbTreeTable[j];
Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; auto* aabbTree = &cellTree->aabbTree[j];
if (aabbTree->smodelIndexes) if (aabbTree->smodelIndexes)
{ {
@ -1122,13 +1122,13 @@ namespace Assets
SaveLogEnter("GfxCell"); SaveLogEnter("GfxCell");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxCell* destCellTable = buffer->dest<Game::GfxCell>(); auto* destCellTable = buffer->dest<Game::GfxCell>();
buffer->saveArray(asset->cells, cellCount); buffer->saveArray(asset->cells, cellCount);
for (int i = 0; i < cellCount; ++i) for (int i = 0; i < cellCount; ++i)
{ {
Game::GfxCell* destCell = &destCellTable[i]; auto* destCell = &destCellTable[i];
Game::GfxCell* cell = &asset->cells[i]; auto* cell = &asset->cells[i];
if (cell->portals) if (cell->portals)
{ {
@ -1136,13 +1136,13 @@ namespace Assets
SaveLogEnter("GfxPortal"); SaveLogEnter("GfxPortal");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxPortal* destPortalTable = buffer->dest<Game::GfxPortal>(); auto* destPortalTable = buffer->dest<Game::GfxPortal>();
buffer->saveArray(cell->portals, cell->portalCount); buffer->saveArray(cell->portals, cell->portalCount);
for (int j = 0; j < cell->portalCount; ++j) for (int j = 0; j < cell->portalCount; ++j)
{ {
Game::GfxPortal* destPortal = &destPortalTable[j]; auto* destPortal = &destPortalTable[j];
Game::GfxPortal* portal = &cell->portals[j]; auto* portal = &cell->portals[j];
if (portal->vertices) if (portal->vertices)
{ {
@ -1189,13 +1189,13 @@ namespace Assets
SaveLogEnter("MaterialMemory"); SaveLogEnter("MaterialMemory");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::MaterialMemory* destMaterialMemoryTable = buffer->dest<Game::MaterialMemory>(); auto* destMaterialMemoryTable = buffer->dest<Game::MaterialMemory>();
buffer->saveArray(asset->materialMemory, asset->materialMemoryCount); buffer->saveArray(asset->materialMemory, asset->materialMemoryCount);
for (int i = 0; i < asset->materialMemoryCount; ++i) for (int i = 0; i < asset->materialMemoryCount; ++i)
{ {
Game::MaterialMemory* destMaterialMemory = &destMaterialMemoryTable[i]; auto* destMaterialMemory = &destMaterialMemoryTable[i];
Game::MaterialMemory* materialMemory = &asset->materialMemory[i]; auto* materialMemory = &asset->materialMemory[i];
if (materialMemory->material) if (materialMemory->material)
{ {
@ -1284,13 +1284,13 @@ namespace Assets
SaveLogEnter("GfxShadowGeometry"); SaveLogEnter("GfxShadowGeometry");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxShadowGeometry* destShadowGeometryTable = buffer->dest<Game::GfxShadowGeometry>(); auto* destShadowGeometryTable = buffer->dest<Game::GfxShadowGeometry>();
buffer->saveArray(asset->shadowGeom, asset->primaryLightCount); buffer->saveArray(asset->shadowGeom, asset->primaryLightCount);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i) for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{ {
Game::GfxShadowGeometry* destShadowGeometry = &destShadowGeometryTable[i]; auto* destShadowGeometry = &destShadowGeometryTable[i];
Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i]; auto* shadowGeometry = &asset->shadowGeom[i];
if (shadowGeometry->sortedSurfIndex) if (shadowGeometry->sortedSurfIndex)
{ {
@ -1317,13 +1317,13 @@ namespace Assets
SaveLogEnter("GfxLightRegion"); SaveLogEnter("GfxLightRegion");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxLightRegion* destLightRegionTable = buffer->dest<Game::GfxLightRegion>(); auto* destLightRegionTable = buffer->dest<Game::GfxLightRegion>();
buffer->saveArray(asset->lightRegion, asset->primaryLightCount); buffer->saveArray(asset->lightRegion, asset->primaryLightCount);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i) for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{ {
Game::GfxLightRegion* destLightRegion = &destLightRegionTable[i]; auto* destLightRegion = &destLightRegionTable[i];
Game::GfxLightRegion* lightRegion = &asset->lightRegion[i]; auto* lightRegion = &asset->lightRegion[i];
if (lightRegion->hulls) if (lightRegion->hulls)
{ {
@ -1331,13 +1331,13 @@ namespace Assets
SaveLogEnter("GfxLightRegionHull"); SaveLogEnter("GfxLightRegionHull");
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
Game::GfxLightRegionHull* destLightRegionHullTable = buffer->dest<Game::GfxLightRegionHull>(); auto* destLightRegionHullTable = buffer->dest<Game::GfxLightRegionHull>();
buffer->saveArray(lightRegion->hulls, lightRegion->hullCount); buffer->saveArray(lightRegion->hulls, lightRegion->hullCount);
for (unsigned int j = 0; j < lightRegion->hullCount; ++j) for (unsigned int j = 0; j < lightRegion->hullCount; ++j)
{ {
Game::GfxLightRegionHull* destLightRegionHull = &destLightRegionHullTable[j]; auto* destLightRegionHull = &destLightRegionHullTable[j];
Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; auto* lightRegionHull = &lightRegion->hulls[j];
if (lightRegionHull->axis) if (lightRegionHull->axis)
{ {
@ -1373,7 +1373,6 @@ namespace Assets
Utils::Stream::ClearPointer(&dest->heroOnlyLights); Utils::Stream::ClearPointer(&dest->heroOnlyLights);
} }
//buffer->setPointerAssertion(false);
buffer->popBlock(); buffer->popBlock();
SaveLogExit(); SaveLogExit();
} }

View File

@ -12,7 +12,7 @@ namespace Assets
return; return;
} }
Game::LoadedSound* sound = builder->getAllocator()->allocate<Game::LoadedSound>(); auto* sound = builder->getAllocator()->allocate<Game::LoadedSound>();
if (!sound) if (!sound)
{ {
Components::Logger::Print("Error allocating memory for sound structure!\n"); Components::Logger::Print("Error allocating memory for sound structure!\n");
@ -27,16 +27,16 @@ namespace Assets
Utils::Stream::Reader reader(builder->getAllocator(), soundFile.getBuffer()); Utils::Stream::Reader reader(builder->getAllocator(), soundFile.getBuffer());
unsigned int chunkIDBuffer = reader.read<unsigned int>(); auto chunkIDBuffer = reader.read<unsigned int>();
if (chunkIDBuffer != 0x46464952) // RIFF if (chunkIDBuffer != 0x46464952) // RIFF
{ {
Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name); Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name);
return; return;
} }
unsigned int chunkSize = reader.read<unsigned int>(); auto chunkSize = reader.read<unsigned int>();
unsigned int format = reader.read<unsigned int>(); auto format = reader.read<unsigned int>();
if (format != 0x45564157) // WAVE if (format != 0x45564157) // WAVE
{ {
Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name); Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name);
@ -62,7 +62,10 @@ namespace Assets
sound->sound.info.channels = reader.read<short>(); sound->sound.info.channels = reader.read<short>();
sound->sound.info.rate = reader.read<int>(); sound->sound.info.rate = reader.read<int>();
sound->sound.info.samples = reader.read<int>();
// We read samples later, this is byte rate we don't need it
reader.read<int>();
sound->sound.info.block_size = reader.read<short>(); sound->sound.info.block_size = reader.read<short>();
sound->sound.info.bits = reader.read<short>(); sound->sound.info.bits = reader.read<short>();
@ -76,6 +79,7 @@ namespace Assets
case 0x61746164: // data case 0x61746164: // data
sound->sound.info.data_len = chunkSize; sound->sound.info.data_len = chunkSize;
sound->sound.info.samples = chunkSize / (sound->sound.info.bits / 8);
sound->sound.data = reader.readArray<char>(chunkSize); sound->sound.data = reader.readArray<char>(chunkSize);
break; break;
@ -102,9 +106,9 @@ namespace Assets
{ {
AssertSize(Game::LoadedSound, 44); AssertSize(Game::LoadedSound, 44);
Utils::Stream* buffer = builder->getBuffer(); auto* buffer = builder->getBuffer();
Game::LoadedSound* asset = header.loadSnd; auto* asset = header.loadSnd;
Game::LoadedSound* dest = buffer->dest<Game::LoadedSound>(); auto* dest = buffer->dest<Game::LoadedSound>();
buffer->save(asset); buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);

View File

@ -40,6 +40,8 @@ namespace Assets
if (!header->data) this->loadJson(header, name, builder); // Check if we want to load a material from disk if (!header->data) this->loadJson(header, name, builder); // Check if we want to load a material from disk
if (!header->data) this->loadBinary(header, name, builder); // Check if we want to load a material from disk (binary format) if (!header->data) this->loadBinary(header, name, builder); // Check if we want to load a material from disk (binary format)
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
assert(header->data);
} }
@ -206,12 +208,17 @@ namespace Assets
textureDef->u.image = nullptr; textureDef->u.image = nullptr;
if (textureJson["image"].is_string()) if (textureJson["image"].is_string())
{ {
textureDef->u.image = Components::AssetHandler::FindAssetForZone textureDef->u.image = Components::AssetHandler::FindAssetForZone(
( Game::ASSET_TYPE_IMAGE,
Game::XAssetType::ASSET_TYPE_IMAGE,
textureJson["image"].get<std::string>(), textureJson["image"].get<std::string>(),
builder builder
).image; ).image;
assert(textureDef->u.image);
}
else
{
AssertUnreachable;
} }
} }
} }

View File

@ -11,6 +11,8 @@ namespace Assets
{ {
if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
assert(header->data);
} }
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/) void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
@ -28,6 +30,7 @@ namespace Assets
*tech = nullptr; *tech = nullptr;
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name); Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
AssertUnreachable;
return; return;
} }

View File

@ -1,4 +1,6 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include <json.hpp>
#include "IPhysPreset.hpp" #include "IPhysPreset.hpp"
namespace Assets namespace Assets
@ -7,9 +9,9 @@ namespace Assets
{ {
AssertSize(Game::PhysPreset, 44); AssertSize(Game::PhysPreset, 44);
Utils::Stream* buffer = builder->getBuffer(); auto* buffer = builder->getBuffer();
Game::PhysPreset* asset = header.physPreset; auto* asset = header.physPreset;
Game::PhysPreset* dest = buffer->dest<Game::PhysPreset>(); auto* dest = buffer->dest<Game::PhysPreset>();
buffer->save(asset); buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
@ -28,4 +30,54 @@ namespace Assets
buffer->popBlock(); buffer->popBlock();
} }
void IPhysPreset::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
loadFromDisk(header, name, builder);
}
void IPhysPreset::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File physPresetFile(std::format("physpreset/{}.iw4x.json", name));
auto* asset = builder->getAllocator()->allocate<Game::PhysPreset>();
if (physPresetFile.exists())
{
nlohmann::json physPresetJson;
try
{
physPresetJson = nlohmann::json::parse(physPresetFile.getBuffer());
}
catch (const std::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for physpreset {}! {}", name, e.what());
return;
}
try
{
asset->name = builder->getAllocator()->duplicateString(physPresetJson["name"].get<std::string>());
asset->type = physPresetJson["type"].get<int>();
asset->bounce = physPresetJson["bounce"].get<float>();
asset->mass = physPresetJson["mass"].get<float>();
asset->friction = physPresetJson["friction"].get<float>();
asset->bulletForceScale = physPresetJson["bulletForceScale"].get<float>();
asset->explosiveForceScale = physPresetJson["explosiveForceScale"].get<float>();
asset->sndAliasPrefix = builder->getAllocator()->duplicateString(physPresetJson["sndAliasPrefix"].get<std::string>());
asset->piecesSpreadFraction = physPresetJson["piecesSpreadFraction"].get<float>();
asset->piecesUpwardVelocity = physPresetJson["piecesUpwardVelocity"].get<float>();
asset->tempDefaultToCylinder = physPresetJson["tempDefaultToCylinder"].get<bool>();
asset->perSurfaceSndAlias = physPresetJson["perSurfaceSndAlias"].get<bool>();
assert(asset->mass > std::numeric_limits<float>::epsilon());
}
catch (const std::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed JSON for physpreset {}! {}", name, e.what());
return;
}
}
header->physPreset = asset;
}
} }

View File

@ -8,5 +8,7 @@ namespace Assets
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_PHYSPRESET; } Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_PHYSPRESET; }
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
}; };
} }

View File

@ -1,4 +1,6 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include <json.hpp>
#include "ISndCurve.hpp" #include "ISndCurve.hpp"
namespace Assets namespace Assets
@ -7,9 +9,9 @@ namespace Assets
{ {
AssertSize(Game::SndCurve, 136); AssertSize(Game::SndCurve, 136);
Utils::Stream* buffer = builder->getBuffer(); auto* buffer = builder->getBuffer();
Game::SndCurve* asset = header.sndCurve; auto* asset = header.sndCurve;
Game::SndCurve* dest = buffer->dest<Game::SndCurve>(); auto* dest = buffer->dest<Game::SndCurve>();
buffer->save(asset); buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
@ -22,4 +24,48 @@ namespace Assets
buffer->popBlock(); buffer->popBlock();
} }
void ISndCurve::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File sndCurveFile(std::format("sndcurve/{}.iw4x.json", name));
if (!sndCurveFile.exists())
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Missing file for sndcurve {}!", name);
return;
}
nlohmann::json sndCurveJson;
try
{
sndCurveJson = nlohmann::json::parse(sndCurveFile.getBuffer());
}
catch (const std::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for sndcurve {}! {}", name, e.what());
return;
}
auto* sndCurve = builder->getAllocator()->allocate<Game::SndCurve>();
try
{
sndCurve->filename = builder->getAllocator()->duplicateString(sndCurveJson["filename"].get<std::string>());
sndCurve->knotCount = sndCurveJson["knotCount"].get<unsigned short>();
for (auto side = 0; side < 2; side++)
{
for (auto knot = 0; knot < 16; knot++)
{
sndCurve->knots[knot][side] = sndCurveJson["knots"][knot][side].get<float>();
}
}
}
catch (const std::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed JSON for sndcurve {}! {}", name, e.what());
return;
}
header->sndCurve = sndCurve;
}
} }

View File

@ -8,5 +8,6 @@ namespace Assets
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_SOUND_CURVE; } Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_SOUND_CURVE; }
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
}; };
} }

View File

@ -1,7 +1,7 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "IXAnimParts.hpp" #include "IXAnimParts.hpp"
#define IW4X_ANIM_VERSION 1 #define IW4X_ANIM_VERSION 2
namespace Assets namespace Assets
{ {
@ -9,100 +9,148 @@ namespace Assets
{ {
Components::FileSystem::File animFile(std::format("xanim/{}.iw4xAnim", name)); Components::FileSystem::File animFile(std::format("xanim/{}.iw4xAnim", name));
if (animFile.exists()) if (!animFile.exists())
{ {
Utils::Stream::Reader reader(builder->getAllocator(), animFile.getBuffer()); return;
}
__int64 magic = reader.read<__int64>(); Utils::Stream::Reader reader(builder->getAllocator(), animFile.getBuffer());
if (std::memcmp(&magic, "IW4xAnim", 8))
auto magic = reader.read<std::int64_t>();
if (std::memcmp(&magic, "IW4xAnim", 8) != 0)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, header is invalid!", name);
}
int version = reader.read<int>();
if (version > IW4X_ANIM_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, expected version is {}, but it was {}!", name, IW4X_ANIM_VERSION, version);
}
auto* xanim = reader.readArray<Game::XAnimParts>();
if (!xanim)
{
return;
}
if (xanim->name)
{
xanim->name = reader.readCString();
}
if (xanim->names)
{
xanim->names = builder->getAllocator()->allocateArray<unsigned short>(xanim->boneCount[Game::PART_TYPE_ALL]);
for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i)
{ {
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, header is invalid!", name); xanim->names[i] = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
} }
}
int version = reader.read<int>(); if (xanim->notify)
if (version != IW4X_ANIM_VERSION) {
xanim->notify = reader.readArray<Game::XAnimNotifyInfo>(xanim->notifyCount);
for (int i = 0; i < xanim->notifyCount; ++i)
{ {
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, expected version is {}, but it was {}!", name, IW4X_ANIM_VERSION, version); xanim->notify[i].name = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
} }
}
Game::XAnimParts* xanim = reader.readArray<Game::XAnimParts>(); if (xanim->dataByte)
{
xanim->dataByte = reader.readArray<char>(xanim->dataByteCount);
}
if (xanim) if (xanim->dataShort)
{
xanim->dataShort = reader.readArray<short>(xanim->dataShortCount);
}
if (xanim->dataInt)
{
xanim->dataInt = reader.readArray<int>(xanim->dataIntCount);
}
if (xanim->randomDataByte)
{
xanim->randomDataByte = reader.readArray<char>(xanim->randomDataByteCount);
}
if (xanim->randomDataShort)
{
xanim->randomDataShort = reader.readArray<short>(xanim->randomDataShortCount);
}
if (xanim->randomDataInt)
{
xanim->randomDataInt = reader.readArray<int>(xanim->randomDataIntCount);
}
if (xanim->indices.data)
{
if (xanim->numframes < 256)
{ {
if (xanim->name) xanim->indices._1 = reader.readArray<char>(xanim->indexCount);
{ }
xanim->name = reader.readCString(); else
} {
xanim->indices._2 = reader.readArray<unsigned short>(xanim->indexCount);
}
}
if (xanim->names) if (version > 1)
{
if (xanim->deltaPart)
{
xanim->deltaPart = reader.readObject<Game::XAnimDeltaPart>();
auto delta = xanim->deltaPart;
if (delta->trans)
{ {
xanim->names = builder->getAllocator()->allocateArray<unsigned short>(xanim->boneCount[Game::PART_TYPE_ALL]); delta->trans = reader.readObject<Game::XAnimPartTrans>();
for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i) if (delta->trans->size)
{ {
xanim->names[i] = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0)); delta->trans->u.frames = reader.read<Game::XAnimPartTransFrames>();
}
}
if (xanim->notify) if (xanim->numframes > 0xFF)
{ {
xanim->notify = reader.readArray<Game::XAnimNotifyInfo>(xanim->notifyCount); auto indices2 = reader.readArray<unsigned short>(delta->trans->size + 1);
std::memcpy(delta->trans->u.frames.indices._2, indices2, sizeof(short) * (delta->trans->size + 1));
}
else
{
auto indices1 = reader.readArray<char>(delta->trans->size + 1);
std::memcpy(delta->trans->u.frames.indices._1, indices1, delta->trans->size + 1);
}
for (int i = 0; i < xanim->notifyCount; ++i) if (delta->trans->u.frames.frames._1)
{ {
xanim->notify[i].name = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0)); if (delta->trans->smallTrans)
} {
} delta->trans->u.frames.frames._1 = reinterpret_cast<char(*)[3]>(3, (delta->trans->size + 1));
}
if (xanim->dataByte) else
{ {
xanim->dataByte = reader.readArray<char>(xanim->dataByteCount); delta->trans->u.frames.frames._2 = reinterpret_cast<unsigned short(*)[3]>(6, (delta->trans->size + 1));
} }
}
if (xanim->dataShort)
{
xanim->dataShort = reader.readArray<short>(xanim->dataShortCount);
}
if (xanim->dataInt)
{
xanim->dataInt = reader.readArray<int>(xanim->dataIntCount);
}
if (xanim->randomDataByte)
{
xanim->randomDataByte = reader.readArray<char>(xanim->randomDataByteCount);
}
if (xanim->randomDataShort)
{
xanim->randomDataShort = reader.readArray<short>(xanim->randomDataShortCount);
}
if (xanim->randomDataInt)
{
xanim->randomDataInt = reader.readArray<int>(xanim->randomDataIntCount);
}
if (xanim->indices.data)
{
if (xanim->numframes < 256)
{
xanim->indices._1 = reader.readArray<char>(xanim->indexCount);
} }
else else
{ {
xanim->indices._2 = reader.readArray<unsigned short>(xanim->indexCount); auto frames = reader.readObject<Game::vec3_t>();
std::memcpy(delta->trans->u.frame0, frames, sizeof(Game::vec3_t));
} }
} }
if (!reader.end())
{
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, remaining raw data found!", name);
}
header->parts = xanim;
} }
} }
if (!reader.end())
{
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, remaining raw data found!", name);
}
header->parts = xanim;
} }
void IXAnimParts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) void IXAnimParts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)

View File

@ -1,7 +1,7 @@
#include <STDInclude.hpp> #include <STDInclude.hpp>
#include "IXModel.hpp" #include "IXModel.hpp"
#define IW4X_MODEL_VERSION 8 #define IW4X_MODEL_VERSION 9
namespace Assets namespace Assets
{ {
@ -250,34 +250,32 @@ namespace Assets
if (geom->brushWrapper) if (geom->brushWrapper)
{ {
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>(); Game::BrushWrapper* brush = reader.readArrayOnce<Game::BrushWrapper>();
geom->brushWrapper = brush; geom->brushWrapper = brush;
{ {
if (brush->brush.sides) if (brush->brush.sides)
{ {
brush->brush.sides = reader.readArray<Game::cbrushside_t>(brush->brush.numsides); brush->brush.sides = reader.readArrayOnce<Game::cbrushside_t>(brush->brush.numsides);
for (unsigned short j = 0; j < brush->brush.numsides; ++j) for (unsigned short j = 0; j < brush->brush.numsides; ++j)
{ {
Game::cbrushside_t* side = &brush->brush.sides[j]; Game::cbrushside_t* side = &brush->brush.sides[j];
// TODO: Add pointer support
if (side->plane) if (side->plane)
{ {
side->plane = reader.readObject<Game::cplane_s>(); side->plane = reader.readArrayOnce<Game::cplane_s>();
} }
} }
} }
if (brush->brush.baseAdjacentSide) if (brush->brush.baseAdjacentSide)
{ {
brush->brush.baseAdjacentSide = reader.readArray<char>(brush->totalEdgeCount); brush->brush.baseAdjacentSide = reader.readArrayOnce<unsigned char>(brush->totalEdgeCount);
} }
} }
// TODO: Add pointer support
if (brush->planes) if (brush->planes)
{ {
brush->planes = reader.readArray<Game::cplane_s>(brush->brush.numsides); brush->planes = reader.readArrayOnce<Game::cplane_s>(brush->brush.numsides);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ namespace Assets
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
static void dump(Game::XAssetHeader /*header*/);
private: private:
class SModelQuadtree class SModelQuadtree
@ -89,5 +90,9 @@ namespace Assets
float x, y, z; float x, y, z;
float halfX, halfY, halfZ; float halfX, halfY, halfZ;
}; };
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
void loadFromJSON(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
}; };
} }

View File

@ -222,7 +222,10 @@ namespace Assets
{ {
alias->soundFile->exists = true; alias->soundFile->exists = true;
alias->aliasName = builder->getAllocator()->duplicateString(aliasName.get<std::string>());
// These must be THE SAME POINTER !!
// Wanna know why ? Check out 0x685646
alias->aliasName = aliasList->aliasName;
if (subtitle.is_string()) if (subtitle.is_string())
{ {
@ -244,7 +247,7 @@ namespace Assets
alias->pitchMax = pitchMax.get<float>(); alias->pitchMax = pitchMax.get<float>();
alias->distMin = distMin.get<float>(); alias->distMin = distMin.get<float>();
alias->distMax = distMax.get<float>(); alias->distMax = distMax.get<float>();
alias->flags = flags.get<int>(); alias->flags.intValue = flags.get<int>();
alias->___u15.slavePercentage = slavePercentage.get<float>(); alias->___u15.slavePercentage = slavePercentage.get<float>();
alias->probability = probability.get<float>(); alias->probability = probability.get<float>();
alias->lfePercentage = lfePercentage.get<float>(); alias->lfePercentage = lfePercentage.get<float>();
@ -308,6 +311,9 @@ namespace Assets
} }
auto curve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, fallOffCurve, builder).sndCurve; auto curve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, fallOffCurve, builder).sndCurve;
assert(curve);
alias->volumeFalloffCurve = curve; alias->volumeFalloffCurve = curve;
} }
@ -320,11 +326,10 @@ namespace Assets
{ {
alias->soundFile->type = Game::SAT_STREAMED; alias->soundFile->type = Game::SAT_STREAMED;
std::string streamedFile = soundFile.get<std::string>(); auto streamedFile = soundFile.get<std::string>();
std::string directory = ""s; std::string directory;
int split = streamedFile.find_last_of('/'); auto split = streamedFile.find_last_of('/');
if (split != std::string::npos)
if (split >= 0)
{ {
directory = streamedFile.substr(0, split); directory = streamedFile.substr(0, split);
streamedFile = streamedFile.substr(split+1); streamedFile = streamedFile.substr(split+1);
@ -379,6 +384,127 @@ namespace Assets
} }
} }
void Isnd_alias_list_t::dump(Game::XAssetHeader header)
{
nlohmann::json output;
Utils::Memory::Allocator strDuplicator;
auto ents = header.sound;
auto head = nlohmann::json::array_t();
for (size_t i = 0; i < ents->count; i++)
{
Game::snd_alias_t alias = ents->head[i];
auto channelMaps = nlohmann::json::array_t();
for (size_t j = 0; j < 2; j++)
{
for (size_t k = 0; k < 2; k++)
{
auto iw3ChannelMap = alias.speakerMap->channelMaps[j][k];
auto speakers = nlohmann::json::array_t();
for (size_t speakerIndex = 0; speakerIndex < iw3ChannelMap.speakerCount; speakerIndex++)
{
auto iw4Speaker = iw3ChannelMap.speakers[speakerIndex];
nlohmann::json::object_t speaker;
speaker.emplace("levels0", iw4Speaker.numLevels > 0 ? iw4Speaker.levels[0] : 0);
speaker.emplace("levels1", iw4Speaker.numLevels > 1 ? iw4Speaker.levels[1] : 0);
speaker.emplace("numLevels", iw4Speaker.numLevels);
speaker.emplace("speaker", iw4Speaker.speaker);
speakers.emplace_back(speaker);
}
auto channelMap = nlohmann::json::object_t();
channelMap.emplace("entryCount", iw3ChannelMap.speakerCount);
channelMap.emplace("speakers", speakers);
channelMaps.emplace_back(channelMap);
}
}
auto speakerMap = nlohmann::json::object_t();
speakerMap.emplace("channelMaps", channelMaps);
speakerMap.emplace("isDefault", alias.speakerMap->isDefault);
speakerMap.emplace("name", (alias.speakerMap->name));
std::string soundFile;
if (alias.soundFile)
{
switch (alias.soundFile->type)
{
// LOADED
case Game::snd_alias_type_t::SAT_LOADED:
// Save the LoadedSound subasset
soundFile = alias.soundFile->u.loadSnd->name;
break;
// STREAMED
case Game::snd_alias_type_t::SAT_STREAMED:
{
soundFile = alias.soundFile->u.streamSnd.filename.info.raw.name;
if (alias.soundFile->u.streamSnd.filename.info.raw.dir)
{
soundFile = Utils::String::VA("%s/%s", alias.soundFile->u.streamSnd.filename.info.raw.dir, soundFile.c_str());
}
break;
}
// I DON'T KNOW :(
default:
Components::Logger::Print("Error dumping sound alias %s: unknown format %d\n", alias.aliasName, alias.soundFile->type);
return;
}
}
else
{
Components::Logger::Print("Error dumping sound alias %s: NULL soundfile!\n", alias.aliasName);
return;
}
auto iw4Flags = alias.flags.intValue;
auto json_alias = nlohmann::json::object_t();
json_alias.emplace("aliasName", (alias.aliasName));
json_alias.emplace("centerPercentage", alias.centerPercentage);
json_alias.emplace("chainAliasName", (alias.chainAliasName == nullptr ? nlohmann::json() : alias.chainAliasName));
json_alias.emplace("distMax", alias.distMax);
json_alias.emplace("distMin", alias.distMin);
json_alias.emplace("envelopMax", alias.envelopMax);
json_alias.emplace("envelopMin", alias.envelopMin);
json_alias.emplace("envelopPercentage", alias.envelopPercentage);
json_alias.emplace("flags", iw4Flags);
json_alias.emplace("lfePercentage", alias.lfePercentage);
json_alias.emplace("mixerGroup", nlohmann::json());
json_alias.emplace("pitchMax", alias.pitchMax);
json_alias.emplace("pitchMin", alias.pitchMin);
json_alias.emplace("probability", alias.probability);
json_alias.emplace("secondaryAliasName", (alias.secondaryAliasName == nullptr ? nlohmann::json() : alias.secondaryAliasName));
json_alias.emplace("sequence", alias.sequence);
json_alias.emplace("slavePercentage", alias.___u15.slavePercentage);
json_alias.emplace("speakerMap", speakerMap);
json_alias.emplace("soundFile", (strDuplicator.duplicateString(soundFile)));
json_alias.emplace("startDelay", alias.startDelay);
json_alias.emplace("subtitle", (alias.subtitle == nullptr ? nlohmann::json() : alias.subtitle));
json_alias.emplace("type", alias.soundFile->type);
json_alias.emplace("volMax", alias.volMax);
json_alias.emplace("volMin", alias.volMin);
json_alias.emplace("volumeFalloffCurve", (alias.volumeFalloffCurve->filename));
head.emplace_back(json_alias);
}
output.emplace("aliasName", (ents->aliasName));
output.emplace("count", ents->count);
output.emplace("head", head);
const auto dump = output.dump(4);
Utils::IO::WriteFile(std::format("raw/sounds/{}.json", ents->aliasName), dump);
}
void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{ {
AssertSize(Game::snd_alias_list_t, 12); AssertSize(Game::snd_alias_list_t, 12);
@ -392,8 +518,16 @@ namespace Assets
if (asset->aliasName) if (asset->aliasName)
{ {
buffer->saveString(builder->getAssetName(this->getType(), asset->aliasName)); if (builder->hasPointer(asset->aliasName))
Utils::Stream::ClearPointer(&dest->aliasName); {
dest->aliasName = builder->getPointer(asset->aliasName);
}
else
{
builder->storePointer(asset->aliasName);
buffer->saveString(asset->aliasName);
Utils::Stream::ClearPointer(&dest->aliasName);
}
} }
if (asset->head) if (asset->head)
@ -419,8 +553,16 @@ namespace Assets
if (alias->aliasName) if (alias->aliasName)
{ {
buffer->saveString(alias->aliasName); if (builder->hasPointer(alias->aliasName))
Utils::Stream::ClearPointer(&destAlias->aliasName); {
destAlias->aliasName = builder->getPointer(alias->aliasName);
}
else
{
builder->storePointer(alias->aliasName);
buffer->saveString(alias->aliasName);
Utils::Stream::ClearPointer(&destAlias->aliasName);
}
} }
if (alias->subtitle) if (alias->subtitle)
@ -467,7 +609,7 @@ namespace Assets
{ {
if (alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED) if (alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED)
{ {
destSoundFile->u.loadSnd = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, alias->soundFile->u.loadSnd).loadSnd; destSoundFile->u.loadSnd = builder->saveSubAsset(Game::ASSET_TYPE_LOADED_SOUND, alias->soundFile->u.loadSnd).loadSnd;
} }
else else
{ {
@ -494,7 +636,7 @@ namespace Assets
if (alias->volumeFalloffCurve) if (alias->volumeFalloffCurve)
{ {
destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve; destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve;
} }
if (alias->speakerMap) if (alias->speakerMap)

View File

@ -10,5 +10,6 @@ namespace Assets
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override; void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
void dump(Game::XAssetHeader header) override;
}; };
} }

View File

@ -242,6 +242,11 @@ namespace Components
void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum) void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum)
{ {
if (!DebugOverlay)
{
return;
}
switch (DebugOverlay->current.integer) switch (DebugOverlay->current.integer)
{ {
case 2: case 2:

View File

@ -80,11 +80,12 @@ namespace Components
void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args) void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args)
{ {
const auto msg = std::vformat(fmt, args);
#ifdef _DEBUG #ifdef _DEBUG
if (IsDebuggerPresent()) __debugbreak(); if (IsDebuggerPresent()) __debugbreak();
#endif #endif
const auto msg = std::vformat(fmt, args);
Game::Com_Error(error, "%s", msg.data()); Game::Com_Error(error, "%s", msg.data());
} }

View File

@ -15,6 +15,7 @@ namespace Components
Dvar::Var Renderer::r_drawAABBTrees; Dvar::Var Renderer::r_drawAABBTrees;
Dvar::Var Renderer::r_playerDrawDebugDistance; Dvar::Var Renderer::r_playerDrawDebugDistance;
Dvar::Var Renderer::r_forceTechnique; Dvar::Var Renderer::r_forceTechnique;
Dvar::Var Renderer::r_drawRunners;
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f }; float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
@ -118,10 +119,13 @@ namespace Components
} }
} }
void Renderer::R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state) void Renderer::R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state, int samplerCode)
{ {
Logger::Error(Game::ERR_FATAL, "Tried to use sampler '{}' when it isn't valid for material '{}' and technique '{}'", Logger::Error(
sampler, state->material->info.name, state->technique->name); Game::ERR_FATAL,
"Tried to use sampler #{} ('{}') at the wrong timing! Additional info:\nMaterial: '{}'\nTechnique {}\nTechnique slot: {}\nTechnique flags:{}\nPass: {}\nPixel shader: {}\n",
samplerCode, sampler, state->material->info.name, state->technique->name, (int)state->techType, state->technique->flags, state->passIndex, state->pixelShader->name
);
} }
__declspec(naked) void Renderer::StoreGfxBufContextPtrStub1() __declspec(naked) void Renderer::StoreGfxBufContextPtrStub1()
@ -154,10 +158,11 @@ namespace Components
// show error // show error
pushad pushad
push eax
push ebx push ebx
push edx push edx
call R_TextureFromCodeError call R_TextureFromCodeError
add esp, 8 add esp, 0xC
popad popad
// go back // go back
@ -361,7 +366,7 @@ namespace Components
if (staticModel->model) if (staticModel->model)
{ {
Game::R_AddDebugBounds(staticModelsColor, b); Game::R_AddDebugBounds(staticModelsColor, b);
} }
} }
} }
@ -458,6 +463,41 @@ namespace Components
} }
} }
void Renderer::DebugDrawRunners()
{
if (!Game::CL_IsCgameInitialized())
{
return;
}
if (r_drawRunners.get<bool>())
{
auto* fxSystem = reinterpret_cast<Game::FxSystem*>(0x173F200);
if (fxSystem)
{
for (auto i = 0; i < fxSystem->activeElemCount; i++)
{
auto* elem = &fxSystem->effects[i];
if (elem->def)
{
Game::R_AddDebugString(sceneModelsColor, elem->frameNow.origin, 1.0f, elem->def->name);
}
}
}
auto soundCount = *reinterpret_cast<int*>(0x7C5C90);
auto* sounds = reinterpret_cast<Game::ClientEntSound*>(0x7C5CA0);
for (auto i = 0; i < soundCount; i++)
{
if (sounds[i].aliasList)
{
Game::R_AddDebugString(staticModelsColor, sounds[i].origin, 1.0f, sounds[i].aliasList->aliasName);
}
}
}
}
void Renderer::DebugDrawAABBTrees() void Renderer::DebugDrawAABBTrees()
{ {
if (!r_drawAABBTrees.get<bool>()) return; if (!r_drawAABBTrees.get<bool>()) return;
@ -507,6 +547,7 @@ namespace Components
{ {
if (Game::CL_IsCgameInitialized()) if (Game::CL_IsCgameInitialized())
{ {
DebugDrawRunners();
DebugDrawAABBTrees(); DebugDrawAABBTrees();
DebugDrawModelNames(); DebugDrawModelNames();
DebugDrawModelBoundingBoxes(); DebugDrawModelBoundingBoxes();
@ -561,6 +602,7 @@ namespace Components
nullptr nullptr
}; };
Renderer::r_drawRunners = Game::Dvar_RegisterBool("r_drawRunners", false, Game::DVAR_NONE, "Draw active sound & fx runners");
Renderer::r_drawModelBoundingBoxes = Game::Dvar_RegisterEnum("r_drawModelBoundingBoxes", values, 0, Game::DVAR_CHEAT, "Draw scene model bounding boxes"); Renderer::r_drawModelBoundingBoxes = Game::Dvar_RegisterEnum("r_drawModelBoundingBoxes", values, 0, Game::DVAR_CHEAT, "Draw scene model bounding boxes");
Renderer::r_drawSceneModelCollisions = Game::Dvar_RegisterBool("r_drawSceneModelCollisions", false, Game::DVAR_CHEAT, "Draw scene model collisions"); Renderer::r_drawSceneModelCollisions = Game::Dvar_RegisterBool("r_drawSceneModelCollisions", false, Game::DVAR_CHEAT, "Draw scene model collisions");
Renderer::r_drawTriggers = Game::Dvar_RegisterBool("r_drawTriggers", false, Game::DVAR_CHEAT, "Draw triggers"); Renderer::r_drawTriggers = Game::Dvar_RegisterBool("r_drawTriggers", false, Game::DVAR_CHEAT, "Draw triggers");

View File

@ -28,7 +28,7 @@ namespace Components
static void PostVidRestart(); static void PostVidRestart();
static void PostVidRestartStub(); static void PostVidRestartStub();
static void R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state); static void R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state, int samplerCode);
static void StoreGfxBufContextPtrStub1(); static void StoreGfxBufContextPtrStub1();
static void StoreGfxBufContextPtrStub2(); static void StoreGfxBufContextPtrStub2();
@ -38,6 +38,7 @@ namespace Components
static void DebugDrawSceneModelCollisions(); static void DebugDrawSceneModelCollisions();
static void DebugDrawModelBoundingBoxes(); static void DebugDrawModelBoundingBoxes();
static void DebugDrawModelNames(); static void DebugDrawModelNames();
static void DebugDrawRunners();
static void DebugDrawAABBTrees(); static void DebugDrawAABBTrees();
static void ForceTechnique(); static void ForceTechnique();
@ -50,6 +51,7 @@ namespace Components
static Utils::Signal<BackendCallback> SingleBackendFrameSignal; static Utils::Signal<BackendCallback> SingleBackendFrameSignal;
static Dvar::Var r_drawTriggers; static Dvar::Var r_drawTriggers;
static Dvar::Var r_drawRunners;
static Dvar::Var r_drawSceneModelCollisions; static Dvar::Var r_drawSceneModelCollisions;
static Dvar::Var r_drawModelBoundingBoxes; static Dvar::Var r_drawModelBoundingBoxes;
static Dvar::Var r_drawModelNames; static Dvar::Var r_drawModelNames;

View File

@ -882,8 +882,8 @@ namespace Game
{ {
float normal[3]; float normal[3];
float dist; float dist;
char type; unsigned char type;
char pad[3]; unsigned char pad[3];
}; };
struct cbrushside_t struct cbrushside_t
@ -896,13 +896,13 @@ namespace Game
struct cbrush_t struct cbrush_t
{ {
unsigned __int16 numsides; unsigned short numsides;
unsigned __int16 glassPieceIndex; unsigned short glassPieceIndex;
cbrushside_t* sides; cbrushside_t* sides;
char* baseAdjacentSide; unsigned char* baseAdjacentSide;
__int16 axialMaterialNum[2][3]; unsigned short axialMaterialNum[2][3];
char firstAdjacentSideOffsets[2][3]; unsigned char firstAdjacentSideOffsets[2][3];
char edgeCount[2][3]; unsigned char edgeCount[2][3];
}; };
struct BrushWrapper struct BrushWrapper
@ -2497,6 +2497,189 @@ namespace Game
SAT_COUNT = 0x4, SAT_COUNT = 0x4,
}; };
struct snd_volume_info_t
{
float volume;
float goalvolume;
float goalrate;
};
struct snd_channelvolgroup
{
snd_volume_info_t channelvol[64];
bool active;
};
struct snd_background_info_t
{
float goalvolume;
float goalrate;
};
struct snd_enveffect
{
int roomtype;
float drylevel;
float drygoal;
float dryrate;
float wetlevel;
float wetgoal;
float wetrate;
bool active;
};
struct orientation_t
{
float origin[3];
float axis[3][3];
};
struct snd_listener
{
orientation_t orient;
float velocity;
int clientNum;
bool active;
};
struct snd_amplifier
{
snd_listener* listener;
int minRadius;
int maxRadius;
float falloffExp;
float minVol;
float maxVol;
};
struct snd_entchannel_info_t
{
char name[64];
int priority;
bool is3d;
bool isRestricted;
bool isPausable;
int maxVoices;
int voiceCount;
};
struct snd_entchan_overrides_t
{
unsigned int isPausable[2];
float timescaleLerp[64];
};
enum SndFileLoadingState
{
SFLS_UNLOADED = 0x0,
SFLS_LOADING = 0x1,
SFLS_LOADED = 0x2,
};
struct SndFileSpecificChannelInfo
{
SndFileLoadingState loadingState;
int srcChannelCount;
int baserate;
};
union SndEntHandle
{
struct
{
unsigned int entIndex;
} field;
int handle;
};
enum SndLengthId
{
SndLengthNotify_Subtitle = 0x0,
SndLengthNotify_EntityCustom = 0x1,
SndLengthNotifyCount = 0x2,
};
struct sndLengthNotifyInfo
{
SndLengthId id[4];
void* data[4];
int count;
};
enum snd_alias_system_t
{
SASYS_UI = 0x0,
SASYS_CGAME = 0x1,
SASYS_GAME = 0x2,
SASYS_COUNT = 0x3,
};
struct snd_channel_info_t
{
SndFileSpecificChannelInfo soundFileInfo;
SndEntHandle sndEnt;
int entchannel;
int startDelay;
int looptime;
int totalMsec;
int playbackId;
sndLengthNotifyInfo lengthNotifyInfo;
float basevolume;
float pitch;
struct snd_alias_t* alias0;
struct snd_alias_t* alias1;
int saveIndex0;
int saveIndex1;
float lerp;
float org[3];
float offset[3];
bool paused;
bool master;
float timescaleLerp;
snd_alias_system_t system;
};
struct snd_local_t
{
bool Initialized2d;
bool Initialized3d;
bool paused;
int playbackIdCounter;
unsigned int playback_rate;
int playback_channels;
float timescale;
int pausetime;
int cpu;
struct
{
char buffer[16384];
volatile int size;
bool compress;
} restore;
float volume;
snd_volume_info_t mastervol;
snd_channelvolgroup channelVolGroups[4];
snd_channelvolgroup* channelvol;
snd_background_info_t background[4];
int ambient_track;
float slaveLerp;
float masterPercentage;
snd_enveffect envEffects[5];
snd_enveffect* effect;
snd_listener listeners[2];
int time;
int looptime;
snd_amplifier amplifier;
snd_entchannel_info_t entchaninfo[64];
snd_entchan_overrides_t entchanOverrides;
int entchannel_count;
snd_channel_info_t chaninfo[52];
int max_2D_channels;
int max_3D_channels;
int max_stream_channels;
};
struct SoundFile struct SoundFile
{ {
char type; char type;
@ -2504,12 +2687,6 @@ namespace Game
SoundFileRef u; SoundFileRef u;
}; };
union $C8D87EB0090687D323381DFB7A82089C
{
float slavePercentage;
float masterPercentage;
};
struct SndCurve struct SndCurve
{ {
const char* filename; const char* filename;
@ -2537,6 +2714,26 @@ namespace Game
MSSChannelMap channelMaps[2][2]; MSSChannelMap channelMaps[2][2];
}; };
union SoundAliasFlags
{
#pragma warning(push)
#pragma warning(disable: 4201)
struct
{
unsigned int looping : 1; // & 1 / 0x1 / 0000 0000 0000 0001
unsigned int isMaster : 1; // & 2 / 0x2 / 0000 0000 0000 0010
unsigned int isSlave : 1; // & 4 / 0x4 / 0000 0000 0000 0100
unsigned int fullDryLevel : 1; // & 8 / 0x8 / 0000 0000 0000 1000
unsigned int noWetLevel : 1; // & 16 / 0x10 / 0000 0000 0001 0000
unsigned int unknown : 1; // & 32 / 0x20 / 0000 0000 0010 0000
unsigned int unk_is3D : 1; // & 64 / 0x40 / 0000 0000 0100 0000 // CONFIRMED IW4 IW5
unsigned int type : 2; // & 384 / 0x180 / 0000 0001 1000 0000 // CONFIRMED IW4 IW5
unsigned int channel : 6; // & 32256 / 0x7E00 / 0111 1110 0000 0000 // CONFIRMED IW4 IW5
};
#pragma warning(pop)
unsigned int intValue;
};
const struct snd_alias_t const struct snd_alias_t
{ {
const char* aliasName; const char* aliasName;
@ -2553,8 +2750,12 @@ namespace Game
float distMin; float distMin;
float distMax; float distMax;
float velocityMin; float velocityMin;
int flags; SoundAliasFlags flags;
$C8D87EB0090687D323381DFB7A82089C ___u15; union
{
float slavePercentage;
float masterPercentage;
} ___u15;
float probability; float probability;
float lfePercentage; float lfePercentage;
float centerPercentage; float centerPercentage;
@ -2637,7 +2838,7 @@ namespace Game
struct cLeafBrushNode_s struct cLeafBrushNode_s
{ {
char axis; unsigned char axis;
__int16 leafBrushCount; __int16 leafBrushCount;
int contents; int contents;
cLeafBrushNodeData_t data; cLeafBrushNodeData_t data;
@ -2654,9 +2855,9 @@ namespace Game
struct CollisionPartition struct CollisionPartition
{ {
char triCount; unsigned char triCount;
char borderCount; unsigned char borderCount;
char firstVertSegment; unsigned char firstVertSegment;
int firstTri; int firstTri;
CollisionBorder* borders; CollisionBorder* borders;
}; };
@ -2993,7 +3194,7 @@ namespace Game
{ {
const char* name; const char* name;
int isInUse; int isInUse;
int planeCount; unsigned int planeCount;
cplane_s* planes; cplane_s* planes;
unsigned int numStaticModels; unsigned int numStaticModels;
cStaticModel_s* staticModelList; cStaticModel_s* staticModelList;
@ -3002,7 +3203,7 @@ namespace Game
unsigned int numBrushSides; unsigned int numBrushSides;
cbrushside_t* brushsides; cbrushside_t* brushsides;
unsigned int numBrushEdges; unsigned int numBrushEdges;
char* brushEdges; unsigned char* brushEdges;
unsigned int numNodes; unsigned int numNodes;
cNode_t* nodes; cNode_t* nodes;
unsigned int numLeafs; unsigned int numLeafs;
@ -3014,15 +3215,15 @@ namespace Game
unsigned int numLeafSurfaces; unsigned int numLeafSurfaces;
unsigned int* leafsurfaces; unsigned int* leafsurfaces;
unsigned int vertCount; unsigned int vertCount;
float(*verts)[3]; vec3_t* verts;
int triCount; unsigned int triCount;
unsigned __int16* triIndices; unsigned __int16* triIndices;
char* triEdgeIsWalkable; unsigned char* triEdgeIsWalkable;
int borderCount; unsigned int borderCount;
CollisionBorder* borders; CollisionBorder* borders;
int partitionCount; int partitionCount;
CollisionPartition* partitions; CollisionPartition* partitions;
int aabbTreeCount; unsigned int aabbTreeCount;
CollisionAabbTree* aabbTrees; CollisionAabbTree* aabbTrees;
unsigned int numSubModels; unsigned int numSubModels;
cmodel_t* cmodels; cmodel_t* cmodels;
@ -3394,9 +3595,9 @@ namespace Game
float texCoordOrigin[2]; float texCoordOrigin[2];
unsigned int supportMask; unsigned int supportMask;
float areaX2; float areaX2;
char defIndex; unsigned char defIndex;
char vertCount; unsigned char vertCount;
char fanDataCount; unsigned char fanDataCount;
char pad[1]; char pad[1];
}; };
@ -8654,6 +8855,12 @@ namespace Game
unsigned __int16 children; unsigned __int16 children;
}; };
struct ClientEntSound
{
float origin[3];
snd_alias_list_t* aliasList;
};
struct FxEffect struct FxEffect
{ {
const FxEffectDef* def; const FxEffectDef* def;
@ -10672,6 +10879,124 @@ namespace Game
HANDLE handle; HANDLE handle;
}; };
struct FxCamera
{
float origin[3];
volatile int isValid;
float frustum[6][4];
float axis[3][3];
unsigned int frustumPlaneCount;
float viewOffset[3];
bool thermal;
unsigned int pad[2];
};
struct r_double_index_t
{
unsigned __int16 value[2];
};
struct FxSpriteInfo
{
r_double_index_t* indices;
unsigned int indexCount;
Material* material;
const char* name;
};
struct FxVisBlocker
{
float origin[3];
unsigned __int16 radius;
unsigned __int16 visibility;
};
struct FxVisState
{
FxVisBlocker blocker[256];
volatile int blockerCount;
unsigned int pad[3];
};
struct FxElem
{
char defIndex;
char sequence;
char atRestFraction;
char emitResidual;
unsigned __int16 nextElemHandleInEffect;
unsigned __int16 prevElemHandleInEffect;
int msecBegin;
float baseVel[3];
union
{
int physObjId;
float origin[3];
} ___u8;
union
{
unsigned __int16 lightingHandle;
unsigned __int16 sparkCloudHandle;
unsigned __int16 sparkFountainHandle;
} u;
};
struct FxSystem
{
FxCamera camera;
FxCamera cameraPrev;
FxSpriteInfo sprite;
FxEffect* effects;
FxElem *elems;
void* trails;
void* trailElems;
void* bolts;
void* sparkClouds;
void* sparkFountains;
void* sparkFountainClusters;
unsigned __int16* deferredElems;
volatile int firstFreeElem;
volatile int firstFreeTrailElem;
volatile int firstFreeTrail;
volatile int firstFreeBolt;
volatile int firstFreeSparkCloud;
volatile int firstFreeSparkFountain;
volatile int firstFreeSparkFountainCluster;
volatile int deferredElemCount;
volatile int activeElemCount;
volatile int activeTrailElemCount;
volatile int activeTrailCount;
volatile int activeBoltCount;
volatile int activeSparkCloudCount;
volatile int activeSparkFountainCount;
volatile int activeSparkFountainClusterCount;
volatile int gfxCloudCount;
FxVisState* visState;
FxVisState* visStateBufferRead;
FxVisState* visStateBufferWrite;
volatile int firstActiveEffect;
volatile int firstNewEffect;
volatile int firstFreeEffect;
unsigned __int16 allEffectHandles[1024];
volatile int activeSpotLightEffectCount;
volatile int activeSpotLightElemCount;
unsigned __int16 activeSpotLightEffectHandle;
unsigned __int16 activeSpotLightElemHandle;
__int16 activeSpotLightBoltDobj;
volatile int iteratorCount;
int msecNow;
volatile int msecDraw;
int frameCount;
bool isInitialized;
bool needsGarbageCollection;
bool isArchiving;
char localClientNum;
unsigned int restartList[32];
FxEffect** restartEffectsList;
unsigned int restartCount;
unsigned int pad1[14];
};
#pragma endregion #pragma endregion
#ifndef IDA #ifndef IDA

View File

@ -62,4 +62,12 @@ namespace Utils::Json
return input.to_ulong(); return input.to_ulong();
} }
Game::Bounds ReadBounds(const nlohmann::json_abi_v3_11_2::json value)
{
Game::Bounds bounds{};
Utils::Json::CopyArray(bounds.midPoint, value["midPoint"]);
Utils::Json::CopyArray(bounds.halfSize, value["halfSize"]);
return bounds;
}
} }

View File

@ -1,9 +1,25 @@
#pragma once #pragma once
#include <json.hpp> #include <json.hpp>
namespace Utils::Json namespace Utils::Json
{ {
std::string TypeToString(nlohmann::json::value_t type); std::string TypeToString(nlohmann::json::value_t type);
unsigned long ReadFlags(std::string binaryFlags, size_t size); unsigned long ReadFlags(const std::string binaryFlags, size_t size);
Game::Bounds ReadBounds(const nlohmann::json_abi_v3_11_2::json value);
template <typename T> void CopyArray(T* destination, const nlohmann::json_abi_v3_11_2::json& json_member, size_t count = 0)
{
if (count == 0)
{
count = json_member.size();
}
for (size_t i = 0; i < count; i++)
{
destination[i] = json_member[i].get<T>();
}
}
} }

View File

@ -7,7 +7,7 @@ namespace Utils::Maths
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
} }
void VectorSubtract(float va[3], float vb[3], float out[3]) void VectorSubtract(const float va[3], const float vb[3], float out[3])
{ {
out[0] = va[0] - vb[0]; out[0] = va[0] - vb[0];
out[1] = va[1] - vb[1]; out[1] = va[1] - vb[1];
@ -35,7 +35,7 @@ namespace Utils::Maths
out[2] = v[2] * scale; out[2] = v[2] * scale;
} }
float Vec3SqrDistance(float v1[3], float v2[3]) float Vec3SqrDistance(const float v1[3], const float v2[3])
{ {
float out[3]; float out[3];

View File

@ -7,9 +7,9 @@ namespace Utils::Maths
constexpr auto VectorNegate(float x[3]) { x[0] = -x[0]; x[1] = -x[1]; x[2] = -x[2]; } constexpr auto VectorNegate(float x[3]) { x[0] = -x[0]; x[1] = -x[1]; x[2] = -x[2]; }
float DotProduct(float v1[3], float v2[3]); float DotProduct(float v1[3], float v2[3]);
void VectorSubtract(float va[3], float vb[3], float out[3]); void VectorSubtract(const float va[3], const float vb[3], float out[3]);
void VectorAdd(float va[3], float vb[3], float out[3]); void VectorAdd(float va[3], float vb[3], float out[3]);
void VectorCopy(float in[3], float out[3]); void VectorCopy(float in[3], float out[3]);
void VectorScale(float v[3], float scale, float out[3]); void VectorScale(float v[3], float scale, float out[3]);
float Vec3SqrDistance(float v1[3], float v2[3]); float Vec3SqrDistance(const float v1[3], const float v2[3]);
} }

View File

@ -16,14 +16,14 @@ namespace Utils
const char* Stream::Reader::readCString() const char* Stream::Reader::readCString()
{ {
return this->allocator->duplicateString(this->readString()); return this->allocator_->duplicateString(this->readString());
} }
char Stream::Reader::readByte() char Stream::Reader::readByte()
{ {
if ((this->position + 1) <= this->buffer.size()) if ((this->position_ + 1) <= this->buffer_.size())
{ {
return this->buffer[this->position++]; return this->buffer_[this->position_++];
} }
throw std::runtime_error("Reading past the buffer"); throw std::runtime_error("Reading past the buffer");
@ -31,45 +31,45 @@ namespace Utils
void* Stream::Reader::read(size_t size, std::size_t count) void* Stream::Reader::read(size_t size, std::size_t count)
{ {
size_t bytes = size * count; auto bytes = size * count;
if ((this->position + bytes) <= this->buffer.size()) if ((this->position_ + bytes) <= this->buffer_.size())
{ {
void* _buffer = this->allocator->allocate(bytes); auto* buffer = this->allocator_->allocate(bytes);
std::memcpy(_buffer, this->buffer.data() + this->position, bytes); std::memcpy(buffer, this->buffer_.data() + this->position_, bytes);
this->position += bytes; this->position_ += bytes;
return _buffer; return buffer;
} }
throw std::runtime_error("Reading past the buffer"); throw std::runtime_error("Reading past the buffer");
} }
bool Stream::Reader::end() bool Stream::Reader::end() const
{ {
return (this->buffer.size() == this->position); return (this->buffer_.size() == this->position_);
} }
void Stream::Reader::seek(unsigned int _position) void Stream::Reader::seek(unsigned int position)
{ {
if (this->buffer.size() >= _position) if (this->buffer_.size() >= position)
{ {
this->position = _position; this->position_ = position;
} }
} }
void Stream::Reader::seekRelative(unsigned int _position) void Stream::Reader::seekRelative(unsigned int position)
{ {
return this->seek(_position + this->position); return this->seek(position + this->position_);
} }
void* Stream::Reader::readPointer() void* Stream::Reader::readPointer()
{ {
void* pointer = this->read<void*>(); auto* pointer = this->read<void*>();
if (!this->hasPointer(pointer)) if (!this->hasPointer(pointer))
{ {
this->pointerMap[pointer] = nullptr; this->pointerMap_[pointer] = nullptr;
} }
return pointer; return pointer;
} }
@ -78,18 +78,18 @@ namespace Utils
{ {
if (this->hasPointer(oldPointer)) if (this->hasPointer(oldPointer))
{ {
this->pointerMap[oldPointer] = newPointer; this->pointerMap_[oldPointer] = newPointer;
} }
} }
bool Stream::Reader::hasPointer(void* pointer) bool Stream::Reader::hasPointer(void* pointer) const
{ {
return this->pointerMap.find(pointer) != this->pointerMap.end(); return this->pointerMap_.contains(pointer);
} }
Stream::Stream() : ptrAssertion(false), criticalSectionState(0) Stream::Stream() : ptrAssertion(false), criticalSectionState(0)
{ {
memset(this->blockSize, 0, sizeof(this->blockSize)); std::memset(this->blockSize, 0, sizeof(this->blockSize));
#ifdef WRITE_LOGS #ifdef WRITE_LOGS
this->structLevel = 0; this->structLevel = 0;
@ -99,12 +99,12 @@ namespace Utils
Stream::Stream(size_t size) : Stream() Stream::Stream(size_t size) : Stream()
{ {
this->buffer.reserve(size); this->buffer_.reserve(size);
} }
Stream::~Stream() Stream::~Stream()
{ {
this->buffer.clear(); this->buffer_.clear();
if (this->criticalSectionState != 0) if (this->criticalSectionState != 0)
{ {
@ -114,12 +114,12 @@ namespace Utils
std::size_t Stream::length() const std::size_t Stream::length() const
{ {
return this->buffer.length(); return this->buffer_.length();
} }
std::size_t Stream::capacity() const std::size_t Stream::capacity() const
{ {
return this->buffer.capacity(); return this->buffer_.capacity();
} }
void Stream::assertPointer(const void* pointer, std::size_t length) void Stream::assertPointer(const void* pointer, std::size_t length)
@ -159,7 +159,7 @@ namespace Utils
return this->at(); return this->at();
} }
auto data = this->data(); auto* data = this->data();
if (this->isCriticalSection() && this->length() + (size * count) > this->capacity()) if (this->isCriticalSection() && this->length() + (size * count) > this->capacity())
{ {
@ -167,7 +167,7 @@ namespace Utils
__debugbreak(); __debugbreak();
} }
this->buffer.append(static_cast<const char*>(_str), size * count); this->buffer_.append(static_cast<const char*>(_str), size * count);
if (this->data() != data && this->isCriticalSection()) if (this->data() != data && this->isCriticalSection())
{ {
@ -319,7 +319,7 @@ namespace Utils
char* Stream::data() char* Stream::data()
{ {
return const_cast<char*>(this->buffer.data()); return const_cast<char*>(this->buffer_.data());
} }
unsigned int Stream::getBlockSize(Game::XFILE_BLOCK_TYPES stream) unsigned int Stream::getBlockSize(Game::XFILE_BLOCK_TYPES stream)

View File

@ -21,13 +21,13 @@ namespace Utils
int criticalSectionState; int criticalSectionState;
unsigned int blockSize[Game::MAX_XFILE_COUNT]; unsigned int blockSize[Game::MAX_XFILE_COUNT];
std::vector<Game::XFILE_BLOCK_TYPES> streamStack; std::vector<Game::XFILE_BLOCK_TYPES> streamStack;
std::string buffer; std::string buffer_;
public: public:
class Reader class Reader
{ {
public: public:
Reader(Memory::Allocator* _allocator, const std::string& _buffer) : position(0), buffer(_buffer), allocator(_allocator) {} Reader(Memory::Allocator* allocator, std::string& buffer) : position_(0), buffer_(std::move(buffer)), allocator_(allocator) {}
std::string readString(); std::string readString();
const char* readCString(); const char* readCString();
@ -53,18 +53,18 @@ namespace Utils
auto ptr = read<int>(); auto ptr = read<int>();
auto* voidPtr = reinterpret_cast<void*>(ptr); auto* voidPtr = reinterpret_cast<void*>(ptr);
if (allocator->isPointerMapped(voidPtr)) if (this->allocator_->isPointerMapped(voidPtr))
{ {
return allocator->getPointer<T>(voidPtr); return this->allocator_->getPointer<T>(voidPtr);
} }
throw std::runtime_error("Bad data: missing ptr"); throw std::runtime_error("Bad data: missing ptr");
} }
case FOLLOWING: case FOLLOWING:
{ {
auto filePosition = position; auto filePosition = this->position_;
auto data = readArray<T>(count); auto data = readArray<T>(count);
allocator->mapPointer(reinterpret_cast<void*>(filePosition), data); this->allocator_->mapPointer(reinterpret_cast<void*>(filePosition), data);
return data; return data;
} }
default: default:
@ -89,19 +89,19 @@ namespace Utils
return obj; return obj;
} }
bool end(); bool end() const;
void seek(unsigned int position); void seek(unsigned int position);
void seekRelative(unsigned int position); void seekRelative(unsigned int position);
void* readPointer(); void* readPointer();
void mapPointer(void* oldPointer, void* newPointer); void mapPointer(void* oldPointer, void* newPointer);
bool hasPointer(void* pointer); bool hasPointer(void* pointer) const;
private: private:
unsigned int position; unsigned int position_;
std::string buffer; std::string buffer_;
std::map<void*, void*> pointerMap; std::map<void*, void*> pointerMap_;
Memory::Allocator* allocator; Memory::Allocator* allocator_;
}; };
enum Alignment enum Alignment
@ -123,11 +123,13 @@ namespace Utils
Stream(size_t size); Stream(size_t size);
~Stream(); ~Stream();
std::unordered_map<void*, size_t> dataPointers;
[[nodiscard]] std::size_t length() const; [[nodiscard]] std::size_t length() const;
[[nodiscard]] std::size_t capacity() const; [[nodiscard]] std::size_t capacity() const;
char* save(const void * _str, std::size_t size, std::size_t count = 1); char* save(const void * str, std::size_t size, std::size_t count = 1);
char* save(Game::XFILE_BLOCK_TYPES stream, const void * _str, std::size_t size, std::size_t count); char* save(Game::XFILE_BLOCK_TYPES stream, const void * str, std::size_t size, std::size_t count);
char* save(Game::XFILE_BLOCK_TYPES stream, int value, std::size_t count); char* save(Game::XFILE_BLOCK_TYPES stream, int value, std::size_t count);
template <typename T> char* save(T* object) template <typename T> char* save(T* object)
@ -135,6 +137,42 @@ namespace Utils
return saveArray<T>(object, 1); return saveArray<T>(object, 1);
} }
template <typename T> char* saveObject(T value)
{
return saveArray(&value, 1);
}
template <typename T> void saveArrayIfNotExisting(T* data, size_t count)
{
#define POINTER 255
#define FOLLOWING 254
if (const auto itr = dataPointers.find(data); itr != dataPointers.end())
{
saveByte(POINTER);
saveObject(itr->second);
}
else
{
saveByte(FOLLOWING);
dataPointers.insert_or_assign(reinterpret_cast<void*>(data), length());
saveArray(data, count);
}
}
char* save(int value, size_t count = 1)
{
auto ret = this->length();
for (size_t i = 0; i < count; ++i)
{
this->save(&value, 4, 1);
}
return this->data() + ret;
}
template <typename T> char* saveArray(T* array, std::size_t count) template <typename T> char* saveArray(T* array, std::size_t count)
{ {
return save(array, sizeof(T), count); return save(array, sizeof(T), count);