Merge branch 'develop' of https://github.com/XLabsProject/iw4x-client into feature/xinput

This commit is contained in:
rackover 2021-05-02 11:47:33 +02:00
commit 332232b88f
23 changed files with 1366 additions and 200 deletions

View File

@ -4,7 +4,7 @@
![issues](https://img.shields.io/github/issues/IW4x/iw4x-client.svg) ![issues](https://img.shields.io/github/issues/IW4x/iw4x-client.svg)
[![build status](https://ci.appveyor.com/api/projects/status/rvljq0ooxen0oexm/branch/develop?svg=true)](https://ci.appveyor.com/project/iw4x/iw4x-client/branch/develop) [![build status](https://ci.appveyor.com/api/projects/status/rvljq0ooxen0oexm/branch/develop?svg=true)](https://ci.appveyor.com/project/iw4x/iw4x-client/branch/develop)
[![discord](https://img.shields.io/endpoint?url=https://momo5502.com/iw4x/members-badge.php)](https://discord.gg/sKeVmR3) [![discord](https://img.shields.io/endpoint?url=https://momo5502.com/iw4x/members-badge.php)](https://discord.gg/sKeVmR3)
[![patreon](https://img.shields.io/badge/patreon-support-blue.svg?logo=patreon)](https://www.patreon.com/iw4x) [![patreon](https://img.shields.io/badge/patreon-support-blue.svg?logo=patreon)](https://www.patreon.com/xlabsproject)
# IW4x: Client # IW4x: Client

View File

@ -456,10 +456,7 @@ workspace "iw4x"
--iw4mvm.project() --iw4mvm.project()
workspace "*" workspace "*"
buildoptions { cppdialect "C++17"
"/std:c++latest"
}
systemversion "latest"
defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" } defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" }
rule "ProtobufCompiler" rule "ProtobufCompiler"

View File

@ -7,12 +7,13 @@ namespace Assets
void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{ {
if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx
if (!header->data /*&& !builder->isPrimaryAsset()*/) 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->loadBinary(header, name, builder); // Check if we need to import a new one into the game
if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one
} }
void IFxEffectDef::loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) void IFxEffectDef::loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
{ {
switch (elemType) switch (elemType)
{ {
case Game::FX_ELEM_TYPE_MODEL: case Game::FX_ELEM_TYPE_MODEL:
@ -34,10 +35,7 @@ namespace Assets
if (visuals->soundName) if (visuals->soundName)
{ {
visuals->soundName = reader->readCString(); visuals->soundName = reader->readCString();
visuals->soundName = "null";
Components::Logger::Print("Unable to load sounds yet!\n");
} }
break; break;
} }

View File

@ -4,7 +4,7 @@ namespace Assets
{ {
void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{ {
Components::FileSystem::File soundFile(Utils::String::VA("sounds/%s", name.data())); Components::FileSystem::File soundFile(Utils::String::VA("loaded_sound/%s", name.data()));
if (!soundFile.exists()) if (!soundFile.exists())
{ {
header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd; header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd;
@ -60,8 +60,8 @@ namespace Assets
} }
sound->sound.info.channels = reader.read<short>(); sound->sound.info.channels = reader.read<short>();
sound->sound.info.samples = reader.read<int>();
sound->sound.info.rate = reader.read<int>(); sound->sound.info.rate = reader.read<int>();
sound->sound.info.samples = reader.read<int>();
sound->sound.info.block_size = reader.read<short>(); sound->sound.info.block_size = reader.read<short>();
sound->sound.info.bits = reader.read<short>(); sound->sound.info.bits = reader.read<short>();

View File

@ -29,6 +29,33 @@ namespace Assets
"_add_lin_nofog", "_add_lin_nofog",
}; };
std::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_multiply", "wc_unlit_multiply_lin"},
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
{"wc_unlit", "wc_unlit_add_lin"},
{"wc_unlit_alphatest", "wc_unlit_blend_lin_ua"},
{"wc_unlit_multiply_lin", "wc_unlit_multiply_lin"},
{"wc_unlit_blend", "wc_unlit_blend_lin"},
{"wc_unlit_replace", "wc_unlit_replace_lin"},
{"mc_unlit_replace", "mc_unlit_replace_lin"},
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
{"mc_unlit", "mc_unlit_blend_lin"},
{"mc_unlit_alphatest", "mc_unlit_blend_lin"}
/*,
{"", ""},
{"", ""},
{"", ""},
{"", ""},
{"", ""},*/
};
Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data())); Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data()));
if (!materialFile.exists()) return; if (!materialFile.exists()) return;
@ -185,12 +212,14 @@ namespace Assets
if (!t1->techniques[i] && !t2->techniques[i]) continue;; if (!t1->techniques[i] && !t2->techniques[i]) continue;;
if (!t1->techniques[i] || !t2->techniques[i]) return false; if (!t1->techniques[i] || !t2->techniques[i]) return false;
if (t1->techniques[i]->flags != t1->techniques[i]->flags) return false; // Apparently, this is really not that important
//if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false;
} }
return true; return true;
}; };
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry) Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry)
{ {
if (!replacementFound) if (!replacementFound)
@ -209,8 +238,52 @@ namespace Assets
if (!replacementFound && asset->techniqueSet) if (!replacementFound && asset->techniqueSet)
{ {
Components::Logger::Print("No replacement found for material %s with techset %s\n", asset->info.name, asset->techniqueSet->name); Components::Logger::Print("No replacement found for material %s with techset %s\n", asset->info.name, asset->techniqueSet->name);
std::string techName = asset->techniqueSet->name;
if (techSetCorrespondance.find(techName) != techSetCorrespondance.end()) {
auto iw4TechSetName = techSetCorrespondance[techName];
Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
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)
{
Components::Logger::Print("Material %s with techset %s has been mapped to %s (last chance!)\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name);
asset->info.sortKey = header.material->info.sortKey;
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
// 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)
{
Components::Logger::Print("Could not find any loaded material with techset %s (in replacement of %s), so I cannot set the sortkey for material %s\n", iw4TechSetName.data(), asset->techniqueSet->name, asset->info.name);
}
}
else
{
Components::Logger::Print("Could not find any loaded techset with iw4 name %s for iw3 techset %s\n", iw4TechSetName.data(), asset->techniqueSet->name);
}
}
else
{
Components::Logger::Print("Could not match iw3 techset %s 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.data());
}
} }
if (!reader.end()) if (!reader.end())

View File

@ -15,9 +15,9 @@ namespace Assets
} }
} }
void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
{ {
Game::WeaponCompleteDef* asset = header.weapon; Game::WeaponCompleteDef* asset = header.weapon;
// convert all script strings // convert all script strings
if (asset->hideTags) if (asset->hideTags)
@ -119,7 +119,80 @@ namespace Assets
if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect); if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect);
if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect); if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect);
if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect); if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect);
}
#define LoadWeapSound(sound) if (asset->weapDef->##sound##) builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, asset->weapDef->##sound##)
LoadWeapSound(pickupSound);
LoadWeapSound(pickupSoundPlayer);
LoadWeapSound(ammoPickupSound);
LoadWeapSound(ammoPickupSoundPlayer);
LoadWeapSound(projectileSound);
LoadWeapSound(pullbackSound);
LoadWeapSound(pullbackSoundPlayer);
LoadWeapSound(fireSound);
LoadWeapSound(fireSoundPlayer);
LoadWeapSound(fireSoundPlayerAkimbo);
LoadWeapSound(fireLoopSound);
LoadWeapSound(fireLoopSoundPlayer);
LoadWeapSound(fireStopSound);
LoadWeapSound(fireStopSoundPlayer);
LoadWeapSound(fireLastSound);
LoadWeapSound(fireLastSoundPlayer);
LoadWeapSound(emptyFireSound);
LoadWeapSound(emptyFireSoundPlayer);
LoadWeapSound(meleeSwipeSound);
LoadWeapSound(meleeSwipeSoundPlayer);
LoadWeapSound(meleeHitSound);
LoadWeapSound(meleeMissSound);
LoadWeapSound(rechamberSound);
LoadWeapSound(rechamberSoundPlayer);
LoadWeapSound(reloadSound);
LoadWeapSound(reloadSoundPlayer);
LoadWeapSound(reloadEmptySound);
LoadWeapSound(reloadEmptySoundPlayer);
LoadWeapSound(reloadStartSound);
LoadWeapSound(reloadStartSoundPlayer);
LoadWeapSound(reloadEndSound);
LoadWeapSound(reloadEndSoundPlayer);
LoadWeapSound(detonateSound);
LoadWeapSound(detonateSoundPlayer);
LoadWeapSound(nightVisionWearSound);
LoadWeapSound(nightVisionWearSoundPlayer);
LoadWeapSound(nightVisionRemoveSound);
LoadWeapSound(nightVisionRemoveSoundPlayer);
LoadWeapSound(altSwitchSound);
LoadWeapSound(altSwitchSoundPlayer);
LoadWeapSound(raiseSound);
LoadWeapSound(raiseSoundPlayer);
LoadWeapSound(firstRaiseSound);
LoadWeapSound(firstRaiseSoundPlayer);
LoadWeapSound(putawaySound);
LoadWeapSound(putawaySoundPlayer);
LoadWeapSound(scanSound);
if (asset->weapDef->bounceSound)
{
for (size_t i = 0; i < 31; i++)
{
LoadWeapSound(bounceSound[i]);
}
}
LoadWeapSound(projExplosionSound);
LoadWeapSound(projDudSound);
LoadWeapSound(projIgnitionSound);
LoadWeapSound(turretOverheatSound);
LoadWeapSound(turretBarrelSpinMaxSnd);
for (size_t i = 0; i < 4; i++)
{
LoadWeapSound(turretBarrelSpinUpSnd[i]);
LoadWeapSound(turretBarrelSpinDownSnd[i]);
}
LoadWeapSound(missileConeSoundAlias);
LoadWeapSound(missileConeSoundAliasAtBase);
}
void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer) void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer)
{ {
@ -274,9 +347,9 @@ namespace Assets
{ {
buffer->align(Utils::Stream::ALIGN_4); buffer->align(Utils::Stream::ALIGN_4);
int* ptrs = buffer->dest<int>(); int* ptrs = buffer->dest<int>();
buffer->saveMax(37 * sizeof(Game::snd_alias_list_t*)); buffer->saveMax(31 * sizeof(Game::snd_alias_list_t*));
for (int i = 0; i < 37; i++) for (int i = 0; i < 31; i++)
{ {
if (!def->bounceSound[i]) if (!def->bounceSound[i])
{ {
@ -433,14 +506,16 @@ namespace Assets
if (def->projExplosionSound) if (def->projExplosionSound)
{ {
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->projExplosionSound->aliasName); buffer->saveString(def->projExplosionSound->aliasName);
Utils::Stream::ClearPointer(&dest->projExplosionSound); Utils::Stream::ClearPointer(&dest->projExplosionSound);
} }
if (def->projDudSound) if (def->projDudSound)
{ {
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->projDudSound->aliasName); buffer->saveString(def->projDudSound->aliasName);
Utils::Stream::ClearPointer(&dest->projDudSound); Utils::Stream::ClearPointer(&dest->projDudSound);
} }
@ -476,7 +551,8 @@ namespace Assets
if (def->projIgnitionSound) if (def->projIgnitionSound)
{ {
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->projIgnitionSound->aliasName); buffer->saveString(def->projIgnitionSound->aliasName);
Utils::Stream::ClearPointer(&dest->projIgnitionSound); Utils::Stream::ClearPointer(&dest->projIgnitionSound);
} }
@ -551,7 +627,8 @@ namespace Assets
if (def->turretOverheatSound) if (def->turretOverheatSound)
{ {
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretOverheatSound->aliasName); buffer->saveString(def->turretOverheatSound->aliasName);
Utils::Stream::ClearPointer(&dest->turretOverheatSound); Utils::Stream::ClearPointer(&dest->turretOverheatSound);
} }
@ -569,7 +646,8 @@ namespace Assets
if (def->turretBarrelSpinMaxSnd) if (def->turretBarrelSpinMaxSnd)
{ {
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName); buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName);
Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd); Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd);
} }
@ -577,7 +655,8 @@ namespace Assets
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (!def->turretBarrelSpinUpSnd[i]) continue; if (!def->turretBarrelSpinUpSnd[i]) continue;
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName); buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName);
Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]); Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]);
} }
@ -585,21 +664,24 @@ namespace Assets
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (!def->turretBarrelSpinDownSnd[i]) continue; if (!def->turretBarrelSpinDownSnd[i]) continue;
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName); buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName);
Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]); Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]);
} }
if (def->missileConeSoundAlias) if (def->missileConeSoundAlias)
{ {
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->missileConeSoundAlias->aliasName); buffer->saveString(def->missileConeSoundAlias->aliasName);
Utils::Stream::ClearPointer(&dest->missileConeSoundAlias); Utils::Stream::ClearPointer(&dest->missileConeSoundAlias);
} }
if (def->missileConeSoundAliasAtBase) if (def->missileConeSoundAliasAtBase)
{ {
buffer->saveMax(4); buffer->align(Utils::Stream::ALIGN_4);
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
buffer->saveString(def->missileConeSoundAliasAtBase->aliasName); buffer->saveString(def->missileConeSoundAliasAtBase->aliasName);
Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase); Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase);
} }

