diff --git a/README.md b/README.md index 186dc7b3..18e6e8fb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![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) [![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 diff --git a/premake5.lua b/premake5.lua index 28eafda6..ea000824 100644 --- a/premake5.lua +++ b/premake5.lua @@ -456,10 +456,7 @@ workspace "iw4x" --iw4mvm.project() workspace "*" - buildoptions { - "/std:c++latest" - } - systemversion "latest" + cppdialect "C++17" defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" } rule "ProtobufCompiler" diff --git a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp index 7cd2c87d..38928466 100644 --- a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp @@ -7,12 +7,13 @@ namespace Assets 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 /*&& !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 /*&& !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) { + switch (elemType) { case Game::FX_ELEM_TYPE_MODEL: @@ -34,10 +35,7 @@ namespace Assets if (visuals->soundName) { visuals->soundName = reader->readCString(); - visuals->soundName = "null"; - Components::Logger::Print("Unable to load sounds yet!\n"); } - break; } diff --git a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp index f2ddb8b0..9dc06fb1 100644 --- a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp +++ b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp @@ -4,7 +4,7 @@ namespace Assets { 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()) { header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd; @@ -60,8 +60,8 @@ namespace Assets } sound->sound.info.channels = reader.read(); - sound->sound.info.samples = reader.read(); sound->sound.info.rate = reader.read(); + sound->sound.info.samples = reader.read(); sound->sound.info.block_size = reader.read(); sound->sound.info.bits = reader.read(); diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index f401eccb..d12974a4 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -29,6 +29,33 @@ namespace Assets "_add_lin_nofog", }; + std::map 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())); 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]) 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; }; + Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry) { if (!replacementFound) @@ -209,8 +238,52 @@ namespace Assets if (!replacementFound && asset->techniqueSet) { - 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()) diff --git a/src/Components/Modules/AssetInterfaces/IWeapon.cpp b/src/Components/Modules/AssetInterfaces/IWeapon.cpp index 92135700..350006d9 100644 --- a/src/Components/Modules/AssetInterfaces/IWeapon.cpp +++ b/src/Components/Modules/AssetInterfaces/IWeapon.cpp @@ -15,9 +15,9 @@ namespace Assets } } - void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::WeaponCompleteDef* asset = header.weapon; + void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::WeaponCompleteDef* asset = header.weapon; // convert all script strings if (asset->hideTags) @@ -64,7 +64,7 @@ namespace Assets builder->addScriptString(asset->weapDef->notetrackRumbleMapValues[i]); } } - + // now load all sub-assets properly if (asset->killIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->killIcon); @@ -119,7 +119,80 @@ namespace Assets 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->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) { @@ -274,9 +347,9 @@ namespace Assets { buffer->align(Utils::Stream::ALIGN_4); int* ptrs = buffer->dest(); - 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]) { @@ -433,14 +506,16 @@ namespace Assets 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); Utils::Stream::ClearPointer(&dest->projExplosionSound); } 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); Utils::Stream::ClearPointer(&dest->projDudSound); } @@ -476,7 +551,8 @@ namespace Assets 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); Utils::Stream::ClearPointer(&dest->projIgnitionSound); } @@ -551,7 +627,8 @@ namespace Assets 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); Utils::Stream::ClearPointer(&dest->turretOverheatSound); } @@ -569,7 +646,8 @@ namespace Assets 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); Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd); } @@ -577,7 +655,8 @@ namespace Assets for (int i = 0; i < 4; i++) { 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); Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]); } @@ -585,21 +664,24 @@ namespace Assets for (int i = 0; i < 4; i++) { 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); Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]); } 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); Utils::Stream::ClearPointer(&dest->missileConeSoundAlias); } 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); Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase); } diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index 3904ea74..6ccc39b0 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -1,6 +1,6 @@ #include "StdInclude.hpp" -#define IW4X_CLIPMAP_VERSION 1 +#define IW4X_CLIPMAP_VERSION 2 namespace Assets { @@ -587,12 +587,12 @@ namespace Assets Game::clipMap_t* orgClipMap = nullptr; Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, [](Game::XAssetHeader header, void* clipMap) - { - if (!*reinterpret_cast(clipMap)) { - *reinterpret_cast(clipMap) = header.clipMap; - } - }, &orgClipMap, false); + if (!*reinterpret_cast(clipMap)) + { + *reinterpret_cast(clipMap) = header.clipMap; + } + }, &orgClipMap, false); if (orgClipMap) std::memcpy(clipMap, orgClipMap, sizeof Game::clipMap_t); @@ -605,7 +605,7 @@ namespace Assets } int version = reader.read(); - 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); } @@ -881,33 +881,38 @@ namespace Assets clipMap->smodelNodeCount = reader.read(); clipMap->smodelNodes = reader.readArray(clipMap->smodelNodeCount); - clipMap->checksum = reader.read(); - 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 - /* - std::list slabs; - std::list hulls; - std::list models; + if (version >= 2) { + if (clipMap->numSubModels > 0) { + clipMap->mapEnts->trigger.count = clipMap->numSubModels; + clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels; - for (int i = 0; i < clipMap->numCModels; ++i) - { - Game::cLeafBrushNode_t* node = &clipMap->cLeafBrushNodes[clipMap->cModels[i].leaf.leafBrushNode]; - if (!node->leafBrushCount) continue; // skip empty brushes + Game::TriggerHull* hulls = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.hullCount); + Game::TriggerModel* models = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.count); - int baseHull = hulls.size(); - 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) + for (unsigned int i = 0; i < clipMap->numSubModels; ++i) { - Game::TriggerSlab curSlab; + models[i] = reader.read(); + hulls[i] = reader.read(); } + + size_t slabCount = reader.read(); + clipMap->mapEnts->trigger.slabCount = slabCount; + Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray(clipMap->mapEnts->trigger.slabCount); + for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) { + slabs[i] = reader.read(); + } + + + clipMap->mapEnts->trigger.models = &models[0]; + clipMap->mapEnts->trigger.hulls = &hulls[0]; + clipMap->mapEnts->trigger.slabs = &slabs[0]; } } - */ + + clipMap->checksum = reader.read(); // This mustn't be null and has to have at least 1 'valid' entry. if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 2bbc6a8e..f14e00b5 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -4,10 +4,10 @@ namespace Assets { 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()) { - header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound; + header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.c_str()).sound; return; } @@ -18,134 +18,330 @@ namespace Assets return; } - aliasList->head = builder->getAllocator()->allocate(); + 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(aliasList->count); if (!aliasList->head) { Components::Logger::Print("Error allocating memory for sound alias structure!\n"); return; } - aliasList->head->soundFile = builder->getAllocator()->allocate(); - 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"); - return; - } + json11::Json head = aliasesContainer[i]; - aliasList->count = 1; + if (!infoData.is_object()) + { + Components::Logger::Error("Failed to load sound %s!", name.c_str()); + return; + } - std::string errors; - json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors); + aliasList->head->soundFile = builder->getAllocator()->allocate(); + if (!aliasList->head->soundFile) + { + Components::Logger::Print("Error allocating memory for sound alias structure!\n"); + return; + } - if (!infoData.is_object()) - { - Components::Logger::Error("Failed to load sound %s!", name.data()); - return; - } + Game::snd_alias_t* alias = aliasList->head; - 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 - auto type = infoData["type"]; - auto subtitle = infoData["subtitle"]; - auto secondaryAliasName = infoData["secondaryAliasName"]; - 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"]; + // Fix casing + if (soundFile.is_null()) + { + soundFile = head["soundfile"]; - if (type.is_null() || soundFile.is_null()) - { - Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile", name.data()); - return; - } + Components::Logger::Print("Fixed casing on %s\n", name.c_str()); + } + + 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()) - // TODO: actually support all of those properties - if (CHECK(type, 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, string)) - { - - alias->soundFile->exists = true; - - if (subtitle.is_string()) + // TODO: actually support all of those properties + if (!CHECK(type, number)) { - alias->subtitle = subtitle.string_value().data(); - } - if (secondaryAliasName.is_string()) - { - alias->secondaryAliasName = secondaryAliasName.string_value().data(); - } - if (chainAliasName.is_string()) - { - alias->chainAliasName = chainAliasName.string_value().data(); + Components::Logger::Print("%s is not number but %d (%s)\n", "type", type.type(), type.dump().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()); - - if (volumeFalloffCurve.is_string()) + if (!CHECK(subtitle, 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; - alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd; + Components::Logger::Print("%s is not string but %d (%s)\n", "aliasName", aliasName.type(), aliasName.dump().c_str()); } - else if (type.string_value() == "streamed"s) + + if (!CHECK(secondaryAliasName, string)) { - alias->soundFile->type = Game::SAT_STREAMED; - 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()); - 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", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().c_str()); + } + + if (!CHECK(chainAliasName, string)) + { + 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(); + 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(speaker["levels0"].number_value()); + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = static_cast(speaker["levels1"].number_value()); + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = static_cast(speaker["numLevels"].number_value()); + alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = static_cast(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(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(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 { - 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 - { - Components::Logger::Error("Failed to parse sound %s!", name.data()); - return; - } + + header->sound = aliasList; #undef CHECK @@ -155,7 +351,7 @@ namespace Assets { 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]; @@ -166,7 +362,12 @@ namespace Assets 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(); 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* alias = &asset->head[i]; diff --git a/src/Components/Modules/Dvar.cpp b/src/Components/Modules/Dvar.cpp index 13a473b5..f342c658 100644 --- a/src/Components/Modules/Dvar.cpp +++ b/src/Components/Modules/Dvar.cpp @@ -225,6 +225,9 @@ namespace Components // set flags of cg_drawFPS to archive Utils::Hook::Or(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED); + // un-cheat camera_thirdPersonCrosshairOffset and add archive flags + Utils::Hook::Xor(0x447B41, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); + // un-cheat cg_fov and add archive flags Utils::Hook::Xor(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); diff --git a/src/Components/Modules/Localization.cpp b/src/Components/Modules/Localization.cpp index bc586f54..242c4cb5 100644 --- a/src/Components/Modules/Localization.cpp +++ b/src/Components/Modules/Localization.cpp @@ -170,10 +170,10 @@ namespace Components "/dev/full", "/dev/sdb", "/dev/sr0", - "/dev//dev/tty0", + "/dev/tty0", "/dev/urandom", "Snake", - "lsb_release -a", + "lsb_release -a" }; static const char* contributors[] = @@ -192,6 +192,7 @@ namespace Components "INeedGames", "Killera", "Lithium", + "Louvenarde", "OneFourOne", "quaK", "RaidMax", diff --git a/src/Components/Modules/MapDump.cpp b/src/Components/Modules/MapDump.cpp index 4f3053d5..1c4e94bf 100644 --- a/src/Components/Modules/MapDump.cpp +++ b/src/Components/Modules/MapDump.cpp @@ -112,6 +112,9 @@ namespace Components const unsigned int vertOffset = surface->tris.firstVertex + 1; 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); for (unsigned short j = 0; j < surface->tris.triCount; ++j) @@ -332,33 +335,31 @@ namespace Components 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('/'); - 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; + const auto hash = Game::R_HashString(type.data()); 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) { - Logger::Print("Failed to get color map for material: %s\n", material->info.name); - return; + return image; } // TODO: This is still wrong. @@ -383,12 +384,42 @@ namespace Components 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("Ka 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(Utils::String::VA("map_Ka textures/%s.png\n", image->name)); - this->material_.append(Utils::String::VA("map_Kd 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", 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() diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 44c3a2ec..daaac1bc 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -739,6 +739,22 @@ namespace Components Utils::Hook::Call(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(0x5050C0)(ent); + } + + int16 Maps::CM_TriggerModelBounds(int modelPointer, Game::Bounds* bounds) { +#ifdef DEBUG + Game::MapEnts* ents = *reinterpret_cast(0x1AA651C); // Use me for debugging +#endif + return Utils::Hook::Call(0x4416C0)(modelPointer, bounds); + } Maps::Maps() { @@ -778,6 +794,15 @@ namespace Components // disable turrets on CoD:OL 448+ maps for now Utils::Hook(0x5EE577, 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 #if !defined(DEBUG) || !defined(SORT_SMODELS) diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index cbc4a282..75f9df7a 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -120,5 +120,7 @@ namespace Components static void SetSpecularStub1(); static void SetSpecularStub2(); 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); }; } diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index b79bcdd0..4b0e78b3 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -946,17 +946,12 @@ namespace Components } }); - Dvar::OnInit([] - { - Dvar::Register("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees"); - }); - Scheduler::OnFrame([]() { if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get()) return; 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(0x7998E0); //Game::GfxWorld* gameWorld = *reinterpret_cast(0x66DEE94); @@ -973,6 +968,181 @@ namespace Components } }); + Dvar::OnInit([] + { + Dvar::Register("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes"); + Dvar::Register("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions"); + Dvar::Register("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers"); + Dvar::Register("r_drawModelNames", false, Game::DVAR_FLAG_CHEAT, "Draw all model names"); + Dvar::Register("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees"); + }); + + Scheduler::OnFrame([]() + { + if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawModelNames").get()) 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(); + 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()) 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()) 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()) 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 Dvar::Register("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI"); diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index c5379905..eae970ee 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -108,7 +108,7 @@ namespace Components 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); } diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 3cd8f3d3..644c6ebf 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -409,7 +409,7 @@ namespace Components { 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() diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 82477459..dd51c331 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -44,7 +44,7 @@ namespace Components 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); + 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; } diff --git a/src/Components/Modules/Zones.cpp b/src/Components/Modules/Zones.cpp index f47850ee..16c22bff 100644 --- a/src/Components/Modules/Zones.cpp +++ b/src/Components/Modules/Zones.cpp @@ -1655,18 +1655,48 @@ namespace Components image->delayLoadPixels = image359.loaded; image->name = image359.name; + FixImageCategory(image); + // Used for later stuff (&image->delayLoadPixels)[1] = image359.pad3[1]; } else { std::memcpy(buffer + 28, buffer + (size - 4), 4); + + Game::GfxImage* image = reinterpret_cast(buffer); + FixImageCategory(image); } } 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) { int count = 0; diff --git a/src/Components/Modules/Zones.hpp b/src/Components/Modules/Zones.hpp index f97fba5e..c38f4400 100644 --- a/src/Components/Modules/Zones.hpp +++ b/src/Components/Modules/Zones.hpp @@ -100,5 +100,6 @@ namespace Components static void LoadMaterialAsset(Game::Material** asset); static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size); static void LoadTracerDefFxEffect(); + static void FixImageCategory(Game::GfxImage* image); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index b5fdfd03..954f771a 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -425,6 +425,8 @@ namespace Game clientstate_t* clcState = reinterpret_cast(0xB2C540); + GfxScene* scene = reinterpret_cast(0x6944914); + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) { int elSize = DB_GetXAssetSizeHandlers[type](); @@ -734,6 +736,29 @@ namespace Game 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) { DWORD* specular1 = reinterpret_cast(0x69F105C); @@ -812,6 +837,73 @@ namespace Game 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) __declspec(naked) float UI_GetScoreboardLeft(void* /*a1*/) { diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 2cee14e3..a15cc01e 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -870,6 +870,8 @@ namespace Game extern clientstate_t* clcState; + extern GfxScene* scene; + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); void Menu_FreeItemMemory(Game::itemDef_s* item); const char* TableLookup(StringTable* stringtable, int row, int column); @@ -918,9 +920,12 @@ namespace Game void Vec3Normalize(vec3_t& vec); void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out); 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 R_AddDebugLine(float* color, float* v1, float* v2); 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, const float(*quat)[4]); } diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 37a6d3c2..3f1a30e7 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -98,6 +98,19 @@ namespace Game DVAR_FLAG_NONEXISTENT = 0xFFFFFFFF //no such dvar } 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 { DVAR_SOURCE_INTERNAL = 0x0, @@ -893,7 +906,7 @@ namespace Game struct MSSChannelMap { - int speakerCount; + unsigned int speakerCount; MSSSpeakerLevels speakers[6]; }; @@ -937,7 +950,7 @@ namespace Game { const char *aliasName; snd_alias_t *head; - int count; + unsigned int count; }; struct cStaticModel_s @@ -4461,18 +4474,148 @@ namespace Game char pad3[724]; } 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; - unsigned char pad[308]; // 4 - float origin[3]; // 312 - float angles[3]; // 324 - char pad2[8]; + int eType; + LerpEntityState lerp; + int time2; + 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 - 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 pad4; - unsigned char pad5[248]; + unsigned __int16 script_classname; + 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; #pragma pack(push, 1) @@ -4816,6 +4959,310 @@ namespace Game 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 #ifndef IDA diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index d6574ed3..07b3c6c0 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -5,8 +5,8 @@ #ifndef RC_INVOKED -#define _HAS_CXX17 1 -#define _HAS_CXX20 1 +//#define _HAS_CXX17 1 +//#define _HAS_CXX20 1 #define VC_EXTRALEAN #define WIN32_LEAN_AND_MEAN #define _CRT_SECURE_NO_WARNINGS @@ -38,6 +38,7 @@ #include #include #include +#include // Experimental C++17 features #include