Big zonebuilder update (#427)

This commit is contained in:
Louve 2022-12-14 09:40:15 +01:00 committed by GitHub
parent f6b66fdc0d
commit f91fa0d0b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 2395 additions and 573 deletions

View File

@ -257,7 +257,7 @@ namespace Components
for (int k = 0; k < (pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); ++k)
{
if (pass->args[k].type == D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_CONSTINT)
if (pass->args[k].type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST)
{
if (pass->args[k].u.codeConst.index == -28132)
{

View File

@ -391,7 +391,7 @@ namespace Assets
switch (elemType)
{
case 7:
case Game::FX_ELEM_TYPE_MODEL:
{
if (visuals->model)
{
@ -401,11 +401,11 @@ namespace Assets
break;
}
case 8:
case 9:
case Game::FX_ELEM_TYPE_OMNI_LIGHT:
case Game::FX_ELEM_TYPE_SPOT_LIGHT:
break;
case 0xA:
case Game::FX_ELEM_TYPE_SOUND:
{
if (visuals->soundName)
{
@ -416,7 +416,7 @@ namespace Assets
break;
}
case 0xC:
case Game::FX_ELEM_TYPE_RUNNER:
{
if (visuals->effectDef.handle)
{
@ -491,7 +491,7 @@ namespace Assets
// Save_FxElemDefVisuals
{
if (elemDef->elemType == 11)
if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL)
{
if (elemDef->visuals.markArray)
{
@ -501,7 +501,7 @@ namespace Assets
Game::FxElemMarkVisuals* destMarkArray = buffer->dest<Game::FxElemMarkVisuals>();
buffer->saveArray(elemDef->visuals.markArray, elemDef->visualCount);
for (char j = 0; j < elemDef->visualCount; ++j)
for (auto j = 0; j < elemDef->visualCount; ++j)
{
if (elemDef->visuals.markArray[j].materials[0])
{
@ -563,7 +563,7 @@ namespace Assets
{
AssertSize(Game::FxElemExtendedDefPtr, 4);
if (elemDef->elemType == 3)
if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL)
{
// Save_FxTrailDef
{
@ -597,7 +597,7 @@ namespace Assets
}
}
}
else if (elemDef->elemType == 6)
else if (elemDef->elemType == Game::FX_ELEM_TYPE_SPARK_FOUNTAIN)
{
if (elemDef->extended.sparkFountainDef)
{

View File

@ -186,11 +186,20 @@ namespace Assets
}
}
void IFxWorld::load(Game::XAssetHeader* /*header*/, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld;
if (map) return;
Components::Logger::Error(Game::ERR_FATAL, "Missing fx_map {}... you can't make them yet you idiot.", name);
// Generate
map = builder->getAllocator()->allocate<Game::FxWorld>();
map->name = builder->getAllocator()->duplicateString(name);
// No glass for you!
ZeroMemory(&map->glassSys, sizeof(map->glassSys));
map->glassSys.firstFreePiece = 0xFFFF; // That's how rust has it (no glass)
header->fxWorld = map;
}
}

View File

@ -1,6 +1,8 @@
#include <STDInclude.hpp>
#include "IGameWorldMp.hpp"
#define IW4X_GAMEWORLD_VERSION 1
namespace Assets
{
void IGameWorldMp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
@ -75,4 +77,118 @@ namespace Assets
buffer->popBlock();
}
void IGameWorldMp::load(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 gameWorld(std::format("gameworld/{}.iw4x.json", name));
if (gameWorld.exists())
{
nlohmann::json gameWorldJson;
try
{
gameWorldJson = nlohmann::json::parse(gameWorld.getBuffer());
}
catch (const std::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for gameworld {}! {}", name, e.what());
return;
}
auto* asset = builder->getAllocator()->allocate<Game::GameWorldMp>();
if (!gameWorldJson.is_object())
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorldMp json for {}\n", name);
return;
}
auto version = gameWorldJson["version"].is_number() ? gameWorldJson["version"].get<int>() : 0;
if (version != IW4X_GAMEWORLD_VERSION)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorld json version for {}, expected {} and got {}\n", name, IW4X_GAMEWORLD_VERSION, version);
return;
}
if (!gameWorldJson["name"].is_string())
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Missing gameworld name! on {}\n", name);
return;
}
asset->name = builder->getAllocator()->duplicateString(gameWorldJson["name"].get<std::string>());
auto glassData = builder->getAllocator()->allocate<Game::G_GlassData>();
if (gameWorldJson["glassData"].is_object())
{
auto jsonGlassData = gameWorldJson["glassData"];
try
{
glassData->damageToDestroy = jsonGlassData["damageToDestroy"].get<unsigned short>();
glassData->damageToWeaken = jsonGlassData["damageToWeaken"].get<unsigned short>();
if (jsonGlassData["glassNames"].is_array())
{
nlohmann::json::array_t glassNames = jsonGlassData["glassNames"];
glassData->glassNameCount = glassNames.size();
glassData->glassNames = builder->getAllocator()->allocateArray<Game::G_GlassName>(glassData->glassNameCount);
for (size_t i = 0; i < glassData->glassNameCount; i++)
{
auto jsonGlassName = glassNames[i];
glassData->glassNames[i].nameStr = builder->getAllocator()->duplicateString(jsonGlassName["nameStr"]);
glassData->glassNames[i].name = jsonGlassName["name"].get<unsigned short>();
if (jsonGlassName["piecesIndices"].is_array())
{
nlohmann::json::array_t jsonPiecesIndices = jsonGlassName["piecesIndices"];
glassData->glassNames[i].pieceCount = static_cast<unsigned short>(jsonPiecesIndices.size());
for (size_t j = 0; j < glassData->glassNames[i].pieceCount; j++)
{
glassData->glassNames[i].pieceIndices[j] = jsonPiecesIndices[j].get<unsigned short>();
}
}
}
}
if (gameWorldJson["glassPieces"].is_array())
{
nlohmann::json::array_t glassPieces = gameWorldJson["glassPieces"];
glassData->pieceCount = glassPieces.size();
glassData->glassPieces = builder->getAllocator()->allocateArray<Game::G_GlassPiece>(glassData->pieceCount);
for (size_t i = 0; i < glassData->pieceCount; i++)
{
glassData->glassPieces[i].collapseTime = glassPieces[i]["collapseTime"].get<unsigned short>();
glassData->glassPieces[i].damageTaken = glassPieces[i]["damageTaken"].get<unsigned short>();
glassData->glassPieces[i].lastStateChangeTime = glassPieces[i]["lastStateChangeTime"].get<int>();
glassData->glassPieces[i].impactDir = glassPieces[i]["impactDir"].get<char>();
nlohmann::json::array_t jsonPos = glassPieces[i]["impactPos"];
glassData->glassPieces[i].impactPos[0] = jsonPos[0].get<char>();
glassData->glassPieces[i].impactPos[1] = jsonPos[1].get<char>();
}
}
}
catch (const nlohmann::json::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed GameWorldMp json for {} ({})\n", name, e.what());
return;
}
}
asset->g_glassData = glassData;
header->gameWorldMp = asset;
}
}
}

View File

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

View File

@ -187,7 +187,7 @@ namespace Assets
for (char j = 0; j < 5; ++j)
{
builder->mapScriptString(&(&node->constant.targetname)[j]);
builder->mapScriptString((&node->constant.targetname)[j]);
}
if (node->constant.Links)

View File

@ -18,7 +18,7 @@ namespace Assets
}
image->name = builder->getAllocator()->duplicateString(name);
image->semantic = 2;
image->semantic = Game::TextureSemantic::TS_COLOR_MAP;
const char* tempName = image->name;
if (tempName[0] == '*') tempName++;
@ -35,7 +35,7 @@ namespace Assets
}
image->mapType = reader.read<char>();
image->semantic = reader.read<char>();
image->semantic = reader.read<Game::TextureSemantic>();
image->category = reader.read<char>();
int dataLength = reader.read<int>();

View File

@ -3,6 +3,27 @@
#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)
@ -47,7 +68,11 @@ namespace Assets
if (model->model)
{
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, reader->readString().data(), builder).model;
auto name = reader->readString();
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, name.data(), builder).model;
assert(model->model);
}
}
}

View File

@ -120,17 +120,8 @@ namespace Assets
if (asset->sound.data)
{
if (builder->hasPointer(asset->sound.data))
{
dest->sound.data = builder->getPointer(asset->sound.data);
}
else
{
builder->storePointer(asset->sound.data);
buffer->saveArray(asset->sound.data, asset->sound.info.data_len);
Utils::Stream::ClearPointer(&dest->sound.data);
}
buffer->saveArray(asset->sound.data, asset->sound.info.data_len);
Utils::Stream::ClearPointer(&dest->sound.data);
}
buffer->popBlock();

View File

@ -1,15 +1,439 @@
#include <STDInclude.hpp>
#include "IMaterial.hpp"
#define IW4X_MAT_VERSION "1"
#define IW4X_MAT_BIN_VERSION "1"
#define IW4X_MAT_JSON_VERSION 1
namespace Assets
{
const std::unordered_map<std::string, std::string> techSetCorrespondance =
{
{"effect", "effect_blend"},
{"effect", "effect_blend"},
{"effect_nofog", "effect_blend_nofog"},
{"effect_zfeather", "effect_zfeather_blend"},
{"effect_zfeather_falloff", "effect_zfeather_falloff_add"},
{"effect_zfeather_nofog", "effect_zfeather_add_nofog"},
{"wc_unlit_add", "wc_unlit_add_lin"},
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin"},
{"wc_unlit", "wc_unlit_replace_lin"},
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
{"wc_unlit_blend", "wc_unlit_blend_lin"},
{"wc_unlit_replace", "wc_unlit_replace_lin"},
{"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" },
{"mc_unlit_replace", "mc_unlit_replace_lin"},
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
{"mc_unlit", "mc_unlit_replace_lin_nocast"},
{"mc_unlit_alphatest", "mc_unlit_blend_lin"},
{"mc_effect_nofog", "mc_effect_blend_nofog"},
{"mc_effect_falloff_add_nofog", "mc_effect_falloff_add_nofog_eyeoffset"},
};
void IMaterial::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
if (!header->data) this->loadJson(header, name, builder); // Check if we want to override materials
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
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
}
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File materialInfo(std::format("materials/{}.iw4x.json", name));
if (!materialInfo.exists()) return;
Game::Material* asset = builder->getAllocator()->allocate<Game::Material>();
nlohmann::json materialJson;
try
{
materialJson = nlohmann::json::parse(materialInfo.getBuffer());
}
catch (const std::exception& e)
{
Components::Logger::Print("Invalid material json for {} (broken json {})\n", name, e.what());
}
if (!materialJson.is_object())
{
Components::Logger::Print("Invalid material json for {} (Is it zonebuilder format?)\n", name);
return;
}
if (materialJson["version"].get<int>() != IW4X_MAT_JSON_VERSION)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json version for {}, expected {} and got {}\n", name, IW4X_MAT_JSON_VERSION, materialJson["version"].get<std::string>());
return;
}
try
{
asset->info.name = builder->getAllocator()->duplicateString(materialJson["name"].get<std::string>());
asset->info.gameFlags = static_cast<char>(Utils::Json::ReadFlags(materialJson["gameFlags"].get<std::string>(), sizeof(char)));
asset->info.sortKey = materialJson["sortKey"].get<char>();
// * We do techset later * //
asset->info.textureAtlasRowCount = materialJson["textureAtlasRowCount"].get<unsigned char>();
asset->info.textureAtlasColumnCount = materialJson["textureAtlasColumnCount"].get<unsigned char>();
asset->info.surfaceTypeBits = static_cast<unsigned int>(Utils::Json::ReadFlags(materialJson["surfaceTypeBits"].get<std::string>(), sizeof(int)));
asset->info.hashIndex = materialJson["hashIndex"].get<unsigned short>();
asset->cameraRegion = materialJson["cameraRegion"].get<char>();
}
catch (const nlohmann::json::exception& e)
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json for {} (broken json {})\n", name, e.what());
return;
}
if (materialJson["gfxDrawSurface"].is_object())
{
asset->info.drawSurf.fields.customIndex = materialJson["gfxDrawSurface"]["customIndex"].get<long long>();
asset->info.drawSurf.fields.hasGfxEntIndex = materialJson["gfxDrawSurface"]["hasGfxEntIndex"].get<long long>();
asset->info.drawSurf.fields.materialSortedIndex = materialJson["gfxDrawSurface"]["materialSortedIndex"].get<long long>();
asset->info.drawSurf.fields.objectId = materialJson["gfxDrawSurface"]["objectId"].get<long long>();
asset->info.drawSurf.fields.prepass = materialJson["gfxDrawSurface"]["prepass"].get<long long>();
asset->info.drawSurf.fields.primarySortKey = materialJson["gfxDrawSurface"]["primarySortKey"].get<long long>();
asset->info.drawSurf.fields.reflectionProbeIndex = materialJson["gfxDrawSurface"]["reflectionProbeIndex"].get<long long>();
asset->info.drawSurf.fields.sceneLightIndex = materialJson["gfxDrawSurface"]["sceneLightIndex"].get<long long>();
asset->info.drawSurf.fields.surfType = materialJson["gfxDrawSurface"]["surfType"].get<long long>();
asset->info.drawSurf.fields.unused = materialJson["gfxDrawSurface"]["unused"].get<long long>();
asset->info.drawSurf.fields.useHeroLighting = materialJson["gfxDrawSurface"]["useHeroLighting"].get<long long>();
}
asset->stateFlags = static_cast<char>(Utils::Json::ReadFlags(materialJson["stateFlags"].get<std::string>(), sizeof(char)));
if (materialJson["textureTable"].is_array())
{
nlohmann::json::array_t textureTable = materialJson["textureTable"];
asset->textureCount = static_cast<unsigned char>(textureTable.size());
asset->textureTable = builder->getAllocator()->allocateArray<Game::MaterialTextureDef>(asset->textureCount);
for (size_t i = 0; i < textureTable.size(); i++)
{
auto& textureJson = textureTable[i];
if (textureJson.is_object())
{
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
textureDef->semantic = textureJson["semantic"].get<Game::TextureSemantic>();
textureDef->samplerState = textureJson["samplerState"].get<char>();
textureDef->nameStart = textureJson["nameStart"].get<char>();
textureDef->nameEnd = textureJson["nameEnd"].get<char>();
textureDef->nameHash = textureJson["nameHash"].get<unsigned int>();
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
{
Game::water_t* water = builder->getAllocator()->allocate<Game::water_t>();
if (textureJson["water"].is_object())
{
auto& waterJson = textureJson["water"];
if (waterJson["image"].is_string())
{
auto imageName = waterJson["image"].get<std::string>();
water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, imageName.data(), builder).image;
}
water->amplitude = waterJson["amplitude"].get<float>();
water->M = waterJson["M"].get<int>();
water->N = waterJson["N"].get<int>();
water->Lx = waterJson["Lx"].get<float>();
water->Lz = waterJson["Lz"].get<float>();
water->gravity = waterJson["gravity"].get<float>();
water->windvel = waterJson["windvel"].get<float>();
auto winddir = waterJson["winddir"].get<std::vector<float>>();
if (winddir.size() == 2)
{
std::copy(winddir.begin(), winddir.end(), water->winddir);
}
auto codeConstant = waterJson["codeConstant"].get<std::vector<float>>();
if (codeConstant.size() == 4)
{
std::copy(codeConstant.begin(), codeConstant.end(), water->codeConstant);
}
/// H0
[[maybe_unused]] auto idealSize = water->M * water->N * sizeof(Game::complex_s);
auto h064 = waterJson["H0"].get<std::string>();
auto predictedSize = static_cast<unsigned long>(std::ceilf((h064.size() / 4.f) * 3.f));
assert(predictedSize >= idealSize);
auto h0 = reinterpret_cast<Game::complex_s*>(builder->getAllocator()->allocate(predictedSize));
[[maybe_unused]] auto h0Result = base64_decode(
h064.data(),
h064.size(),
reinterpret_cast<unsigned char*>(h0),
&predictedSize
);
assert(h0Result == CRYPT_OK);
water->H0 = h0;
/// WTerm
auto wTerm64 = waterJson["wTerm"].get<std::string>();
auto predictedWTermSize = static_cast<unsigned long>(std::ceilf((wTerm64.size() / 4.f) * 3.f));
auto wTerm = reinterpret_cast<float*>(builder->getAllocator()->allocate(predictedWTermSize));
[[maybe_unused]] auto wTermResult = base64_decode(
wTerm64.data(),
wTerm64.size(),
reinterpret_cast<unsigned char*>(wTerm),
&predictedWTermSize
);
assert(wTermResult == CRYPT_OK);
water->wTerm = wTerm;
}
textureDef->u.water = water;
}
else
{
textureDef->u.image = nullptr;
if (textureJson["image"].is_string())
{
textureDef->u.image = Components::AssetHandler::FindAssetForZone
(
Game::XAssetType::ASSET_TYPE_IMAGE,
textureJson["image"].get<std::string>(),
builder
).image;
}
}
}
}
}
// Statebits
if (materialJson["stateBitsEntry"].is_array())
{
nlohmann::json::array_t stateBitsEntry = materialJson["stateBitsEntry"];
for (size_t i = 0; i < std::min(stateBitsEntry.size(), 48u); i++)
{
asset->stateBitsEntry[i] = stateBitsEntry[i].get<char>();
}
}
if (materialJson["stateBitsTable"].is_array())
{
nlohmann::json::array_t array = materialJson["stateBitsTable"];
asset->stateBitsCount = static_cast<unsigned char>(array.size());
asset->stateBitsTable = builder->getAllocator()->allocateArray<Game::GfxStateBits>(array.size());
size_t statebitTableIndex = 0;
for (auto& jsonStateBitEntry : array)
{
auto stateBit = &asset->stateBitsTable[statebitTableIndex++];
unsigned int loadbits0 = 0;
unsigned int loadbits1 = 0;
#define READ_INT_LB_FROM_JSON(x) unsigned int x = jsonStateBitEntry[#x].get<unsigned int>()
#define READ_BOOL_LB_FROM_JSON(x) bool x = jsonStateBitEntry[#x].get<bool>()
READ_INT_LB_FROM_JSON(srcBlendRgb);
READ_INT_LB_FROM_JSON(dstBlendRgb);
READ_INT_LB_FROM_JSON(blendOpRgb);
READ_INT_LB_FROM_JSON(srcBlendAlpha);
READ_INT_LB_FROM_JSON(dstBlendAlpha);
READ_INT_LB_FROM_JSON(blendOpAlpha);
READ_INT_LB_FROM_JSON(depthTest);
READ_INT_LB_FROM_JSON(polygonOffset);
const auto alphaTest = jsonStateBitEntry["alphaTest"].get<std::string>();
const auto cullFace = jsonStateBitEntry["cullFace"].get<std::string>();
READ_BOOL_LB_FROM_JSON(colorWriteRgb);
READ_BOOL_LB_FROM_JSON(colorWriteAlpha);
READ_BOOL_LB_FROM_JSON(polymodeLine);
READ_BOOL_LB_FROM_JSON(gammaWrite);
READ_BOOL_LB_FROM_JSON(depthWrite);
READ_BOOL_LB_FROM_JSON(stencilFrontEnabled);
READ_BOOL_LB_FROM_JSON(stencilBackEnabled);
READ_INT_LB_FROM_JSON(stencilFrontPass);
READ_INT_LB_FROM_JSON(stencilFrontFail);
READ_INT_LB_FROM_JSON(stencilFrontZFail);
READ_INT_LB_FROM_JSON(stencilFrontFunc);
READ_INT_LB_FROM_JSON(stencilBackPass);
READ_INT_LB_FROM_JSON(stencilBackFail);
READ_INT_LB_FROM_JSON(stencilBackZFail);
READ_INT_LB_FROM_JSON(stencilBackFunc);
loadbits0 |= srcBlendRgb << Game::GFXS0_SRCBLEND_RGB_SHIFT;
loadbits0 |= dstBlendRgb << Game::GFXS0_DSTBLEND_RGB_SHIFT;
loadbits0 |= blendOpRgb << Game::GFXS0_BLENDOP_RGB_SHIFT;
loadbits0 |= srcBlendAlpha << Game::GFXS0_SRCBLEND_ALPHA_SHIFT;
loadbits0 |= dstBlendAlpha << Game::GFXS0_DSTBLEND_ALPHA_SHIFT;
loadbits0 |= blendOpAlpha << Game::GFXS0_BLENDOP_ALPHA_SHIFT;
if (depthTest == -1)
{
loadbits1 |= Game::GFXS1_DEPTHTEST_DISABLE;
}
else
{
loadbits1 |= depthTest << Game::GFXS1_DEPTHTEST_SHIFT;
}
loadbits1 |= polygonOffset << Game::GFXS1_POLYGON_OFFSET_SHIFT;
if (alphaTest == "disable")
{
loadbits0 |= Game::GFXS0_ATEST_DISABLE;
}
else if (alphaTest == ">0")
{
loadbits0 |= Game::GFXS0_ATEST_GT_0;
}
else if (alphaTest == "<128")
{
loadbits0 |= Game::GFXS0_ATEST_LT_128;
}
else if (alphaTest == ">=128")
{
loadbits0 |= Game::GFXS0_ATEST_GE_128;
}
else
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid alphatest loadbit0 '{}' in material {}\n", alphaTest, name);
return;
}
if (cullFace == "none")
{
loadbits0 |= Game::GFXS0_CULL_NONE;
}
else if (cullFace == "back")
{
loadbits0 |= Game::GFXS0_CULL_BACK;
}
else if (cullFace == "front")
{
loadbits0 |= Game::GFXS0_CULL_FRONT;
}
else
{
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid cullFace loadbit0 '{}' in material {}\n", cullFace, name);
return;
}
if (gammaWrite)
{
loadbits0 |= Game::GFXS0_GAMMAWRITE;
}
if (colorWriteAlpha)
{
loadbits0 |= Game::GFXS0_COLORWRITE_ALPHA;
}
if (colorWriteRgb)
{
loadbits0 |= Game::GFXS0_COLORWRITE_RGB;
}
if (polymodeLine)
{
loadbits0 |= Game::GFXS0_POLYMODE_LINE;
}
if (depthWrite)
{
loadbits1 |= Game::GFXS1_DEPTHWRITE;
}
if (stencilFrontEnabled)
{
loadbits1 |= Game::GFXS1_STENCIL_FRONT_ENABLE;
}
if (stencilBackEnabled)
{
loadbits1 |= Game::GFXS1_STENCIL_BACK_ENABLE;
}
loadbits1 |= stencilFrontPass << Game::GFXS1_STENCIL_FRONT_PASS_SHIFT;
loadbits1 |= stencilFrontFail << Game::GFXS1_STENCIL_FRONT_FAIL_SHIFT;
loadbits1 |= stencilFrontZFail << Game::GFXS1_STENCIL_FRONT_ZFAIL_SHIFT;
loadbits1 |= stencilFrontFunc << Game::GFXS1_STENCIL_FRONT_FUNC_SHIFT;
loadbits1 |= stencilBackPass << Game::GFXS1_STENCIL_BACK_PASS_SHIFT;
loadbits1 |= stencilBackFail << Game::GFXS1_STENCIL_BACK_FAIL_SHIFT;
loadbits1 |= stencilBackZFail << Game::GFXS1_STENCIL_BACK_ZFAIL_SHIFT;
loadbits1 |= stencilBackFunc << Game::GFXS1_STENCIL_BACK_FUNC_SHIFT;
stateBit->loadBits[0] = loadbits0;
stateBit->loadBits[1] = loadbits1;
}
// Constant table
if (materialJson["constantTable"].is_array())
{
nlohmann::json::array_t constants = materialJson["constantTable"];
asset->constantCount = static_cast<char>(constants.size());
auto table = builder->getAllocator()->allocateArray<Game::MaterialConstantDef>(asset->constantCount);
for (size_t constantIndex = 0; constantIndex < asset->constantCount; constantIndex++)
{
auto& constant = constants[constantIndex];
auto entry = &table[constantIndex];
auto litVec = constant["literal"].get<std::vector<float>>();
std::copy(litVec.begin(), litVec.end(), entry->literal);
auto constantName = constant["name"].get<std::string>();
std::copy(constantName.begin(), constantName.end(), entry->name);
entry->nameHash = constant["nameHash"].get<unsigned int>();
}
asset->constantTable = table;
}
if (materialJson["techniqueSet"].is_string())
{
const std::string techsetName = materialJson["techniqueSet"].get<std::string>();
asset->techniqueSet = findWorkingTechset(techsetName, asset, builder);
if (asset->techniqueSet == nullptr)
{
assert(false);
}
}
header->material = asset;
}
}
Game::MaterialTechniqueSet* IMaterial::findWorkingTechset(std::string techsetName, [[maybe_unused]] Game::Material* material, Components::ZoneBuilder::Zone* builder) const
{
Game::MaterialTechniqueSet* techset;
// Pass 1: Identical techset (1:1)
techset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName.data(), builder).techniqueSet;
if (techset != nullptr)
{
return techset;
}
// We do no more cause we use CoD4 techset and they should always be present
// If one day we want to go back to mw2 fallback we can add extra steps here!
return nullptr;
}
void IMaterial::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
@ -30,29 +454,6 @@ namespace Assets
"_add_lin_nofog",
};
static std::unordered_map<std::string, std::string> techSetCorrespondance =
{
{"effect", "effect_blend"},
{"effect", "effect_blend"},
{"effect_nofog", "effect_blend_nofog"},
{"effect_zfeather", "effect_zfeather_blend"},
{"wc_unlit_add", "wc_unlit_add_lin"},
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
{"wc_unlit", "wc_unlit_replace_lin"},
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
{"wc_unlit_blend", "wc_unlit_blend_lin_ua"},
{"wc_unlit_replace", "wc_unlit_replace_lin"},
{"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" },
{"mc_unlit_replace", "mc_unlit_replace_lin"},
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
{"mc_unlit", "mc_unlit_replace_lin_nocast"},
{"mc_unlit_alphatest", "mc_unlit_blend_lin"}
};
Components::FileSystem::File materialFile(std::format("materials/{}.iw4xMaterial", name));
if (!materialFile.exists()) return;
@ -66,9 +467,9 @@ namespace Assets
std::string version;
version.push_back(reader.read<char>());
if (version != IW4X_MAT_VERSION)
if (version != IW4X_MAT_BIN_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_VERSION, version);
Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_BIN_VERSION, version);
}
auto* asset = reader.readObject<Game::Material>();
@ -127,7 +528,7 @@ namespace Assets
{
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
if (textureDef->semantic == SEMANTIC_WATER_MAP)
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
{
if (textureDef->u.water)
{
@ -175,29 +576,29 @@ namespace Assets
// Find correct sortkey by comparing techsets
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry)
{
if (!replacementFound)
{
Game::XAssetHeader header = entry->asset.header;
const char* name = asset->techniqueSet->name;
if (name[0] == ',') ++name;
if (std::string(name) == header.material->techniqueSet->name)
if (!replacementFound)
{
asset->info.sortKey = header.material->info.sortKey;
Game::XAssetHeader header = entry->asset.header;
// This is temp, as nobody has time to fix materials
asset->stateBitsCount = header.material->stateBitsCount;
asset->stateBitsTable = header.material->stateBitsTable;
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
asset->constantCount = header.material->constantCount;
asset->constantTable = header.material->constantTable;
Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name);
replacementFound = true;
const char* name = asset->techniqueSet->name;
if (name[0] == ',') ++name;
if (std::string(name) == header.material->techniqueSet->name)
{
asset->info.sortKey = header.material->info.sortKey;
// This is temp, as nobody has time to fix materials
asset->stateBitsCount = header.material->stateBitsCount;
asset->stateBitsTable = header.material->stateBitsTable;
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, ARRAYSIZE(asset->stateBitsEntry));
asset->constantCount = header.material->constantCount;
asset->constantTable = header.material->constantTable;
Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name);
replacementFound = true;
}
}
}
}, false);
}, false);
if (!replacementFound)
{
@ -222,70 +623,72 @@ namespace Assets
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry)
{
if (!replacementFound)
{
Game::XAssetHeader header = entry->asset.header;
if (techsetMatches(header.material, asset))
if (!replacementFound)
{
Components::Logger::Print("Material {} with techset {} has been mapped to {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name);
asset->info.sortKey = header.material->info.sortKey;
replacementFound = true;
Game::XAssetHeader header = entry->asset.header;
if (techsetMatches(header.material, asset))
{
Components::Logger::Print("Material {} with techset {} has been mapped to {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name);
asset->info.sortKey = header.material->info.sortKey;
replacementFound = true;
}
}
}
}, false);
}, false);
}
if (!replacementFound && asset->techniqueSet)
{
Components::Logger::Print("No replacement found for material {} with techset {}\n", asset->info.name, asset->techniqueSet->name);
std::string techName = asset->techniqueSet->name;
if (techSetCorrespondance.contains(techName))
if (const auto itr = techSetCorrespondance.find(techName); itr != techSetCorrespondance.end())
{
auto iw4TechSetName = techSetCorrespondance[techName];
Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
auto& iw4TechSetName = itr->second;
auto* iw4TechSet = Game::DB_FindXAssetEntry(Game::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
if (iw4TechSet)
if (iw4TechSet)
{
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry)
{
if (!replacementFound)
{
Game::XAssetHeader header = entry->asset.header;
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet
&& std::string(header.material->info.name).find("icon") == std::string::npos) // Yeah this has a tendency to fuck up a LOT of transparent materials
if (!replacementFound)
{
Game::XAssetHeader header = entry->asset.header;
Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n",
asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
asset->info.sortKey = header.material->info.sortKey;
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
// Yeah this has a tendency to fuck up a LOT of transparent materials
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet && std::string(header.material->info.name).find("icon") != std::string::npos)
{
Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n",
asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
// this is terrible!
asset->stateBitsCount = header.material->stateBitsCount;
asset->stateBitsTable = header.material->stateBitsTable;
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
asset->constantCount = header.material->constantCount;
asset->constantTable = header.material->constantTable;
asset->info.sortKey = header.material->info.sortKey;
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
replacementFound = true;
// this is terrible!
asset->stateBitsCount = header.material->stateBitsCount;
asset->stateBitsTable = header.material->stateBitsTable;
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
asset->constantCount = header.material->constantCount;
asset->constantTable = header.material->constantTable;
replacementFound = true;
}
}
}
}, false);
}, false);
if (!replacementFound)
if (!replacementFound)
{
Components::Logger::Print("Could not find any loaded material with techset {} (in replacement of {}), so I cannot set the sortkey for material {}\n", iw4TechSetName, asset->techniqueSet->name, asset->info.name);
}
}
else
else
{
Components::Logger::Print("Could not find any loaded techset with iw4 name {} for iw3 techset {}\n", iw4TechSetName, asset->techniqueSet->name);
}
}
else
else
{
Components::Logger::Print("Could not match iw3 techset {} with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName);
}
@ -327,15 +730,6 @@ namespace Assets
header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material;
}
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File materialInfo(std::format("materials/{}.json", name));
if (!materialInfo.exists()) return;
header->material = nullptr;
}
void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
Game::Material* asset = header.material;
@ -351,7 +745,7 @@ namespace Assets
{
if (asset->textureTable[i].u.image)
{
if (asset->textureTable[i].semantic == SEMANTIC_WATER_MAP)
if (asset->textureTable[i].semantic == Game::TextureSemantic::TS_WATER_MAP)
{
if (asset->textureTable[i].u.water->image)
{
@ -411,7 +805,7 @@ namespace Assets
auto* destTextureDef = &destTextureTable[i];
auto* textureDef = &asset->textureTable[i];
if (textureDef->semantic == SEMANTIC_WATER_MAP)
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
{
AssertSize(Game::water_t, 68);

View File

@ -13,5 +13,8 @@ namespace Assets
void loadJson(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);
private:
Game::MaterialTechniqueSet* findWorkingTechset(const std::string techsetName, Game::Material* material, Components::ZoneBuilder::Zone* builder) const;
};
}

View File

@ -1,15 +1,15 @@
#include <STDInclude.hpp>
#include "IMaterialPixelShader.hpp"
#define IW4X_TECHSET_VERSION "0"
#define GFX_RENDERER_SHADER_SM3 0
namespace Assets
{
void IMaterialPixelShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
if (!header->data) this->loadBinary(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
}
void IMaterialPixelShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
@ -19,36 +19,19 @@ namespace Assets
void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File psFile(std::format("ps/{}.iw4xPS", name));
Components::FileSystem::File psFile(std::format("ps/{}.cso", name));
if (!psFile.exists()) return;
Utils::Stream::Reader reader(builder->getAllocator(), psFile.getBuffer());
auto buff = psFile.getBuffer();
auto programSize = buff.size() / 4;
Game::MaterialPixelShader* asset = builder->getAllocator()->allocate<Game::MaterialPixelShader>();
char* magic = reader.readArray<char>(8);
if (std::memcmp(magic, "IW4xPIXL", 8))
{
Components::Logger::Error(Game::ERR_FATAL, "Reading pixel shader '{}' failed, header is invalid!", name);
}
asset->name = builder->getAllocator()->duplicateString(name);
asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3;
asset->prog.loadDef.programSize = static_cast<unsigned short>(programSize);
asset->prog.loadDef.program = builder->getAllocator()->allocateArray<unsigned int>(programSize);
memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size());
std::string version;
version.push_back(reader.read<char>());
if (version != IW4X_TECHSET_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL,
"Reading pixel shader '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version);
}
Game::MaterialPixelShader* asset = reader.readObject<Game::MaterialPixelShader>();
if (asset->name)
{
asset->name = reader.readCString();
}
if (asset->prog.loadDef.program)
{
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
}
header->pixelShader = asset;
}

View File

@ -1,14 +1,14 @@
#include <STDInclude.hpp>
#include "IMaterialTechniqueSet.hpp"
#define IW4X_TECHSET_VERSION "0"
#define IW4X_TECHSET_VERSION 1
namespace Assets
{
void IMaterialTechniqueSet::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
}
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
@ -16,132 +16,202 @@ namespace Assets
header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet;
}
void IMaterialTechniqueSet::loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
void IMaterialTechniqueSet::loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
AssertSize(Game::MaterialPass, 20);
Components::FileSystem::File techFile(std::format("techniques/{}.iw4xTech", name));
if (!techFile.exists()) {
Components::FileSystem::File techFile(std::format("techniques/{}.iw4x.json", name));
if (!techFile.exists())
{
*tech = nullptr;
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
return;
}
Utils::Stream::Reader reader(builder->getAllocator(), techFile.getBuffer());
nlohmann::json technique;
char* magic = reader.readArray<char>(8);
if (std::memcmp(magic, "IW4xTECH", 8))
try
{
Components::Logger::Error(Game::ERR_FATAL, "Reading technique '{}' failed, header is invalid!", name);
technique = nlohmann::json::parse(techFile.getBuffer());
}
catch (std::exception& e)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
}
std::string version;
version.push_back(reader.read<char>());
int version = technique["version"].get<int>();
if (version != IW4X_TECHSET_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL,
"Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version.data());
"Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version);
}
unsigned short flags = reader.read<unsigned short>();
unsigned short passCount = reader.read<unsigned short>();
unsigned short flags = static_cast<unsigned short>(Utils::Json::ReadFlags(technique["flags"].get<std::string>(), sizeof(short)));
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passCount - 1)));
if (technique["passArray"].is_array())
{
nlohmann::json::array_t passArray = technique["passArray"];
asset->name = builder->getAllocator()->duplicateString(name);
asset->flags = flags;
asset->passCount = passCount;
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passArray.size() - 1)));
Game::MaterialPass* passes = reader.readArray<Game::MaterialPass>(passCount);
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * passCount);
asset->name = builder->getAllocator()->duplicateString(name);
asset->flags = flags;
asset->passCount = static_cast<unsigned short>(passArray.size());
for (unsigned short i = 0; i < asset->passCount; i++)
{
Game::MaterialPass* pass = &asset->passArray[i];
Game::MaterialPass* passes = builder->getAllocator()->allocateArray<Game::MaterialPass>(asset->passCount);
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * asset->passCount);
if (pass->vertexDecl)
for (unsigned short i = 0; i < asset->passCount; i++)
{
const char* declName = reader.readCString();
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
}
Game::MaterialPass* pass = &asset->passArray[i];
auto jsonPass = passArray[i];
if (pass->vertexShader)
{
const char* vsName = reader.readCString();
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
}
if (pass->pixelShader)
{
const char* psName = reader.readCString();
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
}
pass->args = reader.readArray<Game::MaterialShaderArgument>(pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount);
for (int j = 0; j < pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount; j++)
{
if (pass->args[j].type == 1 || pass->args[j].type == 7)
if (jsonPass["vertexDeclaration"].is_string())
{
pass->args[j].u.literalConst = reader.readArray<float>(4);
auto declName = jsonPass["vertexDeclaration"].get<std::string>();
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
}
if (pass->args[j].type == 3 || pass->args[j].type == 5)
if (jsonPass["vertexShader"].is_string())
{
pass->args[j].u.codeConst.index = *reader.readObject<unsigned short>();
pass->args[j].u.codeConst.firstRow = *reader.readObject<unsigned char>();
pass->args[j].u.codeConst.rowCount = *reader.readObject<unsigned char>();
auto vsName = jsonPass["vertexShader"].get<std::string>();
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
}
if (jsonPass["pixelShader"].is_string())
{
auto psName = jsonPass["pixelShader"].get<std::string>();
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
}
pass->perPrimArgCount = jsonPass["perPrimArgCount"].get<char>();
pass->perObjArgCount = jsonPass["perObjArgCount"].get<char>();
pass->stableArgCount = jsonPass["stableArgCount"].get<char>();
pass->customSamplerFlags = jsonPass["customSamplerFlags"].get<char>();
if (jsonPass["arguments"].is_array())
{
nlohmann::json::array_t jsonAguments = jsonPass["arguments"];
pass->args = builder->getAllocator()->allocateArray<Game::MaterialShaderArgument>(jsonAguments.size());
for (size_t j = 0; j < jsonAguments.size(); j++)
{
auto jsonArgument = jsonAguments[j];
Game::MaterialShaderArgument* argument = &pass->args[j];
argument->type = jsonArgument["type"].get<Game::MaterialShaderArgumentType>();
argument->dest = jsonArgument["dest"].get<unsigned short>();
if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_VERTEX_CONST ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST)
{
argument->u.literalConst = builder->getAllocator()->allocateArray<float>(4);
auto literals = jsonArgument["literals"].get<std::vector<float>>();
std::copy(literals.begin(), literals.end(), argument->u.literalConst);
}
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST)
{
if (jsonArgument["codeConst"].is_object())
{
auto codeConst = jsonArgument["codeConst"];
argument->u.codeConst.index = codeConst["index"].get<unsigned short>();
argument->u.codeConst.firstRow = codeConst["firstRow"].get<unsigned char>();
argument->u.codeConst.rowCount = codeConst["rowCount"].get<unsigned char>();
}
}
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_SAMPLER ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_VERTEX_CONST ||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_CONST)
{
argument->u.nameHash = jsonArgument["nameHash"].get<unsigned int>();
}
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_SAMPLER)
{
argument->u.codeSampler = jsonArgument["codeSampler"].get<unsigned int>();
}
}
}
}
*tech = asset;
}
*tech = asset;
}
void IMaterialTechniqueSet::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
void IMaterialTechniqueSet::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File tsFile(std::format("techsets/{}.iw4xTS", name));
Components::FileSystem::File tsFile(std::format("techsets/{}.iw4x.json", name));
if (!tsFile.exists()) return;
Utils::Stream::Reader reader(builder->getAllocator(), tsFile.getBuffer());
nlohmann::json techset;
char* magic = reader.readArray<char>(8);
if (std::memcmp(magic, "IW4xTSET", 8))
try
{
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, header is invalid!", name);
techset = nlohmann::json::parse(tsFile.getBuffer());
}
catch (std::exception& e)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
}
std::string version;
version.push_back(reader.read<char>());
auto version = techset["version"].get<int>();
if (version != IW4X_TECHSET_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, expected version is {}, but it was {}!",
name, IW4X_TECHSET_VERSION, version);
}
Game::MaterialTechniqueSet* asset = reader.readObject<Game::MaterialTechniqueSet>();
Game::MaterialTechniqueSet* asset = builder->getAllocator()->allocate<Game::MaterialTechniqueSet>();
if (asset->name)
if (asset == nullptr)
{
asset->name = reader.readCString();
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, allocation failed!", name);
return;
}
for (int i = 0; i < 48; i++)
if (techset["name"].is_string())
{
if (asset->techniques[i])
asset->name = builder->getAllocator()->duplicateString(techset["name"].get<std::string>());
}
asset->hasBeenUploaded = techset["hasBeenUploaded"].get<bool>();
asset->worldVertFormat = techset["worldVertFormat"].get<char>();
if (techset["remappedTechniqueSet"].is_string())
{
auto remapped = techset["remappedTechniqueSet"].get<std::string>();
if (remapped != asset->name)
{
const char* techName = reader.readCString();
this->loadBinaryTechnique(&asset->techniques[i], techName, builder);
builder->loadAssetByName(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, remapped, false);
}
}
if (techset["techniques"].is_object())
{
for (int i = 0; i < Game::TECHNIQUE_COUNT; i++)
{
auto technique = techset["techniques"].at(std::to_string(i));
if (technique.is_string())
{
this->loadTechniqueFromDisk(&asset->techniques[i], technique.get<std::string>(), builder);
}
}
}
header->techniqueSet = asset;
}
void IMaterialTechniqueSet::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{
{
Game::MaterialTechniqueSet* asset = header.techniqueSet;
for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i)
@ -177,8 +247,10 @@ namespace Assets
AssertSize(Game::MaterialTechniqueSet, 204);
Utils::Stream* buffer = builder->getBuffer();
Game::MaterialTechniqueSet* asset = header.techniqueSet;
Game::MaterialTechniqueSet* dest = buffer->dest<Game::MaterialTechniqueSet>();
buffer->save(asset);
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);

