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 "IFxEffectDef.hpp"
#define IW4X_FX_VERSION 1
#define IW4X_FX_VERSION 2
namespace Assets
{
@ -65,132 +65,139 @@ namespace Assets
void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
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>();
if (std::memcmp(&magic, "IW4xFx ", 8))
Utils::Stream::Reader buffer(builder->getAllocator(), fxFile.getBuffer());
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 (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)
if (elemDef->velSamples)
{
Game::FxElemDef* elemDef = &asset->elemDefs[i];
elemDef->velSamples = buffer.readArray<Game::FxElemVelStateSample>(elemDef->velIntervalCount + 1);
}
if (elemDef->velSamples)
{
elemDef->velSamples = buffer.readArray<Game::FxElemVelStateSample>(elemDef->velIntervalCount + 1);
}
if (elemDef->visSamples)
{
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);
}
// Save_FxElemDefVisuals
{
if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL)
if (elemDef->visuals.markArray)
{
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);
for (char j = 0; j < elemDef->visualCount; ++j)
if (elemDef->visuals.markArray[j].materials[0])
{
if (elemDef->visuals.markArray[j].materials[0])
{
elemDef->visuals.markArray[j].materials[0] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, buffer.readString().data(), builder).material;
}
elemDef->visuals.markArray[j].materials[0] = Components::AssetHandler::FindAssetForZone(Game::ASSET_TYPE_MATERIAL, buffer.readString(), builder).material;
}
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;
}
if (elemDef->visuals.markArray[j].materials[1])
{
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->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.sparkFountainDef)
{
if (elemDef->extended.trailDef)
{
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);
}
}
elemDef->extended.sparkFountainDef = buffer.readObject<Game::FxSparkFountainDef>();
}
}
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
}
#else
(name);
(void)name;
#endif
}

View File

