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:
parent
14b3610fee
commit
eccdf2e25e
@ -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
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
@ -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);
|
||||
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -242,6 +242,11 @@ namespace Components
|
||||
|
||||
void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum)
|
||||
{
|
||||
if (!DebugOverlay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (DebugOverlay->current.integer)
|
||||
{
|
||||
case 2:
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user