View File

@ -1,6 +1,6 @@
#include "StdInclude.hpp" #include "StdInclude.hpp"
#define IW4X_CLIPMAP_VERSION 1 #define IW4X_CLIPMAP_VERSION 2
namespace Assets namespace Assets
{ {
@ -587,12 +587,12 @@ namespace Assets
Game::clipMap_t* orgClipMap = nullptr; Game::clipMap_t* orgClipMap = nullptr;
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, [](Game::XAssetHeader header, void* clipMap) Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, [](Game::XAssetHeader header, void* clipMap)
{
if (!*reinterpret_cast<void**>(clipMap))
{ {
*reinterpret_cast<Game::clipMap_t**>(clipMap) = header.clipMap; if (!*reinterpret_cast<void**>(clipMap))
} {
}, &orgClipMap, false); *reinterpret_cast<Game::clipMap_t**>(clipMap) = header.clipMap;
}
}, &orgClipMap, false);
if (orgClipMap) std::memcpy(clipMap, orgClipMap, sizeof Game::clipMap_t); if (orgClipMap) std::memcpy(clipMap, orgClipMap, sizeof Game::clipMap_t);
@ -605,7 +605,7 @@ namespace Assets
} }
int version = reader.read<int>(); int version = reader.read<int>();
if (version != IW4X_CLIPMAP_VERSION) if (version > IW4X_CLIPMAP_VERSION)
{ {
Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version); Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version);
} }
@ -881,33 +881,38 @@ namespace Assets
clipMap->smodelNodeCount = reader.read<unsigned short>(); clipMap->smodelNodeCount = reader.read<unsigned short>();
clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount); clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
clipMap->checksum = reader.read<int>();
clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts; clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts;
// add triggers to mapEnts // add triggers to mapEnts
/* if (version >= 2) {
std::list<Game::TriggerSlab> slabs; if (clipMap->numSubModels > 0) {
std::list<Game::TriggerHull> hulls; clipMap->mapEnts->trigger.count = clipMap->numSubModels;
std::list<Game::TriggerModel> models; clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels;
for (int i = 0; i < clipMap->numCModels; ++i) Game::TriggerHull* hulls = builder->getAllocator()->allocateArray<Game::TriggerHull>(clipMap->mapEnts->trigger.hullCount);
{ Game::TriggerModel* models = builder->getAllocator()->allocateArray<Game::TriggerModel>(clipMap->mapEnts->trigger.count);
Game::cLeafBrushNode_t* node = &clipMap->cLeafBrushNodes[clipMap->cModels[i].leaf.leafBrushNode];
if (!node->leafBrushCount) continue; // skip empty brushes
int baseHull = hulls.size(); for (unsigned int i = 0; i < clipMap->numSubModels; ++i)
for (int j = 0; j < node->leafBrushCount; ++j)
{
Game::cbrush_t* brush = &clipMap->cBrushes[node->data.leaf.brushes[j]];
int baseSlab = slabs.size();
for (int k = 0; k < brush->numsides; ++k)
{ {
Game::TriggerSlab curSlab; models[i] = reader.read<Game::TriggerModel>();
hulls[i] = reader.read<Game::TriggerHull>();
} }
size_t slabCount = reader.read<size_t>();
clipMap->mapEnts->trigger.slabCount = slabCount;
Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray<Game::TriggerSlab>(clipMap->mapEnts->trigger.slabCount);
for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) {
slabs[i] = reader.read<Game::TriggerSlab>();
}
clipMap->mapEnts->trigger.models = &models[0];
clipMap->mapEnts->trigger.hulls = &hulls[0];
clipMap->mapEnts->trigger.slabs = &slabs[0];
} }
} }
*/
clipMap->checksum = reader.read<int>();
// This mustn't be null and has to have at least 1 'valid' entry. // This mustn't be null and has to have at least 1 'valid' entry.
if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) if (!clipMap->smodelNodeCount || !clipMap->smodelNodes)

