From 00764c0ecd63ffa18069f4ba4b9d6a00f0afe4f3 Mon Sep 17 00:00:00 2001 From: SaiyansKing <38609240+SaiyansKing@users.noreply.github.com> Date: Thu, 4 Nov 2021 01:56:58 +0100 Subject: [PATCH] Fix crash when deleting visuals that can still be in use --- D3D11Engine/GothicAPI.cpp | 48 ++++++++++++++++++++++----------------- D3D11Engine/zCModel.h | 11 +++++++++ 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/D3D11Engine/GothicAPI.cpp b/D3D11Engine/GothicAPI.cpp index f6a1bc67..318d4564 100644 --- a/D3D11Engine/GothicAPI.cpp +++ b/D3D11Engine/GothicAPI.cpp @@ -1227,34 +1227,45 @@ void GothicAPI::OnVisualDeleted( zCVisual* visual ) { StaticMeshVisuals.erase( pm ); } - if ( ((zCModel*)visual)->GetMeshSoftSkinList()->NumInArray ) { - // Find vobs using this visual - for ( std::list::iterator it = SkeletalMeshVobs.begin(); it != SkeletalMeshVobs.end(); ++it ) { - if ( (*it)->VisualInfo && ((zCModel*)(*it)->VisualInfo->Visual)->GetMeshSoftSkinList()->Array[0] == ((zCModel*)visual)->GetMeshSoftSkinList()->Array[0] ) { - (*it)->VisualInfo = nullptr; + zCModel* zmodel = (zCModel*)visual; + if ( zmodel->GetMainPrototypeReferences() <= 1 ) { // Check if it is the last reference in prototype so that we can delete this visual + std::string str = zmodel->GetVisualName(); + if ( str.empty() ) { // Happens when the model has no skeletal-mesh + zSTRING mds = zmodel->GetModelName(); + str = mds.ToChar(); + mds.Delete(); + } + + auto it = SkeletalMeshVisuals.find( str ); + if ( it != SkeletalMeshVisuals.end() ) { + // Find vobs using this visual + for ( std::list::iterator sit = SkeletalMeshVobs.begin(); sit != SkeletalMeshVobs.end(); ++sit ) { + if ( (*sit)->VisualInfo == it->second ) { + (*sit)->VisualInfo = nullptr; + } } } - } - std::string str = ((zCModel*)visual)->GetVisualName(); - if ( str.empty() ) { // Happens when the model has no skeletal-mesh - zSTRING mds = ((zCModel*)visual)->GetModelName(); - str = mds.ToChar(); - mds.Delete(); + delete SkeletalMeshVisuals[str]; + SkeletalMeshVisuals.erase( str ); } - - delete SkeletalMeshVisuals[str]; - SkeletalMeshVisuals.erase( str ); break; } } - // Add to map - std::list list = VobsByVisual[visual]; + // Clear if ( _canClearVobsByVisual ) { + std::list& list = VobsByVisual[visual]; for ( auto const& it : list ) { OnRemovedVob( it->Vob, LoadedWorldInfo->MainWorld ); } + if ( list.size() > 0 ) { + if ( RendererState.RendererSettings.EnableDebugLog ) + LogInfo() << std::string( className ) << " had " + std::to_string( list.size() ) << " vobs"; + + list.clear(); + VobsByVisual.erase( visual ); + } } else { // TODO: #8 - Figure out why exactly we don't get notified that a VOB is re-added after being removed. /*oCNPC* npcVob; @@ -1267,11 +1278,6 @@ void GothicAPI::OnVisualDeleted( zCVisual* visual ) { } }*/ } - if ( list.size() > 0 ) { - if ( RendererState.RendererSettings.EnableDebugLog ) - LogInfo() << std::string( className ) << " had " + std::to_string( list.size() ) << " vobs"; - VobsByVisual[visual].clear(); - } } /** Draws a MeshInfo */ void GothicAPI::DrawMeshInfo( zCMaterial* mat, MeshInfo* msh ) { diff --git a/D3D11Engine/zCModel.h b/D3D11Engine/zCModel.h index 6b750a30..a122b3da 100644 --- a/D3D11Engine/zCModel.h +++ b/D3D11Engine/zCModel.h @@ -268,6 +268,17 @@ class zCModel : public zCVisual { #endif } + int GetMainPrototypeReferences() { + zCArray* prototypes = GetModelProtoList(); + if ( prototypes->NumInArray > 0 ) { + zCModelPrototype* prototype = prototypes->Array[0]; + if ( prototype ) { + return *reinterpret_cast(reinterpret_cast(prototype) + 0x08); // Get reference counter + } + } + return 2; // Allow leakage because it is only container for 3DS models(mob) - better than crash + } + zCArray* GetNodeList() { return (zCArray*)THISPTR_OFFSET( GothicMemoryLocations::zCModel::Offset_NodeList ); }