View File

@ -12,8 +12,8 @@ namespace Assets
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
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 loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
void loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder);
void loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder);
};
}

View File

@ -1,14 +1,14 @@
#include <STDInclude.hpp>
#include "IMaterialVertexDeclaration.hpp"
#define IW4X_TECHSET_VERSION "0"
#define IW4X_TECHSET_VERSION 1
namespace Assets
{
void IMaterialVertexDeclaration::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
if (!header->data) this->loadBinary(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
}
void IMaterialVertexDeclaration::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
@ -29,12 +29,11 @@ namespace Assets
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, header is invalid!", name);
}
std::string version;
version.push_back(reader.read<char>());
auto version = reader.read<char>();
if (version != IW4X_TECHSET_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {}!",
name, IW4X_TECHSET_VERSION, version.data());
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {:d}!",
name, IW4X_TECHSET_VERSION, version);
}
Game::MaterialVertexDeclaration* asset = reader.readObject<Game::MaterialVertexDeclaration>();

View File

@ -1,14 +1,14 @@
#include <STDInclude.hpp>
#include "IMaterialVertexShader.hpp"
#define IW4X_TECHSET_VERSION "0"
#define GFX_RENDERER_SHADER_SM3 0
namespace Assets
{
void IMaterialVertexShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
if (!header->data) this->loadBinary(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
}
void IMaterialVertexShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
@ -18,36 +18,18 @@ namespace Assets
void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{
Components::FileSystem::File vsFile(std::format("vs/{}.iw4xVS", name));
Components::FileSystem::File vsFile(std::format("vs/{}.cso", name));
if (!vsFile.exists()) return;
Utils::Stream::Reader reader(builder->getAllocator(), vsFile.getBuffer());
auto buff = vsFile.getBuffer();
auto programSize = buff.size() / 4;
Game::MaterialVertexShader* asset = builder->getAllocator()->allocate<Game::MaterialVertexShader>();
char* magic = reader.readArray<char>(8);
if (std::memcmp(magic, "IW4xVERT", 8))
{
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, header is invalid!", name);
}
std::string version;
version.push_back(reader.read<char>());
if (version != IW4X_TECHSET_VERSION)
{
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, expected version is {}, but it was {}!",
name, IW4X_TECHSET_VERSION, version);
}
Game::MaterialVertexShader* asset = reader.readObject<Game::MaterialVertexShader>();
if (asset->name)
{
asset->name = reader.readCString();
}
if (asset->prog.loadDef.program)
{
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
}
asset->name = builder->getAllocator()->duplicateString(name);
asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3;
asset->prog.loadDef.programSize = static_cast<unsigned short>(programSize);
asset->prog.loadDef.program = builder->getAllocator()->allocateArray<unsigned int>(programSize);
memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size());
header->vertexShader = asset;
}