View File

@ -4,10 +4,10 @@ namespace Assets
{ {
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
{ {
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.data())); Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.c_str()));
if (!aliasFile.exists()) if (!aliasFile.exists())
{ {
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound; header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.c_str()).sound;
return; return;
} }
@ -18,134 +18,330 @@ namespace Assets
return; return;
} }
aliasList->head = builder->getAllocator()->allocate<Game::snd_alias_t>(); std::string errors;
json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors);
json11::Json aliasesContainer = infoData["head"];
auto aliases = aliasesContainer.array_items();
aliasList->count = aliases.size();
// Allocate
aliasList->head = builder->getAllocator()->allocateArray<Game::snd_alias_t>(aliasList->count);
if (!aliasList->head) if (!aliasList->head)
{ {
Components::Logger::Print("Error allocating memory for sound alias structure!\n"); Components::Logger::Print("Error allocating memory for sound alias structure!\n");
return; return;
} }
aliasList->head->soundFile = builder->getAllocator()->allocate<Game::SoundFile>();
if (!aliasList->head->soundFile) aliasList->aliasName = builder->getAllocator()->duplicateString(name.c_str());
for (size_t i = 0; i < aliasList->count; i++)
{ {
Components::Logger::Print("Error allocating memory for sound alias structure!\n"); json11::Json head = aliasesContainer[i];
return;
}
aliasList->count = 1; if (!infoData.is_object())
{
Components::Logger::Error("Failed to load sound %s!", name.c_str());
return;
}
std::string errors; aliasList->head->soundFile = builder->getAllocator()->allocate<Game::SoundFile>();
json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors); if (!aliasList->head->soundFile)
{
Components::Logger::Print("Error allocating memory for sound alias structure!\n");
return;
}
if (!infoData.is_object()) Game::snd_alias_t* alias = aliasList->head;
{
Components::Logger::Error("Failed to load sound %s!", name.data());
return;
}
Game::snd_alias_t* alias = aliasList->head; // try and parse everything and if it fails then fail for the whole file
auto type = head["type"];
auto subtitle = head["subtitle"];
auto secondaryAliasName = head["secondaryAliasName"];
auto chainAliasName = head["chainAliasName"];
auto soundFile = head["soundFile"];
auto sequence = head["sequence"];
auto volMin = head["volMin"];
auto volMax = head["volMax"];
auto pitchMin = head["pitchMin"];
auto pitchMax = head["pitchMax"];
auto distMin = head["distMin"];
auto distMax = head["distMax"];
auto flags = head["flags"];
auto slavePercentage = head["slavePercentage"];
auto probability = head["probability"];
auto lfePercentage = head["lfePercentage"];
auto centerPercentage = head["centerPercentage"];
auto startDelay = head["startDelay"];
auto volumeFalloffCurve = head["volumeFalloffCurve"];
auto envelopMin = head["envelopMin"];
auto envelopMax = head["envelopMax"];
auto envelopPercentage = head["envelopPercentage"];
auto speakerMap = head["speakerMap"];
auto aliasName = head["aliasName"];
// try and parse everything and if it fails then fail for the whole file // Fix casing
auto type = infoData["type"]; if (soundFile.is_null())
auto subtitle = infoData["subtitle"]; {
auto secondaryAliasName = infoData["secondaryAliasName"]; soundFile = head["soundfile"];
auto chainAliasName = infoData["chainAliasName"];
auto soundFile = infoData["soundFile"];
auto sequence = infoData["sequence"];
auto volMin = infoData["volMin"];
auto volMax = infoData["volMax"];
auto pitchMin = infoData["pitchMin"];
auto pitchMax = infoData["pitchMax"];
auto distMin = infoData["distMin"];
auto distMax = infoData["distMax"];
auto flags = infoData["flags"];
auto slavePercentage = infoData["slavePercentage"];
auto probability = infoData["probability"];
auto lfePercentage = infoData["lfePercentage"];
auto centerPercentage = infoData["centerPercentage"];
auto startDelay = infoData["startDelay"];
auto volumeFalloffCurve = infoData["volumeFalloffCurve"];
auto envelopMin = infoData["envelopMin"];
auto envelopMax = infoData["envelopMax"];
auto envelopPercentage = infoData["envelopPercentage"];
auto speakerMap = infoData["speakerMap"];
if (type.is_null() || soundFile.is_null()) Components::Logger::Print("Fixed casing on %s\n", name.c_str());
{ }
Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile", name.data());
return; if (type.is_null() || soundFile.is_null())
} {
Components::Logger::Print("Type is %s\n", type.dump().c_str());
Components::Logger::Print("SoundFile is %s\n", soundFile.dump().c_str());
Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile\n", name.c_str());
return;
}
#define CHECK(x, type) (x.is_##type##() || x.is_null()) #define CHECK(x, type) (x.is_##type##() || x.is_null())
// TODO: actually support all of those properties // TODO: actually support all of those properties
if (CHECK(type, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) && if (!CHECK(type, number))
CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) &&
CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) &&
CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) &&
CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) &&
CHECK(speakerMap, string))
{
alias->soundFile->exists = true;
if (subtitle.is_string())
{ {
alias->subtitle = subtitle.string_value().data(); Components::Logger::Print("%s is not number but %d (%s)\n", "type", type.type(), type.dump().c_str());
}
if (secondaryAliasName.is_string())
{
alias->secondaryAliasName = secondaryAliasName.string_value().data();
}
if (chainAliasName.is_string())
{
alias->chainAliasName = chainAliasName.string_value().data();
} }
alias->sequence = sequence.int_value(); if (!CHECK(subtitle, string))
alias->volMin = float(volMin.number_value());
alias->volMax = float(volMax.number_value());
alias->pitchMin = float(pitchMin.number_value());
alias->pitchMax = float(pitchMax.number_value());
alias->distMin = float(distMin.number_value());
alias->distMax = float(distMax.number_value());
alias->flags = flags.int_value();
alias->___u15.slavePercentage = float(slavePercentage.number_value());
alias->probability = float(probability.number_value());
alias->lfePercentage = float(lfePercentage.number_value());
alias->centerPercentage = float(centerPercentage.number_value());
alias->startDelay = startDelay.int_value();
alias->envelopMin = float(envelopMin.number_value());
alias->envelopMax = float(envelopMax.number_value());
alias->envelopPercentage = float(envelopPercentage.number_value());
if (volumeFalloffCurve.is_string())
{ {
alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, volumeFalloffCurve.string_value(), builder).sndCurve; Components::Logger::Print("%s is not string but %d (%s)\n", "subtitle", subtitle.type(), subtitle.dump().c_str());
} }
if (type.string_value() == "loaded"s) if (!CHECK(aliasName, string))
{ {
alias->soundFile->type = Game::SAT_LOADED; Components::Logger::Print("%s is not string but %d (%s)\n", "aliasName", aliasName.type(), aliasName.dump().c_str());
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd;
} }
else if (type.string_value() == "streamed"s)
if (!CHECK(secondaryAliasName, string))
{ {
alias->soundFile->type = Game::SAT_STREAMED; Components::Logger::Print("%s is not string but %d (%s)\n", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().c_str());
std::string streamedFile = soundFile.string_value(); }
int split = streamedFile.find_last_of('/');
alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(streamedFile.substr(0, split).c_str()); if (!CHECK(chainAliasName, string))
alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.substr(split).c_str()); {
Components::Logger::Print("%s is not string but %d (%s)\n", "chainAliasName", chainAliasName.type(), chainAliasName.dump().c_str());
}
if (!CHECK(soundFile, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "soundFile", soundFile.type(), soundFile.dump().c_str());
}
if (!CHECK(sequence, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "sequence", sequence.type(), sequence.dump().c_str());
}
if (!CHECK(volMin, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "volMin", volMin.type(), volMin.dump().c_str());
}
if (!CHECK(volMax, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "volMax", volMax.type(), volMax.dump().c_str());
}
if (!CHECK(pitchMin, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMin", pitchMin.type(), pitchMin.dump().c_str());
}
if (!CHECK(pitchMax, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMax", pitchMax.type(), pitchMax.dump().c_str());
}
if (!CHECK(probability, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "probability", probability.type(), probability.dump().c_str());
}
if (!CHECK(lfePercentage, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "lfePercentage", lfePercentage.type(), lfePercentage.dump().c_str());
}
if (!CHECK(centerPercentage, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "centerPercentage", centerPercentage.type(), centerPercentage.dump().c_str());
}
if (!CHECK(startDelay, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "startDelay", startDelay.type(), startDelay.dump().c_str());
}
if (!CHECK(volumeFalloffCurve, string))
{
Components::Logger::Print("%s is not string but %d (%s)\n", "volumeFalloffCurve", volumeFalloffCurve.type(), volumeFalloffCurve.dump().c_str());
}
if (!CHECK(envelopMin, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMin", envelopMin.type(), envelopMin.dump().c_str());
}
if (!CHECK(envelopMax, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMax", envelopMax.type(), envelopMax.dump().c_str());
}
if (!CHECK(envelopPercentage, number))
{
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopPercentage", envelopPercentage.type(), envelopPercentage.dump().c_str());
}
if (!CHECK(speakerMap, object))
{
Components::Logger::Print("%s is not object but %d (%s)\n", "speakerMap", speakerMap.type(), speakerMap.dump().c_str());
}
if (CHECK(type, number) && CHECK(aliasName, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) &&
CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) &&
CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) &&
CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) &&
CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) &&
CHECK(speakerMap, object))
{
alias->soundFile->exists = true;
alias->aliasName = builder->getAllocator()->duplicateString(aliasName.string_value().c_str());
if (subtitle.is_string())
{
alias->subtitle = builder->getAllocator()->duplicateString(subtitle.string_value().c_str());
}
if (secondaryAliasName.is_string())
{
alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.string_value().c_str());
}
if (chainAliasName.is_string())
{
alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.string_value().c_str());
}
alias->sequence = sequence.int_value();
alias->volMin = float(volMin.number_value());
alias->volMax = float(volMax.number_value());
alias->pitchMin = float(pitchMin.number_value());
alias->pitchMax = float(pitchMax.number_value());
alias->distMin = float(distMin.number_value());
alias->distMax = float(distMax.number_value());
alias->flags = flags.int_value();
alias->___u15.slavePercentage = float(slavePercentage.number_value());
alias->probability = float(probability.number_value());
alias->lfePercentage = float(lfePercentage.number_value());
alias->centerPercentage = float(centerPercentage.number_value());
alias->startDelay = startDelay.int_value();
alias->envelopMin = float(envelopMin.number_value());
alias->envelopMax = float(envelopMax.number_value());
alias->envelopPercentage = float(envelopPercentage.number_value());
// Speaker map object
if (!speakerMap.is_null())
{
alias->speakerMap = builder->getAllocator()->allocate<Game::SpeakerMap>();
if (!alias->speakerMap)
{
Components::Logger::Print("Error allocating memory for speakermap in sound alias%s!\n", alias->aliasName);
return;
}
alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].string_value().c_str());
alias->speakerMap->isDefault = speakerMap["isDefault"].bool_value();
if (speakerMap["channelMaps"].is_array())
{
json11::Json::array channelMaps = speakerMap["channelMaps"].array_items();
assert(channelMaps.size() <= 4);
// channelMapIndex should never exceed 1
for (size_t channelMapIndex = 0; channelMapIndex < 2; channelMapIndex++)
{
// subChannelIndex should never exceed 1
for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++)
{
json11::Json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3
auto speakers = channelMap["speakers"].array_items();
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size();
for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++)
{
auto speaker = speakers[speakerIndex];
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = static_cast<float>(speaker["levels0"].number_value());
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = static_cast<float>(speaker["levels1"].number_value());
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = static_cast<int>(speaker["numLevels"].number_value());
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = static_cast<int>(speaker["speaker"].number_value());
}
}
}
}
}
if (volumeFalloffCurve.is_string())
{
std::string fallOffCurve = volumeFalloffCurve.string_value();
if (fallOffCurve.size() == 0)
{
fallOffCurve = "$default";
}
auto curve = Components::AssetHandler::FindAssetForZone(
Game::XAssetType::ASSET_TYPE_SOUND_CURVE,
fallOffCurve.c_str(),
builder
).sndCurve;
alias->volumeFalloffCurve = curve;
}
if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded
{
alias->soundFile->type = Game::SAT_LOADED;
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value().c_str(), builder).loadSnd;
}
else if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed
{
alias->soundFile->type = Game::SAT_STREAMED;
std::string streamedFile = soundFile.string_value();
std::string directory = ""s;
int split = streamedFile.find_last_of('/');
if (split >= 0)
{
directory = streamedFile.substr(0, split);
streamedFile = streamedFile.substr(split+1);
}
alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(directory.c_str());
alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.c_str());
}
else
{
Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s\n", name.c_str(), type.string_value().c_str());
return;
}
aliasList->head[i] = *alias;
} }
else else
{ {
Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s", name.data(), type.string_value().c_str()); Components::Logger::Error("Failed to parse sound %s!\n", name.c_str());
return;
} }
} }
else
{ header->sound = aliasList;
Components::Logger::Error("Failed to parse sound %s!", name.data());
return;
}
#undef CHECK #undef CHECK
@ -155,7 +351,7 @@ namespace Assets
{ {
Game::snd_alias_list_t* asset = header.sound; Game::snd_alias_list_t* asset = header.sound;
for (int i = 0; i < asset->count; ++i) for (unsigned int i = 0; i < asset->count; ++i)
{ {
Game::snd_alias_t* alias = &asset->head[i]; Game::snd_alias_t* alias = &asset->head[i];
@ -166,7 +362,12 @@ namespace Assets
if (alias->volumeFalloffCurve) if (alias->volumeFalloffCurve)
{ {
builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve); if (!builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve))
{
// (Should never happen, but just in case)
alias->volumeFalloffCurve->filename = "$default";
builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve);
}
} }
} }
} }
@ -204,7 +405,7 @@ namespace Assets
Game::snd_alias_t* destHead = buffer->dest<Game::snd_alias_t>(); Game::snd_alias_t* destHead = buffer->dest<Game::snd_alias_t>();
buffer->saveArray(asset->head, asset->count); buffer->saveArray(asset->head, asset->count);
for (int i = 0; i < asset->count; ++i) for (unsigned int i = 0; i < asset->count; ++i)
{ {
Game::snd_alias_t* destAlias = &destHead[i]; Game::snd_alias_t* destAlias = &destHead[i];
Game::snd_alias_t* alias = &asset->head[i]; Game::snd_alias_t* alias = &asset->head[i];

View File

@ -225,6 +225,9 @@ namespace Components
// set flags of cg_drawFPS to archive // set flags of cg_drawFPS to archive
Utils::Hook::Or<BYTE>(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED); Utils::Hook::Or<BYTE>(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED);
// un-cheat camera_thirdPersonCrosshairOffset and add archive flags
Utils::Hook::Xor<BYTE>(0x447B41, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
// un-cheat cg_fov and add archive flags // un-cheat cg_fov and add archive flags
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); Utils::Hook::Xor<BYTE>(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);

View File

@ -170,10 +170,10 @@ namespace Components
"/dev/full", "/dev/full",
"/dev/sdb", "/dev/sdb",
"/dev/sr0", "/dev/sr0",
"/dev//dev/tty0", "/dev/tty0",
"/dev/urandom", "/dev/urandom",
"Snake", "Snake",
"lsb_release -a", "lsb_release -a"
}; };
static const char* contributors[] = static const char* contributors[] =
@ -192,6 +192,7 @@ namespace Components
"INeedGames", "INeedGames",
"Killera", "Killera",
"Lithium", "Lithium",
"Louvenarde",
"OneFourOne", "OneFourOne",
"quaK", "quaK",
"RaidMax", "RaidMax",