@ -12,13 +12,13 @@ namespace Assets
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
private:
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 markFxElemVisuals(Game::FxElemVisuals* visuals, 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 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 <json.hpp>
#include "IFxWorld.hpp"
#define IW4X_FXWORLD_VERSION 1
namespace Assets
{
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)
{
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;
if (map) return;
@ -194,7 +354,7 @@ namespace Assets
// Generate
map = builder->getAllocator()->allocate<Game::FxWorld>();
map->name = builder->getAllocator()->duplicateString(name);
// No glass for you!
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 mark(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);
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"];
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++)
{
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->glassPieces = builder->getAllocator()->allocateArray<Game::G_GlassPiece>(glassData->pieceCount);

View File

@ -3,27 +3,6 @@
#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
{
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)
{
Game::GfxSurface* surface = &asset->surfaces[i];
auto* surface = &asset->surfaces[i];
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)
{
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);
}
@ -86,7 +77,7 @@ namespace Assets
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)
{
Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i];
auto* lightmapArray = &asset->lightmaps[i];
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)
{
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)
{
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)
{
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
@ -156,295 +146,305 @@ namespace Assets
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>();
if (std::memcmp(&magic, "IW4xGfxW", 8))
Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer());
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 (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)
if (sky->skyStartSurfs)
{
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)
{
sky->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image;
}
asset->dpvsPlanes.planes = clip->planes;
}
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)
{
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);
}
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)
{
asset->aabbTrees = reader.readArray<Game::GfxCellTree>(cellCount);
for (int i = 0; i < cellCount; ++i)
if (cellTree->aabbTree)
{
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];
if (aabbTree->smodelIndexes)
auto* oldPointer = aabbTree->smodelIndexes;
if (builder->getAllocator()->isPointerMapped(oldPointer))
{
unsigned short* oldPointer = aabbTree->smodelIndexes;
if(builder->getAllocator()->isPointerMapped(oldPointer))
{
// We still have to read it
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);
}
else
{
aabbTree->smodelIndexes = reader.readArray<unsigned short>(aabbTree->smodelIndexCount);
aabbTree->smodelIndexes = builder->getAllocator()->getPointer<unsigned short>(oldPointer);
}
else
{
aabbTree->smodelIndexes = reader.readArray<unsigned short>(aabbTree->smodelIndexCount);
for (unsigned short k = 0; k < aabbTree->smodelIndexCount; ++k)
{
builder->getAllocator()->mapPointer(&oldPointer[k], &aabbTree->smodelIndexes[k]);
}
for (unsigned short k = 0; k < aabbTree->smodelIndexCount; ++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);
for (int j = 0; j < cell->portalCount; ++j)
auto* portal = &cell->portals[j];
if (portal->vertices)
{
Game::GfxPortal* portal = &cell->portals[j];
if (portal->vertices)
{
portal->vertices = reader.readArray<Game::vec3_t>(portal->vertexCount);
}
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);
}
}
}
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)
auto* lightRegionHull = &lightRegion->hulls[j];
if (lightRegionHull->axis)
{
Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j];
if (lightRegionHull->axis)
{
lightRegionHull->axis = reader.readArray<Game::GfxLightRegionAxis>(lightRegionHull->axisCount);
}
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
if (asset->heroOnlyLights)
{
asset->heroOnlyLights = reader.readArray<Game::GfxHeroOnlyLight>(asset->heroOnlyLightCount);
}
// Obsolete, IW3 has no support for that
if (asset->heroOnlyLights)
{
asset->heroOnlyLights = reader.readArray<Game::GfxHeroOnlyLight>(asset->heroOnlyLightCount);
}
}
void IGfxWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
Game::GfxWorld* asset = header.gfxWorld;
auto* asset = header.gfxWorld;
if (asset->draw.reflectionProbes)
{
for (unsigned int i = 0; i < asset->draw.reflectionProbeCount; ++i)
@ -455,7 +455,7 @@ namespace Assets
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)
{
@ -599,7 +599,7 @@ namespace Assets
{
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);
for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i)
@ -648,13 +648,13 @@ namespace Assets
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);
for (int i = 0; i < asset->lightmapCount; ++i)
{
Game::GfxLightmapArray* lightmapArrayDest = &lightmapArrayDestTable[i];
Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i];
auto* lightmapArrayDest = &lightmapArrayDestTable[i];
auto* lightmapArray = &asset->lightmaps[i];
if (lightmapArray->primary)
{
@ -854,13 +854,13 @@ namespace Assets
SaveLogEnter("GfxSurface");
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);
for (unsigned int i = 0; i < world->surfaceCount; ++i)
{
Game::GfxSurface* surface = &asset->surfaces[i];
Game::GfxSurface* destSurface = &destSurfaceTable[i];
auto* surface = &asset->surfaces[i];
auto* destSurface = &destSurfaceTable[i];
if (surface->material)
{
@ -890,13 +890,13 @@ namespace Assets
SaveLogEnter("GfxStaticModelDrawInst");
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);
for (unsigned int i = 0; i < asset->smodelCount; ++i)
{
Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i];
Game::GfxStaticModelDrawInst* destModel = &destModelTable[i];
auto* model = &asset->smodelDrawInsts[i];
auto* destModel = &destModelTable[i];
if (model->model)
{
@ -986,8 +986,8 @@ namespace Assets
Utils::Stream* buffer = builder->getBuffer();
SaveLogEnter("GfxWorld");
Game::GfxWorld* asset = header.gfxWorld;
Game::GfxWorld* dest = buffer->dest<Game::GfxWorld>();
auto* asset = header.gfxWorld;
auto* dest = buffer->dest<Game::GfxWorld>();
buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
@ -1012,13 +1012,13 @@ namespace Assets
SaveLogEnter("GfxSky");
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);
for (int i = 0; i < asset->skyCount; ++i)
{
Game::GfxSky* destSky = &destSkyTable[i];
Game::GfxSky* sky = &asset->skies[i];
auto* destSky = &destSkyTable[i];
auto* sky = &asset->skies[i];
if (sky->skyStartSurfs)
{
@ -1059,13 +1059,13 @@ namespace Assets
SaveLogEnter("GfxCellTree");
buffer->align(Utils::Stream::ALIGN_128);
Game::GfxCellTree* destCellTreeTable = buffer->dest<Game::GfxCellTree>();
auto* destCellTreeTable = buffer->dest<Game::GfxCellTree>();
buffer->saveArray(asset->aabbTrees, cellCount);
for (int i = 0; i < cellCount; ++i)
{
Game::GfxCellTree* destCellTree = &destCellTreeTable[i];
Game::GfxCellTree* cellTree = &asset->aabbTrees[i];
auto* destCellTree = &destCellTreeTable[i];
auto* cellTree = &asset->aabbTrees[i];
if (cellTree->aabbTree)
{
@ -1073,7 +1073,7 @@ namespace Assets
SaveLogEnter("GfxAabbTree");
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);
// 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)
{
Game::GfxAabbTree* destAabbTree = &destAabbTreeTable[j];
Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j];
auto* destAabbTree = &destAabbTreeTable[j];
auto* aabbTree = &cellTree->aabbTree[j];
if (aabbTree->smodelIndexes)
{
@ -1122,13 +1122,13 @@ namespace Assets
SaveLogEnter("GfxCell");
buffer->align(Utils::Stream::ALIGN_4);
Game::GfxCell* destCellTable = buffer->dest<Game::GfxCell>();
auto* destCellTable = buffer->dest<Game::GfxCell>();
buffer->saveArray(asset->cells, cellCount);
for (int i = 0; i < cellCount; ++i)
{
Game::GfxCell* destCell = &destCellTable[i];
Game::GfxCell* cell = &asset->cells[i];
auto* destCell = &destCellTable[i];
auto* cell = &asset->cells[i];
if (cell->portals)
{
@ -1136,13 +1136,13 @@ namespace Assets
SaveLogEnter("GfxPortal");
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);
for (int j = 0; j < cell->portalCount; ++j)
{
Game::GfxPortal* destPortal = &destPortalTable[j];
Game::GfxPortal* portal = &cell->portals[j];
auto* destPortal = &destPortalTable[j];
auto* portal = &cell->portals[j];
if (portal->vertices)
{
@ -1189,13 +1189,13 @@ namespace Assets
SaveLogEnter("MaterialMemory");
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);
for (int i = 0; i < asset->materialMemoryCount; ++i)
{
Game::MaterialMemory* destMaterialMemory = &destMaterialMemoryTable[i];
Game::MaterialMemory* materialMemory = &asset->materialMemory[i];
auto* destMaterialMemory = &destMaterialMemoryTable[i];
auto* materialMemory = &asset->materialMemory[i];
if (materialMemory->material)
{
@ -1284,13 +1284,13 @@ namespace Assets
SaveLogEnter("GfxShadowGeometry");
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);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{
Game::GfxShadowGeometry* destShadowGeometry = &destShadowGeometryTable[i];
Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i];
auto* destShadowGeometry = &destShadowGeometryTable[i];
auto* shadowGeometry = &asset->shadowGeom[i];
if (shadowGeometry->sortedSurfIndex)
{
@ -1317,13 +1317,13 @@ namespace Assets
SaveLogEnter("GfxLightRegion");
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);
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
{
Game::GfxLightRegion* destLightRegion = &destLightRegionTable[i];
Game::GfxLightRegion* lightRegion = &asset->lightRegion[i];
auto* destLightRegion = &destLightRegionTable[i];
auto* lightRegion = &asset->lightRegion[i];
if (lightRegion->hulls)
{
@ -1331,13 +1331,13 @@ namespace Assets
SaveLogEnter("GfxLightRegionHull");
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);
for (unsigned int j = 0; j < lightRegion->hullCount; ++j)
{
Game::GfxLightRegionHull* destLightRegionHull = &destLightRegionHullTable[j];
Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j];
auto* destLightRegionHull = &destLightRegionHullTable[j];
auto* lightRegionHull = &lightRegion->hulls[j];
if (lightRegionHull->axis)
{
@ -1373,7 +1373,6 @@ namespace Assets
Utils::Stream::ClearPointer(&dest->heroOnlyLights);
}
//buffer->setPointerAssertion(false);
buffer->popBlock();
SaveLogExit();
}

View File

@ -12,7 +12,7 @@ namespace Assets
return;
}
Game::LoadedSound* sound = builder->getAllocator()->allocate<Game::LoadedSound>();
auto* sound = builder->getAllocator()->allocate<Game::LoadedSound>();
if (!sound)
{
Components::Logger::Print("Error allocating memory for sound structure!\n");
@ -27,16 +27,16 @@ namespace Assets
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
{
Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name);
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
{
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.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.bits = reader.read<short>();
@ -76,6 +79,7 @@ namespace Assets
case 0x61746164: // data
sound->sound.info.data_len = chunkSize;
sound->sound.info.samples = chunkSize / (sound->sound.info.bits / 8);
sound->sound.data = reader.readArray<char>(chunkSize);
break;
@ -102,9 +106,9 @@ namespace Assets
{
AssertSize(Game::LoadedSound, 44);
Utils::Stream* buffer = builder->getBuffer();
Game::LoadedSound* asset = header.loadSnd;
Game::LoadedSound* dest = buffer->dest<Game::LoadedSound>();
auto* buffer = builder->getBuffer();
auto* asset = header.loadSnd;
auto* dest = buffer->dest<Game::LoadedSound>();
buffer->save(asset);
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->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
assert(header->data);
}
@ -206,12 +208,17 @@ namespace Assets
textureDef->u.image = nullptr;
if (textureJson["image"].is_string())
{
textureDef->u.image = Components::AssetHandler::FindAssetForZone
(
Game::XAssetType::ASSET_TYPE_IMAGE,
textureDef->u.image = Components::AssetHandler::FindAssetForZone(
Game::ASSET_TYPE_IMAGE,
textureJson["image"].get<std::string>(),
builder
).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->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*/)
@ -28,6 +30,7 @@ namespace Assets
*tech = nullptr;
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
AssertUnreachable;
return;
}

View File

@ -1,4 +1,6 @@
#include <STDInclude.hpp>
#include <json.hpp>
#include "IPhysPreset.hpp"
namespace Assets
@ -7,9 +9,9 @@ namespace Assets
{
AssertSize(Game::PhysPreset, 44);
Utils::Stream* buffer = builder->getBuffer();
Game::PhysPreset* asset = header.physPreset;
Game::PhysPreset* dest = buffer->dest<Game::PhysPreset>();
auto* buffer = builder->getBuffer();
auto* asset = header.physPreset;
auto* dest = buffer->dest<Game::PhysPreset>();
buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
@ -28,4 +30,54 @@ namespace Assets
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; }
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 <json.hpp>
#include "ISndCurve.hpp"
namespace Assets
@ -7,9 +9,9 @@ namespace Assets
{
AssertSize(Game::SndCurve, 136);
Utils::Stream* buffer = builder->getBuffer();
Game::SndCurve* asset = header.sndCurve;
Game::SndCurve* dest = buffer->dest<Game::SndCurve>();
auto* buffer = builder->getBuffer();
auto* asset = header.sndCurve;
auto* dest = buffer->dest<Game::SndCurve>();
buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
@ -22,4 +24,48 @@ namespace Assets
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; }
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 "IXAnimParts.hpp"
#define IW4X_ANIM_VERSION 1
#define IW4X_ANIM_VERSION 2
namespace Assets
{
@ -9,100 +9,148 @@ namespace Assets
{
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>();
if (std::memcmp(&magic, "IW4xAnim", 8))
Utils::Stream::Reader reader(builder->getAllocator(), animFile.getBuffer());
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 (version != IW4X_ANIM_VERSION)
if (xanim->notify)
{
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->name = reader.readCString();
}
xanim->indices._1 = reader.readArray<char>(xanim->indexCount);
}
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]);
for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i)
delta->trans = reader.readObject<Game::XAnimPartTrans>();
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)
{
xanim->notify = reader.readArray<Game::XAnimNotifyInfo>(xanim->notifyCount);
if (xanim->numframes > 0xFF)
{
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)
{
xanim->notify[i].name = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
}
}
if (xanim->dataByte)
{
xanim->dataByte = reader.readArray<char>(xanim->dataByteCount);
}
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);
if (delta->trans->u.frames.frames._1)
{
if (delta->trans->smallTrans)
{
delta->trans->u.frames.frames._1 = reinterpret_cast<char(*)[3]>(3, (delta->trans->size + 1));
}
else
{
delta->trans->u.frames.frames._2 = reinterpret_cast<unsigned short(*)[3]>(6, (delta->trans->size + 1));
}
}
}
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)

View File

@ -1,7 +1,7 @@
#include <STDInclude.hpp>
#include "IXModel.hpp"
#define IW4X_MODEL_VERSION 8
#define IW4X_MODEL_VERSION 9
namespace Assets
{
@ -250,34 +250,32 @@ namespace Assets
if (geom->brushWrapper)
{
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>();
Game::BrushWrapper* brush = reader.readArrayOnce<Game::BrushWrapper>();
geom->brushWrapper = brush;
{
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)
{
Game::cbrushside_t* side = &brush->brush.sides[j];
// TODO: Add pointer support
if (side->plane)
{
side->plane = reader.readObject<Game::cplane_s>();
side->plane = reader.readArrayOnce<Game::cplane_s>();
}
}
}
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)
{
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 mark(Game::XAssetHeader header, 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:
class SModelQuadtree
@ -89,5 +90,9 @@ namespace Assets
float x, y, z;
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->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())
{
@ -244,7 +247,7 @@ namespace Assets
alias->pitchMax = pitchMax.get<float>();
alias->distMin = distMin.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->probability = probability.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;
assert(curve);
alias->volumeFalloffCurve = curve;
}
@ -320,11 +326,10 @@ namespace Assets
{
alias->soundFile->type = Game::SAT_STREAMED;
std::string streamedFile = soundFile.get<std::string>();
std::string directory = ""s;
int split = streamedFile.find_last_of('/');
if (split >= 0)
auto streamedFile = soundFile.get<std::string>();
std::string directory;
auto split = streamedFile.find_last_of('/');
if (split != std::string::npos)
{
directory = streamedFile.substr(0, split);
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)
{
AssertSize(Game::snd_alias_list_t, 12);
@ -392,8 +518,16 @@ namespace Assets
if (asset->aliasName)
{
buffer->saveString(builder->getAssetName(this->getType(), asset->aliasName));
Utils::Stream::ClearPointer(&dest->aliasName);
if (builder->hasPointer(asset->aliasName))
{
dest->aliasName = builder->getPointer(asset->aliasName);
}
else
{
builder->storePointer(asset->aliasName);
buffer->saveString(asset->aliasName);
Utils::Stream::ClearPointer(&dest->aliasName);
}
}
if (asset->head)
@ -419,8 +553,16 @@ namespace Assets
if (alias->aliasName)
{
buffer->saveString(alias->aliasName);
Utils::Stream::ClearPointer(&destAlias->aliasName);
if (builder->hasPointer(alias->aliasName))
{
destAlias->aliasName = builder->getPointer(alias->aliasName);
}
else
{
builder->storePointer(alias->aliasName);
buffer->saveString(alias->aliasName);
Utils::Stream::ClearPointer(&destAlias->aliasName);
}
}
if (alias->subtitle)
@ -467,7 +609,7 @@ namespace Assets
{
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
{
@ -494,7 +636,7 @@ namespace Assets
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)

View File

@ -10,5 +10,6 @@ namespace Assets
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 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)
{
if (!DebugOverlay)
{
return;
}
switch (DebugOverlay->current.integer)
{
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)
{
const auto msg = std::vformat(fmt, args);
#ifdef _DEBUG
if (IsDebuggerPresent()) __debugbreak();
#endif
const auto msg = std::vformat(fmt, args);
Game::Com_Error(error, "%s", msg.data());
}

View File

@ -15,6 +15,7 @@ namespace Components
Dvar::Var Renderer::r_drawAABBTrees;
Dvar::Var Renderer::r_playerDrawDebugDistance;
Dvar::Var Renderer::r_forceTechnique;
Dvar::Var Renderer::r_drawRunners;
float cyan[4] = { 0.0f, 0.5f, 0.5f, 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 '{}'",
sampler, state->material->info.name, state->technique->name);
Logger::Error(
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()
@ -154,10 +158,11 @@ namespace Components
// show error
pushad
push eax
push ebx
push edx
call R_TextureFromCodeError
add esp, 8
add esp, 0xC
popad
// go back
@ -361,7 +366,7 @@ namespace Components
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()
{
if (!r_drawAABBTrees.get<bool>()) return;
@ -507,6 +547,7 @@ namespace Components
{
if (Game::CL_IsCgameInitialized())
{
DebugDrawRunners();
DebugDrawAABBTrees();
DebugDrawModelNames();
DebugDrawModelBoundingBoxes();
@ -561,6 +602,7 @@ namespace Components
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_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");

View File

@ -28,7 +28,7 @@ namespace Components
static void PostVidRestart();
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 StoreGfxBufContextPtrStub2();
@ -38,6 +38,7 @@ namespace Components
static void DebugDrawSceneModelCollisions();
static void DebugDrawModelBoundingBoxes();
static void DebugDrawModelNames();
static void DebugDrawRunners();
static void DebugDrawAABBTrees();
static void ForceTechnique();
@ -50,6 +51,7 @@ namespace Components
static Utils::Signal<BackendCallback> SingleBackendFrameSignal;
static Dvar::Var r_drawTriggers;
static Dvar::Var r_drawRunners;
static Dvar::Var r_drawSceneModelCollisions;
static Dvar::Var r_drawModelBoundingBoxes;
static Dvar::Var r_drawModelNames;

View File

@ -882,8 +882,8 @@ namespace Game
{
float normal[3];
float dist;
char type;
char pad[3];
unsigned char type;
unsigned char pad[3];
};
struct cbrushside_t
@ -896,13 +896,13 @@ namespace Game
struct cbrush_t
{
unsigned __int16 numsides;
unsigned __int16 glassPieceIndex;
unsigned short numsides;
unsigned short glassPieceIndex;
cbrushside_t* sides;
char* baseAdjacentSide;
__int16 axialMaterialNum[2][3];
char firstAdjacentSideOffsets[2][3];
char edgeCount[2][3];
unsigned char* baseAdjacentSide;
unsigned short axialMaterialNum[2][3];
unsigned char firstAdjacentSideOffsets[2][3];
unsigned char edgeCount[2][3];
};
struct BrushWrapper
@ -2497,6 +2497,189 @@ namespace Game
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
{
char type;
@ -2504,12 +2687,6 @@ namespace Game
SoundFileRef u;
};
union $C8D87EB0090687D323381DFB7A82089C
{
float slavePercentage;
float masterPercentage;
};
struct SndCurve
{
const char* filename;
@ -2537,6 +2714,26 @@ namespace Game
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 char* aliasName;
@ -2553,8 +2750,12 @@ namespace Game
float distMin;
float distMax;
float velocityMin;
int flags;
$C8D87EB0090687D323381DFB7A82089C ___u15;
SoundAliasFlags flags;
union
{
float slavePercentage;
float masterPercentage;
} ___u15;
float probability;
float lfePercentage;
float centerPercentage;
@ -2637,7 +2838,7 @@ namespace Game
struct cLeafBrushNode_s
{
char axis;
unsigned char axis;
__int16 leafBrushCount;
int contents;
cLeafBrushNodeData_t data;
@ -2654,9 +2855,9 @@ namespace Game
struct CollisionPartition
{
char triCount;
char borderCount;
char firstVertSegment;
unsigned char triCount;
unsigned char borderCount;
unsigned char firstVertSegment;
int firstTri;
CollisionBorder* borders;
};
@ -2993,7 +3194,7 @@ namespace Game
{
const char* name;
int isInUse;
int planeCount;
unsigned int planeCount;
cplane_s* planes;
unsigned int numStaticModels;
cStaticModel_s* staticModelList;
@ -3002,7 +3203,7 @@ namespace Game
unsigned int numBrushSides;
cbrushside_t* brushsides;
unsigned int numBrushEdges;
char* brushEdges;
unsigned char* brushEdges;
unsigned int numNodes;
cNode_t* nodes;
unsigned int numLeafs;
@ -3014,15 +3215,15 @@ namespace Game
unsigned int numLeafSurfaces;
unsigned int* leafsurfaces;
unsigned int vertCount;
float(*verts)[3];
int triCount;
vec3_t* verts;
unsigned int triCount;
unsigned __int16* triIndices;
char* triEdgeIsWalkable;
int borderCount;
unsigned char* triEdgeIsWalkable;
unsigned int borderCount;
CollisionBorder* borders;
int partitionCount;
CollisionPartition* partitions;
int aabbTreeCount;
unsigned int aabbTreeCount;
CollisionAabbTree* aabbTrees;
unsigned int numSubModels;
cmodel_t* cmodels;
@ -3394,9 +3595,9 @@ namespace Game
float texCoordOrigin[2];
unsigned int supportMask;
float areaX2;
char defIndex;
char vertCount;
char fanDataCount;
unsigned char defIndex;
unsigned char vertCount;
unsigned char fanDataCount;
char pad[1];
};
@ -8654,6 +8855,12 @@ namespace Game
unsigned __int16 children;
};
struct ClientEntSound
{
float origin[3];
snd_alias_list_t* aliasList;
};
struct FxEffect
{
const FxEffectDef* def;
@ -10672,6 +10879,124 @@ namespace Game
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
#ifndef IDA

View File

@ -62,4 +62,12 @@ namespace Utils::Json
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
#include <json.hpp>
namespace Utils::Json
{
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];
}
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[1] = va[1] - vb[1];
@ -35,7 +35,7 @@ namespace Utils::Maths
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];

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]; }
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 VectorCopy(float in[3], 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()
{
return this->allocator->duplicateString(this->readString());
return this->allocator_->duplicateString(this->readString());
}
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");
@ -31,45 +31,45 @@ namespace Utils
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);
this->position += bytes;
std::memcpy(buffer, this->buffer_.data() + this->position_, bytes);
this->position_ += bytes;
return _buffer;
return 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* pointer = this->read<void*>();
auto* pointer = this->read<void*>();
if (!this->hasPointer(pointer))
{
this->pointerMap[pointer] = nullptr;
this->pointerMap_[pointer] = nullptr;
}
return pointer;
}
@ -78,18 +78,18 @@ namespace Utils
{
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)
{
memset(this->blockSize, 0, sizeof(this->blockSize));
std::memset(this->blockSize, 0, sizeof(this->blockSize));
#ifdef WRITE_LOGS
this->structLevel = 0;
@ -99,12 +99,12 @@ namespace Utils
Stream::Stream(size_t size) : Stream()
{
this->buffer.reserve(size);
this->buffer_.reserve(size);
}
Stream::~Stream()
{
this->buffer.clear();
this->buffer_.clear();
if (this->criticalSectionState != 0)
{
@ -114,12 +114,12 @@ namespace Utils
std::size_t Stream::length() const
{
return this->buffer.length();
return this->buffer_.length();
}
std::size_t Stream::capacity() const
{
return this->buffer.capacity();
return this->buffer_.capacity();
}
void Stream::assertPointer(const void* pointer, std::size_t length)
@ -159,7 +159,7 @@ namespace Utils
return this->at();
}
auto data = this->data();
auto* data = this->data();
if (this->isCriticalSection() && this->length() + (size * count) > this->capacity())
{
@ -167,7 +167,7 @@ namespace Utils
__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())
{
@ -319,7 +319,7 @@ namespace Utils
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)

View File

@ -21,13 +21,13 @@ namespace Utils
int criticalSectionState;
unsigned int blockSize[Game::MAX_XFILE_COUNT];
std::vector<Game::XFILE_BLOCK_TYPES> streamStack;
std::string buffer;
std::string buffer_;
public:
class Reader
{
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();
const char* readCString();
@ -53,18 +53,18 @@ namespace Utils
auto ptr = read<int>();
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");
}
case FOLLOWING:
{
auto filePosition = position;
auto filePosition = this->position_;
auto data = readArray<T>(count);
allocator->mapPointer(reinterpret_cast<void*>(filePosition), data);
this->allocator_->mapPointer(reinterpret_cast<void*>(filePosition), data);
return data;
}
default:
@ -89,19 +89,19 @@ namespace Utils
return obj;
}
bool end();
bool end() const;
void seek(unsigned int position);
void seekRelative(unsigned int position);
void* readPointer();
void mapPointer(void* oldPointer, void* newPointer);
bool hasPointer(void* pointer);
bool hasPointer(void* pointer) const;
private:
unsigned int position;
std::string buffer;
std::map<void*, void*> pointerMap;
Memory::Allocator* allocator;
unsigned int position_;
std::string buffer_;
std::map<void*, void*> pointerMap_;
Memory::Allocator* allocator_;
};
enum Alignment
@ -123,11 +123,13 @@ namespace Utils
Stream(size_t size);
~Stream();
std::unordered_map<void*, size_t> dataPointers;
[[nodiscard]] std::size_t length() const;
[[nodiscard]] std::size_t capacity() const;
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(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, int value, std::size_t count);
template <typename T> char* save(T* object)
@ -135,6 +137,42 @@ namespace Utils
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)
{
return save(array, sizeof(T), count);