View File

@ -280,7 +280,7 @@ namespace Assets
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
buffer->saveArray(def->notetrackSoundMapKeys, 16);
for (int i = 0; i < 16; i++) {
builder->mapScriptString(&scriptStringTable[i]);
builder->mapScriptString(scriptStringTable[i]);
}
Utils::Stream::ClearPointer(&dest->notetrackSoundMapKeys);
@ -292,7 +292,7 @@ namespace Assets
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
buffer->saveArray(def->notetrackSoundMapValues, 16);
for (int i = 0; i < 16; i++) {
builder->mapScriptString(&scriptStringTable[i]);
builder->mapScriptString(scriptStringTable[i]);
}
Utils::Stream::ClearPointer(&dest->notetrackSoundMapValues);
@ -304,7 +304,7 @@ namespace Assets
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
buffer->saveArray(def->notetrackRumbleMapKeys, 16);
for (int i = 0; i < 16; i++) {
builder->mapScriptString(&scriptStringTable[i]);
builder->mapScriptString(scriptStringTable[i]);
}
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapKeys);
@ -316,7 +316,7 @@ namespace Assets
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
buffer->saveArray(def->notetrackRumbleMapValues, 16);
for (int i = 0; i < 16; i++) {
builder->mapScriptString(&scriptStringTable[i]);
builder->mapScriptString(scriptStringTable[i]);
}
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapValues);
@ -725,7 +725,7 @@ namespace Assets
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
buffer->saveArray(asset->hideTags, 32);
for (int i = 0; i < 32; i++) {
builder->mapScriptString(&scriptStringTable[i]);
builder->mapScriptString(scriptStringTable[i]);
}
Utils::Stream::ClearPointer(&dest->hideTags);