View File

@ -112,6 +112,9 @@ namespace Components
const unsigned int vertOffset = surface->tris.firstVertex + 1; const unsigned int vertOffset = surface->tris.firstVertex + 1;
const unsigned int indexOffset = surface->tris.baseIndex; const unsigned int indexOffset = surface->tris.baseIndex;
// Fuck cube maps for now
if(this->findImage(surface->material, "colorMap")->mapType == 5) continue;
auto& f = this->getFaceList(surface->material); auto& f = this->getFaceList(surface->material);
for (unsigned short j = 0; j < surface->tris.triCount; ++j) for (unsigned short j = 0; j < surface->tris.triCount; ++j)
@ -332,33 +335,31 @@ namespace Components
this->object_.append("\n"); this->object_.append("\n");
} }
void writeMaterial(Game::Material* material) Game::GfxImage* findImage(Game::Material* material, const std::string& type) const
{ {
std::string name = material->info.name; Game::GfxImage* image = nullptr;
const auto pos = name.find_last_of('/'); const auto hash = Game::R_HashString(type.data());
if (pos != std::string::npos)
{
name = name.substr(pos + 1);
}
this->object_.append(Utils::String::VA("usemtl %s\n", name.data()));
this->object_.append("s off\n");
Game::GfxImage *image = nullptr;
for (char l = 0; l < material->textureCount; ++l) for (char l = 0; l < material->textureCount; ++l)
{ {
if (material->textureTable[l].nameStart == 'c' && material->textureTable[l].nameEnd == 'p') if (material->textureTable[l].nameHash == hash)
{ {
image = material->textureTable[l].u.image; // Hopefully our colorMap image = material->textureTable[l].u.image; // Hopefully our map
break;
} }
} }
return image;
}
Game::GfxImage* extractImage(Game::Material* material, const std::string& type) const
{
auto* image = this->findImage(material, type);
if (!image) if (!image)
{ {
Logger::Print("Failed to get color map for material: %s\n", material->info.name); return image;
return;
} }
// TODO: This is still wrong. // TODO: This is still wrong.
@ -383,12 +384,42 @@ namespace Components
D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr); D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr);
} }
return image;
}
void writeMaterial(Game::Material* material)
{
std::string name = material->info.name;
const auto pos = name.find_last_of('/');
if (pos != std::string::npos)
{
name = name.substr(pos + 1);
}
this->object_.append(Utils::String::VA("usemtl %s\n", name.data()));
this->object_.append("s off\n");
auto* colorMap = this->extractImage(material, "colorMap");
auto* normalMap = this->extractImage(material, "normalMap");
auto* specularMap = this->extractImage(material, "specularMap");
this->material_.append(Utils::String::VA("\nnewmtl %s\n", name.data())); this->material_.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
this->material_.append("Ka 1.0000 1.0000 1.0000\n"); this->material_.append("Ka 1.0000 1.0000 1.0000\n");
this->material_.append("Kd 1.0000 1.0000 1.0000\n"); this->material_.append("Kd 1.0000 1.0000 1.0000\n");
this->material_.append("illum 1\n"); this->material_.append("illum 1\n");
this->material_.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name)); this->material_.append(Utils::String::VA("map_Ka textures/%s.png\n", colorMap->name));
this->material_.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name)); this->material_.append(Utils::String::VA("map_Kd textures/%s.png\n", colorMap->name));
if (specularMap)
{
this->material_.append(Utils::String::VA("map_Ks textures/%s.png\n", specularMap->name));
}
if (normalMap)
{
this->material_.append(Utils::String::VA("bump textures/%s.png\n", normalMap->name));
}
} }
void writeFaces() void writeFaces()

View File

@ -740,6 +740,22 @@ namespace Components
Utils::Hook::Call<void(Game::gentity_s*, int, int)>(0x408910)(ent, unk, unk2); Utils::Hook::Call<void(Game::gentity_s*, int, int)>(0x408910)(ent, unk, unk2);
} }
bool Maps::SV_SetTriggerModelHook(Game::gentity_s* ent) {
// Use me for debugging
//std::string classname = Game::SL_ConvertToString(ent->script_classname);
//std::string targetname = Game::SL_ConvertToString(ent->targetname);
return Utils::Hook::Call<bool(Game::gentity_s*)>(0x5050C0)(ent);
}
int16 Maps::CM_TriggerModelBounds(int modelPointer, Game::Bounds* bounds) {
#ifdef DEBUG
Game::MapEnts* ents = *reinterpret_cast<Game::MapEnts**>(0x1AA651C); // Use me for debugging
#endif
return Utils::Hook::Call<int16(int, Game::Bounds*)>(0x4416C0)(modelPointer, bounds);
}
Maps::Maps() Maps::Maps()
{ {
Dvar::OnInit([]() Dvar::OnInit([]()
@ -779,6 +795,15 @@ namespace Components
Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick();
Utils::Hook(0x44A4D5, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick(); Utils::Hook(0x44A4D5, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick();
#ifdef DEBUG
// Check trigger models
Utils::Hook(0x5FC0F1, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick();
Utils::Hook(0x5FC2671, Maps::SV_SetTriggerModelHook, HOOK_CALL).install()->quick();
Utils::Hook(0x5050D4, Maps::CM_TriggerModelBounds, HOOK_CALL).install()->quick();
#endif
//
//#define SORT_SMODELS //#define SORT_SMODELS
#if !defined(DEBUG) || !defined(SORT_SMODELS) #if !defined(DEBUG) || !defined(SORT_SMODELS)
// Don't sort static models // Don't sort static models

View File

@ -120,5 +120,7 @@ namespace Components
static void SetSpecularStub1(); static void SetSpecularStub1();
static void SetSpecularStub2(); static void SetSpecularStub2();
static void G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2); static void G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2);
static bool SV_SetTriggerModelHook(Game::gentity_s* ent);
static int16 CM_TriggerModelBounds(int brushModelPointer, Game::Bounds* bounds);
}; };
} }

View File

@ -946,17 +946,12 @@ namespace Components
} }
}); });
Dvar::OnInit([]
{
Dvar::Register<bool>("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees");
});
Scheduler::OnFrame([]() Scheduler::OnFrame([]()
{ {
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get<bool>()) return; if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get<bool>()) return;
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f }; float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0); Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0);
//Game::GfxWorld* gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94); //Game::GfxWorld* gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
@ -973,6 +968,181 @@ namespace Components
} }
}); });
Dvar::OnInit([]
{
Dvar::Register<bool>("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes");
Dvar::Register<bool>("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions");
Dvar::Register<bool>("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers");
Dvar::Register<bool>("r_drawModelNames", false, Game::DVAR_FLAG_CHEAT, "Draw all model names");
Dvar::Register<bool>("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees");
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawModelNames").get<bool>()) return;
float sceneModelsColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
float dobjsColor[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float staticModelsColor[4] = { 1.0f, 0.0f, 1.0f, 1.0f };
auto mapName = Dvar::Var("mapname").get<const char*>();
auto* scene = Game::scene;
auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld;
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name);
}
for (auto i = 0; i < scene->sceneDObjCount; i++)
{
if (scene->sceneDObj[i].obj) {
for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++)
{
Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name);
}
}
}
// Static models
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
{
auto staticModel = world->dpvs.smodelDrawInsts[i];
if (staticModel.model) {
Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name);
}
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawSceneModelBoundingBoxes").get<bool>()) return;
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float blue[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
auto* scene = Game::scene;
for(auto i = 0; i < scene->sceneModelCount; i++)
{
if(!scene->sceneModel[i].model)
continue;
auto b = scene->sceneModel[i].model->bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(red, &b, &scene->sceneModel[i].placement.base.quat);
}
for(auto i = 0; i < scene->sceneDObjCount; i++)
{
scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]);
scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]);
scene->sceneDObj[i].cull.bounds.halfSize[2] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[2]);
if (scene->sceneDObj[i].cull.bounds.halfSize[0] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[1] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[2] < 0) {
Components::Logger::Print("WARNING: Negative half size for DOBJ %s, this will cause culling issues!", scene->sceneDObj[i].obj->models[0]->name);
}
Game::R_AddDebugBounds(blue, &scene->sceneDObj[i].cull.bounds);
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized()) return;
if (!Dvar::Var("r_drawSceneModelCollisions").get<bool>()) return;
float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
auto* scene = Game::scene;
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
for (auto j = 0; j < scene->sceneModel[i].model->numCollSurfs; j++) {
auto b = scene->sceneModel[i].model->collSurfs[j].bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(green, &b, &scene->sceneModel[i].placement.base.quat);
}
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawTriggers").get<bool>()) return;
float hurt[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float hurtTouch[4] = { 0.75f, 0.0f, 0.0f, 1.0f };
float damage[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float multiple[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
auto* entities = Game::g_entities;
for(auto i = 0u; i < 0x800; i++)
{
auto* ent = &entities[i];
if(ent->r.isInUse)
{
Game::Bounds b = ent->r.box;
b.midPoint[0] += ent->r.currentOrigin[0];
b.midPoint[1] += ent->r.currentOrigin[1];
b.midPoint[2] += ent->r.currentOrigin[2];
switch(ent->handler)
{
case Game::ENT_HANDLER_TRIGGER_HURT:
Game::R_AddDebugBounds(hurt, &b);
break;
case Game::ENT_HANDLER_TRIGGER_HURT_TOUCH:
Game::R_AddDebugBounds(hurtTouch, &b);
break;
case Game::ENT_HANDLER_TRIGGER_DAMAGE:
Game::R_AddDebugBounds(damage, &b);
break;
case Game::ENT_HANDLER_TRIGGER_MULTIPLE:
if(ent->spawnflags & 0x40)
Game::R_AddDebugBounds(once, &b);
else
Game::R_AddDebugBounds(multiple, &b);
break;
default:
float rv = std::min((float)ent->handler, (float)5) / 5;
float gv = std::clamp((float)ent->handler-5, (float)0, (float)5) / 5;
float bv = std::clamp((float)ent->handler - 10, (float)0, (float)5) / 5;
float color[4] = { rv, gv, bv, 1.0f };
Game::R_AddDebugBounds(color, &b);
break;
}
}
}
});
// Dvars // Dvars
Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI"); Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI");