View File

@ -264,7 +264,7 @@ namespace Assets
for (char i = 0; i < asset->boneCount[Game::PART_TYPE_ALL]; ++i)
{
builder->mapScriptString(&destTagnames[i]);
builder->mapScriptString(destTagnames[i]);
}
Utils::Stream::ClearPointer(&dest->names);
@ -280,7 +280,7 @@ namespace Assets
for (char i = 0; i < asset->notifyCount; ++i)
{
builder->mapScriptString(&destNotetracks[i].name);
builder->mapScriptString(destNotetracks[i].name);
}
Utils::Stream::ClearPointer(&dest->notify);

View File

@ -1,7 +1,7 @@
#include <STDInclude.hpp>
#include "IXModel.hpp"
#define IW4X_MODEL_VERSION 5
#define IW4X_MODEL_VERSION 8
namespace Assets
{
@ -9,32 +9,32 @@ namespace Assets
{
if (entry->nodes)
{
entry->nodes = reader->readArray<Game::XSurfaceCollisionNode>(entry->nodeCount);
entry->nodes = reader->readArrayOnce<Game::XSurfaceCollisionNode>(entry->nodeCount);
}
if (entry->leafs)
{
entry->leafs = reader->readArray<Game::XSurfaceCollisionLeaf>(entry->leafCount);
entry->leafs = reader->readArrayOnce<Game::XSurfaceCollisionLeaf>(entry->leafCount);
}
}
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
{
if (surf->vertInfo.vertsBlend)
{
surf->vertInfo.vertsBlend = reader->readArray<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
surf->vertInfo.vertsBlend = reader->readArrayOnce<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
}
// Access vertex block
if (surf->verts0)
{
surf->verts0 = reader->readArray<Game::GfxPackedVertex>(surf->vertCount);
surf->verts0 = reader->readArrayOnce<Game::GfxPackedVertex>(surf->vertCount);
}
// Save_XRigidVertListArray
if (surf->vertList)
{
surf->vertList = reader->readArray<Game::XRigidVertList>(surf->vertListCount);
surf->vertList = reader->readArrayOnce<Game::XRigidVertList>(surf->vertListCount);
for (unsigned int i = 0; i < surf->vertListCount; ++i)
{
@ -51,17 +51,7 @@ namespace Assets
// Access index block
if (surf->triIndices)
{
void* oldPtr = surf->triIndices;
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
if (builder->getAllocator()->isPointerMapped(oldPtr))
{
surf->triIndices = builder->getAllocator()->getPointer<unsigned short>(oldPtr);
}
else
{
builder->getAllocator()->mapPointer(oldPtr, surf->triIndices);
}
surf->triIndices = reader->readArrayOnce<unsigned short>(surf->triCount * 3);
}
}
@ -74,7 +64,7 @@ namespace Assets
if (asset->surfs)
{
asset->surfs = reader->readArray<Game::XSurface>(asset->numsurfs);
asset->surfs = reader->readArrayOnce<Game::XSurface>(asset->numsurfs);
for (int i = 0; i < asset->numsurfs; ++i)
{
@ -110,11 +100,6 @@ namespace Assets
Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, expected version is {}, but it was {}!", name, IW4X_MODEL_VERSION, version);
}
if (version == 4)
{
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Model '{}' is in legacy format, please update it!\n", name);
}
Game::XModel* asset = reader.readObject<Game::XModel>();
if (asset->name)
@ -134,27 +119,27 @@ namespace Assets
if (asset->parentList)
{
asset->parentList = reader.readArray<char>(asset->numBones - asset->numRootBones);
asset->parentList = reader.readArrayOnce<unsigned char>(asset->numBones - asset->numRootBones);
}
if (asset->quats)
{
asset->quats = reader.readArray<short>((asset->numBones - asset->numRootBones) * 4);
asset->quats = reader.readArrayOnce<short>((asset->numBones - asset->numRootBones) * 4);
}
if (asset->trans)
{
asset->trans = reader.readArray<float>((asset->numBones - asset->numRootBones) * 3);
asset->trans = reader.readArrayOnce<float>((asset->numBones - asset->numRootBones) * 3);
}
if (asset->partClassification)
{
asset->partClassification = reader.readArray<char>(asset->numBones);
asset->partClassification = reader.readArrayOnce<unsigned char>(asset->numBones);
}
if (asset->baseMat)
{
asset->baseMat = reader.readArray<Game::DObjAnimMat>(asset->numBones);
asset->baseMat = reader.readArrayOnce<Game::DObjAnimMat>(asset->numBones);
}
if (asset->materialHandles)
@ -172,7 +157,7 @@ namespace Assets
// Save_XModelLodInfoArray
{
for (int i = 0; i < 4; ++i)
for (unsigned int i = 0; i < 4; ++i)
{
if (asset->lodInfo[i].modelSurfs)
{
@ -247,60 +232,53 @@ namespace Assets
if (asset->physCollmap)
{
if (version == 4)
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
asset->physCollmap = collmap;
if (collmap->name)
{
asset->physCollmap = nullptr;
collmap->name = reader.readCString();
}
else
if (collmap->geoms)
{
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
asset->physCollmap = collmap;
collmap->geoms = reader.readArray<Game::PhysGeomInfo>(collmap->count);
if (collmap->name)
for (unsigned int i = 0; i < collmap->count; ++i)
{
collmap->name = reader.readCString();
}
Game::PhysGeomInfo* geom = &collmap->geoms[i];
if (collmap->geoms)
{
collmap->geoms = reader.readArray<Game::PhysGeomInfo>(collmap->count);
for (unsigned int i = 0; i < collmap->count; ++i)
if (geom->brushWrapper)
{
Game::PhysGeomInfo* geom = &collmap->geoms[i];
if (geom->brushWrapper)
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>();
geom->brushWrapper = brush;
{
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>();
geom->brushWrapper = brush;
if (brush->brush.sides)
{
if (brush->brush.sides)
brush->brush.sides = reader.readArray<Game::cbrushside_t>(brush->brush.numsides);
for (unsigned short j = 0; j < brush->brush.numsides; ++j)
{
brush->brush.sides = reader.readArray<Game::cbrushside_t>(brush->brush.numsides);
for (unsigned short j = 0; j < brush->brush.numsides; ++j)
{
Game::cbrushside_t* side = &brush->brush.sides[j];
Game::cbrushside_t* side = &brush->brush.sides[j];
// TODO: Add pointer support
if (side->plane)
{
side->plane = reader.readObject<Game::cplane_s>();
}
// TODO: Add pointer support
if (side->plane)
{
side->plane = reader.readObject<Game::cplane_s>();
}
}
if (brush->brush.baseAdjacentSide)
{
brush->brush.baseAdjacentSide = reader.readArray<char>(brush->totalEdgeCount);
}
}
// TODO: Add pointer support
if (brush->planes)
if (brush->brush.baseAdjacentSide)
{
brush->planes = reader.readArray<Game::cplane_s>(brush->brush.numsides);
brush->brush.baseAdjacentSide = reader.readArray<char>(brush->totalEdgeCount);
}
}
// TODO: Add pointer support
if (brush->planes)
{
brush->planes = reader.readArray<Game::cplane_s>(brush->brush.numsides);
}
}
}
@ -386,7 +364,7 @@ namespace Assets
for (char i = 0; i < asset->numBones; ++i)
{
builder->mapScriptString(&destBoneNames[i]);
builder->mapScriptString(destBoneNames[i]);
}
Utils::Stream::ClearPointer(&dest->boneNames);
@ -394,37 +372,77 @@ namespace Assets
if (asset->parentList)
{
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
Utils::Stream::ClearPointer(&dest->parentList);
if (builder->hasPointer(asset->parentList))
{
dest->parentList = builder->getPointer(asset->parentList);
}
else
{
builder->storePointer(asset->parentList);
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
Utils::Stream::ClearPointer(&dest->parentList);
}
}
if (asset->quats)
{
buffer->align(Utils::Stream::ALIGN_2);
buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4);
Utils::Stream::ClearPointer(&dest->quats);
if (builder->hasPointer(asset->quats))
{
dest->quats = builder->getPointer(asset->quats);
}
else
{
buffer->align(Utils::Stream::ALIGN_2);
builder->storePointer(asset->quats);
buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4);
Utils::Stream::ClearPointer(&dest->quats);
}
}
if (asset->trans)
{
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3);
Utils::Stream::ClearPointer(&dest->trans);
if (builder->hasPointer(asset->trans))
{
dest->trans = builder->getPointer(asset->trans);
}
else
{
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(asset->trans);
buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3);
Utils::Stream::ClearPointer(&dest->trans);
}
}
if (asset->partClassification)
{
buffer->save(asset->partClassification, asset->numBones);
Utils::Stream::ClearPointer(&dest->partClassification);
if (builder->hasPointer(asset->partClassification))
{
dest->partClassification = builder->getPointer(asset->partClassification);
}
else
{
builder->storePointer(asset->partClassification);
buffer->save(asset->partClassification, asset->numBones);
Utils::Stream::ClearPointer(&dest->partClassification);
}
}
if (asset->baseMat)
{
AssertSize(Game::DObjAnimMat, 32);
if (builder->hasPointer(asset->baseMat))
{
dest->baseMat = builder->getPointer(asset->baseMat);
}
else
{
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(asset->baseMat);
buffer->saveArray(asset->baseMat, asset->numBones);
Utils::Stream::ClearPointer(&dest->baseMat);
}
buffer->align(Utils::Stream::ALIGN_4);
buffer->saveArray(asset->baseMat, asset->numBones);
Utils::Stream::ClearPointer(&dest->baseMat);
}
if (asset->materialHandles)

View File

@ -37,9 +37,18 @@ namespace Assets
if (surf->vertInfo.vertsBlend)
{
buffer->align(Utils::Stream::ALIGN_2);
buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend);
if (builder->hasPointer(surf->vertInfo.vertsBlend))
{
destSurf->vertInfo.vertsBlend = builder->getPointer(surf->vertInfo.vertsBlend);
}
else
{
buffer->align(Utils::Stream::ALIGN_2);
builder->storePointer(surf->vertInfo.vertsBlend);
buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend);
}
}
// Access vertex block
@ -48,9 +57,17 @@ namespace Assets
{
AssertSize(Game::GfxPackedVertex, 32);
buffer->align(Utils::Stream::ALIGN_16);
buffer->saveArray(surf->verts0, surf->vertCount);
Utils::Stream::ClearPointer(&destSurf->verts0);
if (builder->hasPointer(surf->verts0))
{
destSurf->verts0 = builder->getPointer(surf->verts0);
}
else
{
buffer->align(Utils::Stream::ALIGN_16);
builder->storePointer(surf->verts0);
buffer->saveArray(surf->verts0, surf->vertCount);
Utils::Stream::ClearPointer(&destSurf->verts0);
}
}
buffer->popBlock();
@ -59,25 +76,40 @@ namespace Assets
{
AssertSize(Game::XRigidVertList, 12);
buffer->align(Utils::Stream::ALIGN_4);
Game::XRigidVertList* destCt = buffer->dest<Game::XRigidVertList>();
buffer->saveArray(surf->vertList, surf->vertListCount);
for (unsigned int i = 0; i < surf->vertListCount; ++i)
if (builder->hasPointer(surf->vertList))
{
Game::XRigidVertList* destRigidVertList = &destCt[i];
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
if (rigidVertList->collisionTree)
{
buffer->align(Utils::Stream::ALIGN_4);
this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder);
Utils::Stream::ClearPointer(&destRigidVertList->collisionTree);
}
destSurf->vertList = builder->getPointer(surf->vertList);
}
else
{
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(surf->vertList);
Utils::Stream::ClearPointer(&destSurf->vertList);
Game::XRigidVertList* destCt = buffer->dest<Game::XRigidVertList>();
buffer->saveArray(surf->vertList, surf->vertListCount);
for (unsigned int i = 0; i < surf->vertListCount; ++i)
{
Game::XRigidVertList* destRigidVertList = &destCt[i];
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
if (rigidVertList->collisionTree)
{
if (builder->hasPointer(rigidVertList->collisionTree))
{
destRigidVertList->collisionTree = builder->getPointer(rigidVertList->collisionTree);
}
else {
buffer->align(Utils::Stream::ALIGN_4);
builder->storePointer(rigidVertList->collisionTree);
this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder);
Utils::Stream::ClearPointer(&destRigidVertList->collisionTree);
}
}
}
Utils::Stream::ClearPointer(&destSurf->vertList);
}
}
// Access index block
@ -89,6 +121,7 @@ namespace Assets
else
{
buffer->align(Utils::Stream::ALIGN_16);
builder->storePointer(surf->triIndices);
buffer->saveArray(surf->triIndices, surf->triCount * 3);
Utils::Stream::ClearPointer(&destSurf->triIndices);
}

View File

@ -564,7 +564,7 @@ namespace Components
bool Maps::IsCustomMap()
{
Game::GfxWorld*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
if(gameWorld) return gameWorld->checksum == 0xDEADBEEF;
if (gameWorld) return (gameWorld->checksum & 0xFFFF0000) == 0xC0D40000;
return false;
}
@ -619,7 +619,7 @@ namespace Components
for (unsigned int i = 0; i < gameWorld->dpvs.smodelCount; ++i)
{
if (gameWorld->dpvs.smodelDrawInsts[i].model->name == model)
if (model == "all"s || gameWorld->dpvs.smodelDrawInsts[i].model->name == model)
{
gameWorld->dpvs.smodelVisData[0][i] = 0;
gameWorld->dpvs.smodelVisData[1][i] = 0;
@ -641,86 +641,6 @@ namespace Components
}
}
Game::dvar_t* Maps::GetDistortionDvar()
{
Game::dvar_t*& r_distortion = *reinterpret_cast<Game::dvar_t**>(0x69F0DCC);
if(Maps::IsCustomMap())
{
static Game::dvar_t noDistortion;
ZeroMemory(&noDistortion, sizeof noDistortion);
return &noDistortion;
}
return r_distortion;
}
__declspec(naked) void Maps::SetDistortionStub()
{
__asm
{
push eax
pushad
call Maps::GetDistortionDvar
mov [esp + 20h], eax
popad
pop eax
retn
}
}
Game::dvar_t* Maps::GetSpecularDvar()
{
Game::dvar_t*& r_specular = *reinterpret_cast<Game::dvar_t**>(0x69F0D94);
static Game::dvar_t* r_specularCustomMaps = Game::Dvar_RegisterBool("r_specularCustomMaps", false, Game::DVAR_ARCHIVE, "Allows shaders to use phong specular lighting on custom maps");
if (Maps::IsCustomMap())
{
if (!r_specularCustomMaps->current.enabled)
{
static Game::dvar_t noSpecular;
ZeroMemory(&noSpecular, sizeof noSpecular);
return &noSpecular;
}
}
return r_specular;
}
__declspec(naked) void Maps::SetSpecularStub1()
{
__asm
{
push eax
pushad
call Maps::GetSpecularDvar
mov [esp + 20h], eax
popad
pop eax
retn
}
}
__declspec(naked) void Maps::SetSpecularStub2()
{
__asm
{
push eax
pushad
call Maps::GetSpecularDvar
mov [esp + 20h], eax
popad
pop edx
retn
}
}
void Maps::G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2)
{
if (Maps::CurrentMainZone == "mp_backlot_sh"s || Maps::CurrentMainZone == "mp_con_spring"s ||
@ -842,16 +762,6 @@ namespace Components
// Allow loading raw suns
Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick();
// Disable distortion on custom maps
//Utils::Hook(0x50AA47, Maps::SetDistortionStub, HOOK_CALL).install()->quick();
// Disable speculars on custom maps
Utils::Hook(0x525EA6, Maps::SetSpecularStub1, HOOK_CALL).install()->quick();
Utils::Hook(0x51FBC7, Maps::SetSpecularStub2, HOOK_CALL).install()->quick();
Utils::Hook(0x522A2E, Maps::SetSpecularStub2, HOOK_CALL).install()->quick();
Utils::Hook::Nop(0x51FBCC, 1);
Utils::Hook::Nop(0x522A33, 1);
// Intercept map loading for usermap initialization
Utils::Hook(0x6245E3, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
Utils::Hook(0x62493E, Maps::SpawnServerStub, HOOK_CALL).install()->quick();

View File

@ -755,18 +755,6 @@ namespace Components
Menus::CustomMenus.push_back(menu);
}
void Menus::RegisterCustomMenusHook()
{
Utils::Hook::Call<void()>(0x401700)(); // call original load functions
#ifdef _DEBUG
for (int i = 0; i < Game::uiContext->menuCount; i++)
{
OutputDebugStringA(Utils::String::VA("%s\n", Game::uiContext->Menus[i]->window.name));
}
#endif
}
Menus::Menus()
{
if (ZoneBuilder::IsEnabled())
@ -786,9 +774,6 @@ namespace Components
// Don't open connect menu
//Utils::Hook::Nop(0x428E48, 5);
// register custom menufiles if they exist
Utils::Hook(0x4A58C3, Menus::RegisterCustomMenusHook, HOOK_CALL).install()->quick();
// Use the connect menu open call to update server motds
Utils::Hook(0x428E48, []()
{

View File

@ -56,8 +56,6 @@ namespace Components
static void RemoveMenuFromContext(Game::UiContext* dc, Game::menuDef_t* menu);
static void RegisterCustomMenusHook();
// Ugly!
static int KeywordHash(char* key);
};

View File

@ -14,6 +14,7 @@ namespace Components
Dvar::Var Renderer::r_drawModelNames;
Dvar::Var Renderer::r_drawAABBTrees;
Dvar::Var Renderer::r_playerDrawDebugDistance;
Dvar::Var Renderer::r_forceTechnique;
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
@ -171,6 +172,32 @@ namespace Components
return Utils::Hook::Call<int(int, float, float, const char*, Game::vec4_t*, int)>(0x005033E0)(a1, a2, a3, Utils::String::VA("%s (^3%s^7)", mat->info.name, mat->techniqueSet->name), color, a6);
}
void ListSamplers()
{
static auto* source = reinterpret_cast<Game::GfxCmdBufSourceState*>(0x6CAF080);
Game::Font_s* font = Game::R_RegisterFont("fonts/smallFont", 0);
auto height = Game::R_TextHeight(font);
auto scale = 1.0f;
float color[4] = {0.0f, 1.0f, 0.0f, 1.0f};
for (std::size_t i = 0; i < 27; ++i)
{
if (source->input.codeImages[i] == nullptr)
{
color[0] = 1.f;
}
else
{
color[0] = 0.f;
}
std::stringstream str;
str << std::format("{}/{:#X} => ", i, i) << (source->input.codeImages[i] == nullptr ? "---" : source->input.codeImages[i]->name) << " " << std::to_string(source->input.codeImageSamplerStates[i]);
Game::R_AddCmdDrawText(str.str().data(), std::numeric_limits<int>::max(), font, 15.0f, (height * scale + 1) * (i + 1) + 14.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL);
}
}
void Renderer::DebugDrawTriggers()
{
if (!r_drawTriggers.get<bool>()) return;
@ -326,21 +353,15 @@ namespace Components
// Static models
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
{
auto staticModel = world->dpvs.smodelDrawInsts[i];
auto staticModel = &world->dpvs.smodelDrawInsts[i];
Game::Bounds* b = &world->dpvs.smodelInsts[i].bounds;
if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist)
if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel->placement.origin) < sqrDist)
{
if (staticModel.model)
if (staticModel->model)
{
Game::Bounds b = staticModel.model->bounds;
b.midPoint[0] += staticModel.placement.origin[0];
b.midPoint[1] += staticModel.placement.origin[1];
b.midPoint[2] += staticModel.placement.origin[2];
b.halfSize[0] *= staticModel.placement.scale;
b.halfSize[1] *= staticModel.placement.scale;
b.halfSize[2] *= staticModel.placement.scale;
Game::R_AddDebugBounds(staticModelsColor, &b);
Game::R_AddDebugBounds(staticModelsColor, b);
}
}
}
@ -447,6 +468,29 @@ namespace Components
}
}
void Renderer::ForceTechnique()
{
auto forceTechnique = r_forceTechnique.get<int>();
if (forceTechnique > 0)
{
Utils::Hook::Set(0x6FABDF4, forceTechnique);
}
}
int Renderer::FixSunShadowPartitionSize(Game::GfxCamera* camera, Game::GfxSunShadowMapMetrics* mapMetrics, Game::GfxSunShadow* sunShadow, Game::GfxSunShadowClip* clip, float* partitionFraction)
{
auto result = Utils::Hook::Call<int(Game::GfxCamera*, Game::GfxSunShadowMapMetrics*, Game::GfxSunShadow*, Game::GfxSunShadowClip*, float*)>(0x5463B0)(camera, mapMetrics, sunShadow, clip, partitionFraction);
if (Maps::IsCustomMap())
{
// Fixes shadowmap viewport which fixes pixel adjustment shadowmap bug - partly, because the real problem lies within the way CoD4 shaders are programmed
sunShadow->partition[Game::SunShadowPartition::R_SUNSHADOW_FAR].viewportParms.viewport = sunShadow->partition[Game::SunShadowPartition::R_SUNSHADOW_NEAR].viewportParms.viewport;
}
return result;
}
Renderer::Renderer()
{
if (Dedicated::IsEnabled()) return;
@ -460,9 +504,14 @@ namespace Components
DebugDrawModelBoundingBoxes();
DebugDrawSceneModelCollisions();
DebugDrawTriggers();
ForceTechnique();
}
}, Scheduler::Pipeline::RENDERER);
// COD4 Map Fixes
// The day map porting is perfect we should be able to remove these
Utils::Hook(0x546A09, FixSunShadowPartitionSize, HOOK_CALL).install()->quick();
// Log broken materials
Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick();
Utils::Hook(0x0054CF8D, Renderer::StoreGfxBufContextPtrStub2, HOOK_JUMP).install()->quick();
@ -510,6 +559,7 @@ namespace Components
Renderer::r_drawModelNames = Game::Dvar_RegisterEnum("r_drawModelNames", values, 0, Game::DVAR_CHEAT, "Draw all model names");
Renderer::r_drawAABBTrees = Game::Dvar_RegisterBool("r_drawAabbTrees", false, Game::DVAR_CHEAT, "Draw aabb trees");
Renderer::r_playerDrawDebugDistance = Game::Dvar_RegisterInt("r_drawDebugDistance", 1000, 0, 50000, Game::DVAR_ARCHIVE, "r_draw debug functions draw distance, relative to the player");
Renderer::r_forceTechnique = Game::Dvar_RegisterInt("r_forceTechnique", 0, 0, 14, Game::DVAR_NONE, "Force a base technique on the renderer");
}, Scheduler::Pipeline::MAIN);
}