View File

@ -108,7 +108,7 @@ namespace Components
void Renderer::R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state) void Renderer::R_TextureFromCodeError(const char* sampler, Game::GfxCmdBufState* state)
{ {
Game::Com_Error(0, "Tried to use '%s' when it isn't valid for material '%s' and technique '%s'", Game::Com_Error(0, "Tried to use sampler '%s' when it isn't valid for material '%s' and technique '%s'",
sampler, state->material->info.name, state->technique->name); sampler, state->material->info.name, state->technique->name);
} }

View File

@ -409,7 +409,7 @@ namespace Components
{ {
Logger::Error(5, "Entity: %i is not a client", gentity); Logger::Error(5, "Entity: %i is not a client", gentity);
} }
return &Game::svs_clients[gentity->number]; return &Game::svs_clients[gentity->s.number];
} }
void Script::AddFunctions() void Script::AddFunctions()

View File

@ -44,7 +44,7 @@ namespace Components
if (!found) if (!found)
{ {
Logger::Error("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type)); Logger::Print("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type));
} }
} }
@ -222,8 +222,10 @@ namespace Components
} }
Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset); Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset);
if (!assetHeader.data) if (!assetHeader.data)
{ Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type)); {
Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type));
return false; return false;
} }

View File

@ -1655,18 +1655,48 @@ namespace Components
image->delayLoadPixels = image359.loaded; image->delayLoadPixels = image359.loaded;
image->name = image359.name; image->name = image359.name;
FixImageCategory(image);
// Used for later stuff // Used for later stuff
(&image->delayLoadPixels)[1] = image359.pad3[1]; (&image->delayLoadPixels)[1] = image359.pad3[1];
} }
else else
{ {
std::memcpy(buffer + 28, buffer + (size - 4), 4); std::memcpy(buffer + 28, buffer + (size - 4), 4);
Game::GfxImage* image = reinterpret_cast<Game::GfxImage*>(buffer);
FixImageCategory(image);
} }
} }
return result; return result;
} }
void Zones::FixImageCategory(Game::GfxImage* image) {
// CODO makes use of additional enumerator values (9, 10, 11) that don't exist in IW4
// We have to translate them. 9 is for Reflection probes, 11 is for Compass, 10 is for Lightmap
switch (image->category)
{
case 9:
image->category = Game::ImageCategory::IMG_CATEGORY_AUTO_GENERATED;
break;
case 10:
image->category = Game::ImageCategory::IMG_CATEGORY_LIGHTMAP;
break;
case 11:
image->category = Game::ImageCategory::IMG_CATEGORY_LOAD_FROM_FILE;
break;
}
if (image->category > 7 || image->category < 0) {
#ifdef DEBUG
if (IsDebuggerPresent()) __debugbreak();
#endif
}
}
bool Zones::LoadXAsset(bool atStreamStart, char* buffer, int size) bool Zones::LoadXAsset(bool atStreamStart, char* buffer, int size)
{ {
int count = 0; int count = 0;

View File

@ -100,5 +100,6 @@ namespace Components
static void LoadMaterialAsset(Game::Material** asset); static void LoadMaterialAsset(Game::Material** asset);
static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size); static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size);
static void LoadTracerDefFxEffect(); static void LoadTracerDefFxEffect();
static void FixImageCategory(Game::GfxImage* image);
}; };
} }

View File

@ -425,6 +425,8 @@ namespace Game
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540); clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
GfxScene* scene = reinterpret_cast<GfxScene*>(0x6944914);
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
{ {
int elSize = DB_GetXAssetSizeHandlers[type](); int elSize = DB_GetXAssetSizeHandlers[type]();
@ -734,6 +736,29 @@ namespace Game
std::memmove(&solution[0], &res[0], sizeof(res)); std::memmove(&solution[0], &res[0], sizeof(res));
} }
void QuatRot(vec3_t* vec, const vec4_t* quat)
{
vec4_t q{ (*quat)[3],(*quat)[0],(*quat)[1],(*quat)[2] };
vec4_t res{ 0, (*vec)[0], (*vec)[1], (*vec)[2] };
vec4_t res2;
vec4_t quat_conj{ q[0], -q[1], -q[2], -q[3] };
QuatMultiply(&q, &res, &res2);
QuatMultiply(&res2, &quat_conj, &res);
(*vec)[0] = res[1];
(*vec)[1] = res[2];
(*vec)[2] = res[3];
}
void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res)
{
(*res)[0] = (*q2)[0] * (*q1)[0] - (*q2)[1] * (*q1)[1] - (*q2)[2] * (*q1)[2] - (*q2)[3] * (*q1)[3];
(*res)[1] = (*q2)[0] * (*q1)[1] + (*q2)[1] * (*q1)[0] - (*q2)[2] * (*q1)[3] + (*q2)[3] * (*q1)[2];
(*res)[2] = (*q2)[0] * (*q1)[2] + (*q2)[1] * (*q1)[3] + (*q2)[2] * (*q1)[0] - (*q2)[3] * (*q1)[1];
(*res)[3] = (*q2)[0] * (*q1)[3] - (*q2)[1] * (*q1)[2] + (*q2)[2] * (*q1)[1] + (*q2)[3] * (*q1)[0];
}
void SortWorldSurfaces(GfxWorld* world) void SortWorldSurfaces(GfxWorld* world)
{ {
DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C); DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C);
@ -812,6 +837,73 @@ namespace Game
Game::R_AddDebugLine(color, v4, v8); Game::R_AddDebugLine(color, v4, v8);
} }
void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4])
{
vec3_t v[8];
auto* center = b->midPoint;
auto* halfSize = b->halfSize;
v[0][0] = -halfSize[0];
v[0][1] = -halfSize[1];
v[0][2] = -halfSize[2];
v[1][0] = halfSize[0];
v[1][1] = -halfSize[1];
v[1][2] = -halfSize[2];
v[2][0] = -halfSize[0];
v[2][1] = halfSize[1];
v[2][2] = -halfSize[2];
v[3][0] = halfSize[0];
v[3][1] = halfSize[1];
v[3][2] = -halfSize[2];
v[4][0] = -halfSize[0];
v[4][1] = -halfSize[1];
v[4][2] = halfSize[2];
v[5][0] = halfSize[0];
v[5][1] = -halfSize[1];
v[5][2] = halfSize[2];
v[6][0] = -halfSize[0];
v[6][1] = halfSize[1];
v[6][2] = halfSize[2];
v[7][0] = halfSize[0];
v[7][1] = halfSize[1];
v[7][2] = halfSize[2];
for(auto& vec : v)
{
QuatRot(&vec, quat);
vec[0] += center[0];
vec[1] += center[1];
vec[2] += center[2];
}
// bottom
Game::R_AddDebugLine(color, v[0], v[1]);
Game::R_AddDebugLine(color, v[1], v[3]);
Game::R_AddDebugLine(color, v[3], v[2]);
Game::R_AddDebugLine(color, v[2], v[0]);
// top
Game::R_AddDebugLine(color, v[4], v[5]);
Game::R_AddDebugLine(color, v[5], v[7]);
Game::R_AddDebugLine(color, v[7], v[6]);
Game::R_AddDebugLine(color, v[6], v[4]);
// verticals
Game::R_AddDebugLine(color, v[0], v[4]);
Game::R_AddDebugLine(color, v[1], v[5]);
Game::R_AddDebugLine(color, v[2], v[6]);
Game::R_AddDebugLine(color, v[3], v[7]);
}
#pragma optimize("", off) #pragma optimize("", off)
__declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/) __declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/)
{ {

View File

@ -870,6 +870,8 @@ namespace Game
extern clientstate_t* clcState; extern clientstate_t* clcState;
extern GfxScene* scene;
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
void Menu_FreeItemMemory(Game::itemDef_s* item); void Menu_FreeItemMemory(Game::itemDef_s* item);
const char* TableLookup(StringTable* stringtable, int row, int column); const char* TableLookup(StringTable* stringtable, int row, int column);
@ -918,9 +920,12 @@ namespace Game
void Vec3Normalize(vec3_t& vec); void Vec3Normalize(vec3_t& vec);
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out); void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution); void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
void QuatRot(vec3_t* vec, const vec4_t* quat);
void QuatMultiply(const vec4_t* q1, const vec4_t* q2, vec4_t* res);
void SortWorldSurfaces(GfxWorld* world); void SortWorldSurfaces(GfxWorld* world);
void R_AddDebugLine(float* color, float* v1, float* v2); void R_AddDebugLine(float* color, float* v1, float* v2);
void R_AddDebugString(float *color, float *pos, float scale, const char *str); void R_AddDebugString(float *color, float *pos, float scale, const char *str);
void R_AddDebugBounds(float* color, Bounds* b); void R_AddDebugBounds(float* color, Bounds* b);
void R_AddDebugBounds(float* color, Bounds* b, const float(*quat)[4]);
} }

View File

@ -98,6 +98,19 @@ namespace Game
DVAR_FLAG_NONEXISTENT = 0xFFFFFFFF //no such dvar DVAR_FLAG_NONEXISTENT = 0xFFFFFFFF //no such dvar
} dvar_flag; } dvar_flag;
enum ImageCategory : char
{
IMG_CATEGORY_UNKNOWN = 0x0,
IMG_CATEGORY_AUTO_GENERATED = 0x1,
IMG_CATEGORY_LIGHTMAP = 0x2,
IMG_CATEGORY_LOAD_FROM_FILE = 0x3,
IMG_CATEGORY_RAW = 0x4,
IMG_CATEGORY_FIRST_UNMANAGED = 0x5,
IMG_CATEGORY_WATER = 0x5,
IMG_CATEGORY_RENDERTARGET = 0x6,
IMG_CATEGORY_TEMP = 0x7,
} ;
enum DvarSetSource enum DvarSetSource
{ {
DVAR_SOURCE_INTERNAL = 0x0, DVAR_SOURCE_INTERNAL = 0x0,
@ -893,7 +906,7 @@ namespace Game
struct MSSChannelMap struct MSSChannelMap
{ {
int speakerCount; unsigned int speakerCount;
MSSSpeakerLevels speakers[6]; MSSSpeakerLevels speakers[6];
}; };
@ -937,7 +950,7 @@ namespace Game
{ {
const char *aliasName; const char *aliasName;
snd_alias_t *head; snd_alias_t *head;
int count; unsigned int count;
}; };
struct cStaticModel_s struct cStaticModel_s
@ -4461,18 +4474,148 @@ namespace Game
char pad3[724]; char pad3[724];
} gclient_t; } gclient_t;
typedef struct gentity_s struct LerpEntityState
{
char pad[0x70];
};
struct clientLinkInfo_t
{
__int16 parentId;
char tagName;
char flags;
};
struct entityState_s
{ {
int number; int number;
unsigned char pad[308]; // 4 int eType;
float origin[3]; // 312 LerpEntityState lerp;
float angles[3]; // 324 int time2;
char pad2[8]; int otherEntityNum;
int attackerEntityNum;
int groundEntityNum;
int loopSound;
int surfType;
union
{
int brushModel;
int triggerModel;
int item;
int xmodel;
int primaryLight;
} index;
int clientNum;
int iHeadIcon;
int iHeadIconTeam;
int solid;
unsigned int eventParm;
int eventSequence;
int events[4];
unsigned int eventParms[4];
unsigned __int16 weapon;
int legsAnim;
int torsoAnim;
int un1;
int un2;
clientLinkInfo_t clientLinkInfo;
unsigned int partBits[6];
int clientMask[1];
};
struct EntHandle
{
unsigned __int16 number;
unsigned __int16 infoIndex;
};
struct entityShared_t
{
char isLinked;
char modelType;
char svFlags;
char isInUse;
Bounds box;
int contents;
Bounds absBox;
float currentOrigin[3];
float currentAngles[3];
EntHandle ownerNum;
int eventTime;
};
enum EntHandler
{
ENT_HANDLER_NULL = 0x0,
ENT_HANDLER_TRIGGER_MULTIPLE = 0x1,
ENT_HANDLER_TRIGGER_HURT = 0x2,
ENT_HANDLER_TRIGGER_HURT_TOUCH = 0x3,
ENT_HANDLER_TRIGGER_DAMAGE = 0x4,
ENT_HANDLER_SCRIPT_MOVER = 0x5,
ENT_HANDLER_SCRIPT_MODEL = 0x6,
ENT_HANDLER_GRENADE = 0x7,
ENT_HANDLER_TIMED_OBJECT = 0x8,
ENT_HANDLER_ROCKET = 0x9,
ENT_HANDLER_CLIENT = 0xA,
ENT_HANDLER_CLIENT_SPECTATOR = 0xB,
ENT_HANDLER_CLIENT_DEAD = 0xC,
ENT_HANDLER_PLAYER_CLONE = 0xD,
ENT_HANDLER_TURRET_INIT = 0xE,
ENT_HANDLER_TURRET = 0xF,
ENT_HANDLER_DROPPED_ITEM = 0x10,
ENT_HANDLER_ITEM_INIT = 0x11,
ENT_HANDLER_ITEM = 0x12,
ENT_HANDLER_PRIMARY_LIGHT = 0x13,
ENT_HANDLER_PLAYER_BLOCK = 0x14,
ENT_HANDLER_VEHICLE = 0x15,
ENT_HANDLER_COUNT
};
typedef struct gentity_s
{
entityState_s s;
entityShared_t r;
gclient_t* client; // 344 gclient_t* client; // 344
unsigned char pad3[28]; void /*Turret*/* turret;
void /*Vehicle*/* vehicle;
int physObjId;
unsigned __int16 model;
char physicsObject;
char takedamage;
char active;
char handler;
char team;
bool freeAfterEvent;
__int16 padding_short;
short classname; short classname;
short pad4; unsigned __int16 script_classname;
unsigned char pad5[248]; unsigned __int16 script_linkName;
unsigned __int16 target;
unsigned __int16 targetname;
unsigned int attachIgnoreCollision;
int spawnflags;
int flags;
int eventTime;
int clipmask;
int processedFrame;
EntHandle parent;
int nextthink;
int health;
int maxHealth;
int damage;
int count;
EntHandle missileTargetEnt;
EntHandle remoteControlledOwner;
gentity_s* tagChildren;
unsigned __int16 attachModelNames[19];
unsigned __int16 attachTagNames[19];
int useCount;
gentity_s* nextFree;
int birthTime;
char pad[100];
} gentity_t; } gentity_t;
#pragma pack(push, 1) #pragma pack(push, 1)
@ -4816,6 +4959,310 @@ namespace Game
GfxCmdBufState *state; GfxCmdBufState *state;
}; };
struct GfxDrawGroupSetupFields
{
unsigned __int16 materialSortedIndex : 15;
unsigned __int16 useHeroLighting : 1;
char sceneLightIndex;
char surfType;
};
union GfxDrawGroupSetup
{
GfxDrawGroupSetupFields fields;
unsigned int packed;
};
struct GfxMarkSurfLightingFields
{
char lmapIndex;
char reflectionProbeIndex;
unsigned __int16 modelIndex;
};
union GfxMarkSurfLighting
{
GfxMarkSurfLightingFields fields;
unsigned int packed;
};
struct GfxMarkSurf
{
GfxDrawGroupSetup drawGroup;
unsigned __int16* indices;
unsigned __int16 triCount;
char modelType;
char pad;
GfxMarkSurfLighting lighting;
};
struct GfxCodeSurf
{
GfxDrawGroupSetup drawGroup;
unsigned int triCount;
unsigned __int16* indices;
unsigned __int16 argOffset;
unsigned __int16 argCount;
};
struct __declspec(align(4)) GfxGlassSurf
{
GfxDrawGroupSetup drawGroup;
char pad;
char reflectionProbeIndex;
unsigned __int16 triCount;
unsigned __int16* indices;
unsigned __int16 lightingHandle;
};
struct GfxCloudSurfFields
{
unsigned __int16 materialSortedIndex;
char cloudDataIndex;
char surfType;
};
union GfxCloudSurf
{
GfxCloudSurfFields fields;
unsigned int packed;
};
struct GfxSparkSurfFields
{
unsigned __int16 materialSortedIndex;
unsigned __int16 sparkDataIndex;
};
union GfxSparkSurf
{
GfxSparkSurfFields fields;
unsigned int packed;
};
struct GfxSceneDef
{
int time;
float floatTime;
float viewOffset[3];
GfxImage* sunShadowImage;
float sunShadowPixelAdjust[4];
};
struct GfxLight
{
char type;
char canUseShadowMap;
char unused[2];
float color[3];
float dir[3];
float origin[3];
float radius;
float cosHalfFovOuter;
float cosHalfFovInner;
int exponent;
unsigned int spotShadowIndex;
GfxLightDef* def;
};
struct GfxVisibleLight
{
char pad[0x2004];
};
struct GfxEntity
{
unsigned int renderFxFlags;
float materialTime;
};
struct GfxSkinnedXModelSurfs
{
void* firstSurf;
};
struct GfxSceneEntityCull
{
volatile unsigned int state;
Bounds bounds;
GfxSkinnedXModelSurfs skinnedSurfs;
};
union GfxSceneEntityInfo
{
void/*cpose_t*/* pose;
unsigned __int16* cachedLightingHandle;
};
struct DSkelPartBits
{
int anim[6];
int control[6];
int worldCtrl[6];
int skel[6];
};
struct DSkel
{
DSkelPartBits partBits;
int timeStamp;
/*DObjAnimMat*/void* mat;
};
struct DObj
{
/*XAnimTree_s*/ void* tree;
unsigned __int16 duplicateParts;
unsigned __int16 entnum;
char duplicatePartsSize;
char numModels;
char numBones;
char flags;
unsigned int ignoreCollision;
volatile int locked;
DSkel skel;
float radius;
unsigned int hidePartBits[6];
XModel** models;
};
struct GfxSceneEntity
{
float lightingOrigin[3];
GfxPlacement placement;
GfxSceneEntityCull cull;
char lods[32];
unsigned __int32 gfxEntIndex : 7;
unsigned __int32 entnum : 12;
unsigned __int32 renderFxFlags : 13;
DObj* obj;
GfxSceneEntityInfo info;
char reflectionProbeIndex;
};
struct GfxScaledPlacement
{
GfxPlacement base;
float scale;
};
struct GfxSceneModel
{
XModelDrawInfo info;
XModel* model;
DObj* obj;
GfxScaledPlacement placement;
unsigned __int32 gfxEntIndex : 7;
unsigned __int32 entnum : 12;
unsigned __int32 renderFxFlags : 13;
float radius;
unsigned __int16* cachedLightingHandle;
float lightingOrigin[3];
char reflectionProbeIndex;
char lod;
};
struct __declspec(align(4)) GfxSceneBrush
{
BModelDrawInfo info;
unsigned __int16 entnum;
GfxBrushModel* bmodel;
GfxPlacement placement;
char reflectionProbeIndex;
};
union GfxSceneGlass
{
struct
{
bool rendered;
char reflectionProbeIndex;
unsigned __int16 lightingHandle;
};
unsigned int packed;
};
union GfxEntCellRefInfo
{
float radius;
GfxBrushModel* bmodel;
};
struct GfxSceneDpvs
{
unsigned int localClientNum;
char* entVisData[7];
unsigned __int16* sceneXModelIndex;
unsigned __int16* sceneDObjIndex;
GfxEntCellRefInfo* entInfo[4];
};
struct __declspec(align(64)) GfxScene
{
GfxCodeSurf codeEmissiveSurfs[2048];
GfxCodeSurf codeTransSurfs[640];
GfxMarkSurf markSurfs[1536];
GfxGlassSurf glassSurfs[768];
GfxCloudSurf cloudSurfs[256];
GfxDrawSurf drawSurfsDepthHack[32];
GfxDrawSurf drawSurfsLitOpaque[8192];
GfxDrawSurf drawSurfsLitTrans[2048];
GfxDrawSurf drawSurfsEmissive[8192];
GfxDrawSurf drawSurfsSunShadow0[4096];
GfxDrawSurf drawSurfsSunShadow1[8192];
GfxDrawSurf drawSurfsSpotShadow0[896];
GfxDrawSurf drawSurfsSpotShadow1[896];
GfxDrawSurf drawSurfsSpotShadow2[896];
GfxDrawSurf drawSurfsSpotShadow3[896];
unsigned int sceneLightIsUsed[32];
unsigned int cachedSceneLightIsUsed[4][32];
GfxSparkSurf sparkSurfs[64];
unsigned int drawSurfLimit[10];
volatile int drawSurfCount[10];
GfxDrawSurf* drawSurfs[10];
volatile int codeSurfUser[2];
volatile int markMeshGuard;
unsigned int codeEmissiveSurfCount;
unsigned int codeTransSurfCount;
unsigned int markSurfCount;
unsigned int glassSurfCount;
GfxSceneDef def;
unsigned int addedLightCount;
GfxLight addedLight[32];
bool isAddedLightCulled[32];
float dynamicSpotLightNearPlaneOffset;
float dynamicSpotLightLength;
GfxVisibleLight visLight[4];
GfxVisibleLight visLightShadow[1];
unsigned int* entOverflowedDrawBuf;
volatile int gfxEntCount;
GfxEntity gfxEnts[128];
int sceneDObjCount;
int preClientSceneDObjCount;
int sceneDObjCountAtMark;
GfxSceneEntity sceneDObj[520];
char sceneDObjVisData[7][512];
int sceneDObjMarkableViewmodelIndex;
unsigned int sceneDObjFirstViewmodelIndex;
unsigned int sceneDObjViewmodelCount;
volatile int sceneModelCount;
int sceneModelCountAtMark;
int sceneDObjModelCount;
GfxSceneModel sceneModel[1024];
char sceneModelVisData[7][1024];
volatile int sceneBrushCount;
int sceneBrushCountAtMark;
GfxSceneBrush sceneBrush[512];
char sceneBrushVisData[3][512];
GfxSceneGlass sceneGlass[1024];
unsigned int sceneDynModelCount;
unsigned int sceneDynBrushCount;
int gfxEntCountAtMark;
GfxSceneDpvs dpvs;
int updateSound;
int allowAddDObj;
};
#pragma endregion #pragma endregion
#ifndef IDA #ifndef IDA

View File

@ -5,8 +5,8 @@
#ifndef RC_INVOKED #ifndef RC_INVOKED
#define _HAS_CXX17 1 //#define _HAS_CXX17 1
#define _HAS_CXX20 1 //#define _HAS_CXX20 1
#define VC_EXTRALEAN #define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS
@ -38,6 +38,7 @@
#include <future> #include <future>
#include <unordered_map> #include <unordered_map>
#include <queue> #include <queue>
#include <algorithm>
// Experimental C++17 features // Experimental C++17 features
#include <filesystem> #include <filesystem>