View File

@ -39,6 +39,9 @@ namespace Components
static void DebugDrawModelBoundingBoxes();
static void DebugDrawModelNames();
static void DebugDrawAABBTrees();
static void ForceTechnique();
static int FixSunShadowPartitionSize(Game::GfxCamera* camera, Game::GfxSunShadowMapMetrics* mapMetrics, Game::GfxSunShadow* sunShadow, Game::GfxSunShadowClip* clip, float* partitionFraction);
static Utils::Signal<Renderer::Callback> EndRecoverDeviceSignal;
static Utils::Signal<Renderer::Callback> BeginRecoverDeviceSignal;
@ -52,5 +55,6 @@ namespace Components
static Dvar::Var r_drawModelNames;
static Dvar::Var r_drawAABBTrees;
static Dvar::Var r_playerDrawDebugDistance;
static Dvar::Var r_forceTechnique;
};
}

View File

@ -225,6 +225,9 @@ namespace Components
// Sanitize name for empty assets
if (name[0] == ',') name.erase(name.begin());
// Fix forward slashes for FXEffectDef (and probably other assets)
std::replace(name.begin(), name.end(), '\\', '/');
if (this->findAsset(type, name) != -1 || this->findSubAsset(type, name).data) return true;
if (type == Game::XAssetType::ASSET_TYPE_INVALID || type >= Game::XAssetType::ASSET_TYPE_COUNT)
@ -245,6 +248,9 @@ namespace Components
asset.type = type;
asset.header = assetHeader;
// Handle script strings
AssetHandler::ZoneMark(asset, this);
if (isSubAsset)
{
this->loadedSubAssets.push_back(asset);
@ -254,8 +260,6 @@ namespace Components
this->loadedAssets.push_back(asset);
}
// Handle script strings
AssetHandler::ZoneMark(asset, this);
return true;
}
@ -447,38 +451,30 @@ namespace Components
Utils::Stream::ClearPointer(&zoneHeader.assetList.assets);
// Increment ScriptStrings count (for empty script string) if available
if (!this->scriptStrings.empty())
{
zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1;
Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings);
}
zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1;
Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings);
// Write header
this->buffer.save(&zoneHeader, sizeof(Game::ZoneHeader));
this->buffer.pushBlock(Game::XFILE_BLOCK_VIRTUAL); // Push main stream onto the stream stack
// Write ScriptStrings, if available
if (!this->scriptStrings.empty())
this->buffer.saveNull(4);
// Empty script string?
// This actually represents a NULL string, but as scriptString.
// So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString.
// That's the reason why the count is incremented by 1, if scriptStrings are available.
// Write ScriptString pointer table
for (std::size_t i = 0; i < this->scriptStrings.size(); ++i)
{
this->buffer.saveNull(4);
// Empty script string?
// This actually represents a NULL string, but as scriptString.
// So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString.
// That's the reason why the count is incremented by 1, if scriptStrings are available.
this->buffer.saveMax(4);
}
// Write ScriptString pointer table
for (std::size_t i = 0; i < this->scriptStrings.size(); ++i)
{
this->buffer.saveMax(4);
}
this->buffer.align(Utils::Stream::ALIGN_4);
// Write ScriptStrings
for (auto ScriptString : this->scriptStrings)
{
this->buffer.saveString(ScriptString.data());
}
// Write ScriptStrings
for (auto ScriptString : this->scriptStrings)
{
this->buffer.saveString(ScriptString.data());
}
// Align buffer (4 bytes) to get correct offsets for pointers
@ -530,7 +526,7 @@ namespace Components
constexpr auto* data = "Built using the IW4x Zone:B:uilder Version 4";
auto dataLen = std::strlen(data); // + 1 is added by the save code
this->branding = { this->zoneName.data(), 0, static_cast<int>(dataLen), data };
this->branding = {this->zoneName.data(), 0, static_cast<int>(dataLen), data};
if (this->findAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, this->branding.name) != -1)
{
@ -641,9 +637,9 @@ namespace Components
}
// Remap a scriptString to it's corresponding value in the local scriptString table.
void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex)
void ZoneBuilder::Zone::mapScriptString(unsigned short& gameIndex)
{
*gameIndex = 0xFFFF & this->scriptStringMap[*gameIndex];
gameIndex = 0xFFFF & this->scriptStringMap[gameIndex];
}
// Store a new name for a given asset
@ -1006,6 +1002,19 @@ namespace Components
return "";
}
void ZoneBuilder::ReallocateLoadedSounds(void*& data, [[maybe_unused]] void* a2)
{
assert(data);
auto* sound = Utils::Hook::Get<Game::MssSound*>(0x112AE04);
auto length = sound->info.data_len;
auto allocatedSpace = Utils::Memory::AllocateArray<char>(length);
memcpy_s(allocatedSpace, length, data, length);
data = allocatedSpace;
sound->data = allocatedSpace;
sound->info.data_ptr = allocatedSpace;
}
ZoneBuilder::ZoneBuilder()
{
// ReSharper disable CppStaticAssertFailure
@ -1048,7 +1057,7 @@ namespace Components
//Utils::Hook::Nop(0x5BB632, 5);
// Don't load sounds
//Utils::Hook::Set<BYTE>(0x413430, 0xC3);
Utils::Hook(0x492EFC, ReallocateLoadedSounds, HOOK_CALL).install()->quick();
// Don't display errors when assets are missing (we might manually build those)
Utils::Hook::Nop(0x5BB3F2, 5);
@ -1228,6 +1237,41 @@ namespace Components
}
});
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, [[maybe_unused]] const std::string& name, [[maybe_unused]] bool* restrict)
{
if (type != Game::ASSET_TYPE_SOUND)
{
return;
}
auto sound = asset.sound;
for (size_t i = 0; i < sound->count; i++)
{
auto thisSound = sound->head[i];
if (thisSound.soundFile->type == Game::SAT_LOADED)
{
if (thisSound.soundFile->u.loadSnd->sound.data == nullptr)
{
// ouch
// This should never happen and will cause a memory leak
// Let's change it to a streamed sound instead
thisSound.soundFile->type = Game::SAT_STREAMED;
auto virtualPath = std::filesystem::path(thisSound.soundFile->u.loadSnd->name);
thisSound.soundFile->u.streamSnd.filename.info.raw.name = Utils::Memory::DuplicateString(virtualPath.filename().string());
auto dir = virtualPath.remove_filename().string();
dir = dir.substr(0, dir.size() - 1); // remove /
thisSound.soundFile->u.streamSnd.filename.info.raw.dir = Utils::Memory::DuplicateString(dir);
}
}
}
});
Command::Add("buildtechsets", [](Command::Params*)
{
Utils::IO::CreateDir("zone_source/techsets");

View File

@ -60,7 +60,7 @@ namespace Components
int findScriptString(const std::string& str);
void addRawAsset(Game::XAssetType type, void* ptr);
void mapScriptString(unsigned short* gameIndex);
void mapScriptString(unsigned short& gameIndex);
void renameAsset(Game::XAssetType type, const std::string& asset, const std::string& newName);
std::string getAssetName(Game::XAssetType type, const std::string& asset);
@ -138,6 +138,7 @@ namespace Components
static void ReleaseTexture(Game::XAssetHeader header);
static std::string FindMaterialByTechnique(const std::string& name);
static void ReallocateLoadedSounds(void*& data, void* a2);
static int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/);
static void HandleError(Game::errorParm_t code, const char* fmt, ...);

View File

@ -1456,7 +1456,7 @@ namespace Components
{
Game::MaterialShaderArgument* arg = &argument[i];
if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT)
if (arg->type != Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST && arg->type != Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST)
{
continue;
}
@ -1730,7 +1730,7 @@ namespace Components
{
Game::GfxImageLoadDef* texture;
char mapType;
char semantic;
Game::TextureSemantic semantic;
char category;
char flags;
int cardMemory;

View File

@ -323,6 +323,7 @@ namespace Game
PhysPreset*** varPhysPresetPtr = reinterpret_cast<PhysPreset***>(0x112B378);
MaterialPass** varMaterialPass = reinterpret_cast<MaterialPass**>(0x112A960);
snd_alias_list_t*** varsnd_alias_list_name = reinterpret_cast<snd_alias_list_t***>(0x112AF38);
MaterialVertexShader** varMaterialVertexShader = reinterpret_cast<MaterialVertexShader**>(0x112B338);
FxElemField* s_elemFields = reinterpret_cast<FxElemField*>(0x73B848);

View File

@ -646,6 +646,7 @@ namespace Game
extern PhysPreset*** varPhysPresetPtr;
extern MaterialPass** varMaterialPass;
extern snd_alias_list_t*** varsnd_alias_list_name;
extern MaterialVertexShader** varMaterialVertexShader;
extern FxElemField* s_elemFields;

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,15 @@ namespace Utils::Compression
// Make sure the buffer is large enough
if (length < 100) length *= 10;
char* buffer = allocator.allocateArray<char>(length);
if (compress2(reinterpret_cast<Bytef*>(buffer), &length, reinterpret_cast<Bytef*>(const_cast<char*>(data.data())), data.size(), Z_BEST_COMPRESSION) != Z_OK)
auto* buffer = allocator.allocateArray<char>(length);
#ifdef _DEBUG
constexpr auto compression = Z_NO_COMPRESSION;
#else
constexpr auto compression = Z_BEST_COMPRESSION;
#endif
if (compress2((Bytef*)(buffer), &length, (const Bytef*)(data.data()), data.size(), compression) != Z_OK)
{
return {};
}
@ -34,13 +41,13 @@ namespace Utils::Compression
int ret;
Memory::Allocator allocator;
std::uint8_t* dest = allocator.allocateArray<uint8_t>(CHUNK);
const char* dataPtr = data.data();
auto* dest = allocator.allocateArray<std::uint8_t>(CHUNK);
const auto* dataPtr = data.data();
do
{
stream.avail_in = std::min(static_cast<size_t>(CHUNK), data.size() - (dataPtr - data.data()));
stream.next_in = reinterpret_cast<const uint8_t*>(dataPtr);
stream.avail_in = std::min(static_cast<std::size_t>(CHUNK), data.size() - (dataPtr - data.data()));
stream.next_in = reinterpret_cast<const std::uint8_t*>(dataPtr);
dataPtr += stream.avail_in;
do

View File

@ -35,7 +35,9 @@ namespace Utils
{
const auto& model = itr->second;
if (!model.empty() && model[0] != '*' && model[0] != '?') // Skip brushmodels
if (!model.empty() && model[0] != '*' && model[0] != '?' && // Skip brushmodels
model != "com_plasticcase_green_big_us_dirt"s // Team zones
)
{
if (std::find(models.begin(), models.end(), model) == models.end())
{

View File

@ -1,4 +1,5 @@
#include <STDInclude.hpp>
#include <bitset>
namespace Utils::Json
{
@ -31,4 +32,32 @@ namespace Utils::Json
return "null";
}
}
unsigned long ReadFlags(const std::string binaryFlags, size_t size)
{
std::bitset<64> input;
const auto binarySize = size * 8;
if (binaryFlags.size() > binarySize)
{
Components::Logger::Print("Flag {} might not be properly translated, it seems to contain an error (invalid length)\n", binaryFlags);
return 0;
}
auto i = binarySize - 1;
for (char bit : binaryFlags)
{
if (i < 0)
{
Components::Logger::Print("Flag {} might not be properly translated, it seems to contain an error (invalid length)\n", binaryFlags);
break;
}
bool isOne = bit == '1';
input.set(i--, isOne);
}
return input.to_ulong();
}
}

View File

@ -3,4 +3,6 @@
namespace Utils::Json
{
std::string TypeToString(nlohmann::json::value_t type);
unsigned long ReadFlags(const std::string binaryFlags, size_t size);
}

View File

@ -39,10 +39,44 @@ namespace Utils
{
return readArray<T>(1);
}
template <typename T> T* readArrayOnce(std::size_t count = 1)
{
constexpr auto POINTER = 255;
constexpr auto FOLLOWING = 254;
auto b = static_cast<unsigned char>(readByte());
switch (b)
{
case POINTER:
{
auto ptr = read<int>();
auto* voidPtr = reinterpret_cast<void*>(ptr);
if (allocator->isPointerMapped(voidPtr))
{
return allocator->getPointer<T>(voidPtr);
}
throw std::runtime_error("Bad data: missing ptr");
}
case FOLLOWING:
{
auto filePosition = position;
auto data = readArray<T>(count);
allocator->mapPointer(reinterpret_cast<void*>(filePosition), data);
return data;
}
default:
throw std::runtime_error("Bad data");
}
}
template <typename T> T* readArray(std::size_t count = 1)
{
return static_cast<T*>(this->read(sizeof(T), count));
}
template <typename T> T read()
{
T obj;