/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Commando/shutdown.cpp $* * * * $Author:: Steve_t $* * * * $Modtime:: 10/16/02 11:11a $* * * * $Revision:: 104 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "shutdown.h" #include "wwmath.h" #include "wwsaveload.h" #include "ww3d.h" #include "WWAudio.H" #include "wwphys.h" #include "debug.h" #include "assets.h" #include "ffactorylist.h" #include "input.h" #include "inputconfigmgr.h" #include "gamemode.h" //#include "gamesettings.h" #include "miscutil.h" #include "refcount.h" #include "cnetwork.h" #include "_globals.h" #include "systemsettings.h" #include "diagnostics.h" #include "translatedb.h" #include "pathmgr.h" #include "renegadedialogmgr.h" #include "campaign.h" #include "diaglog.h" #include "binkmovie.h" //#include "helptext.h" #include "init.h" #include "serverfps.h" #include "encyclopediamgr.h" #include "playermanager.h" #include "teammanager.h" #include "bandwidthgraph.h" #include "except.h" #include "skinpackagemgr.h" #include "modpackagemgr.h" #include "dx8wrapper.h" #include "pscene.h" #include "systeminfolog.h" #include "cpudetect.h" #include "dx8caps.h" #include "registry.h" #include "specialbuilds.h" #include #include // UNLEN extern SimpleFileFactoryClass RenegadeBaseFileFactory; #define SYSTEM_INFO_LOG_DISABLE "SystemInfoLogDisable" // These are copy-pasted from WWConfig //extern const char *KEY_NAME_SETTINGS; extern const char *VALUE_NAME_DYN_LOD; extern const char *VALUE_NAME_STATIC_LOD; extern const char *VALUE_NAME_DYN_SHADOWS; const char *VALUE_NAME_PRELIT_MODE = "Prelit_Mode"; extern const char *VALUE_NAME_SHADOW_MODE; extern const char *VALUE_NAME_STATIC_SHADOWS; extern const char *VALUE_NAME_TEXTURE_RES; const char *VALUE_NAME_SURFACE_EFFECT = "Surface_Effect_Detail"; extern const char *VALUE_NAME_PARTICLE_DETAIL; const char *VALUE_NAME_TEXTURE_FILTER_MODE="Texture_Filter_Mode"; static void Get_Detail_String(StringClass& str) { str=""; RegistryClass registry (APPLICATION_SUB_KEY_NAME_SYSTEM_SETTINGS); if (registry.Is_Valid ()) { // // Read the values from the registry // int dynamic_lod = registry.Get_Int (VALUE_NAME_DYN_LOD, 3000); int static_lod = registry.Get_Int (VALUE_NAME_STATIC_LOD, 3000); int dynamic_shadows = registry.Get_Int (VALUE_NAME_DYN_SHADOWS, 1); int static_shadows = registry.Get_Int (VALUE_NAME_STATIC_SHADOWS, 1); int texture_filter = registry.Get_Int (VALUE_NAME_TEXTURE_FILTER_MODE, TextureClass::TEXTURE_FILTER_BILINEAR); int prelit_mode = registry.Get_Int (VALUE_NAME_PRELIT_MODE, WW3D::PRELIT_MODE_LIGHTMAP_MULTI_TEXTURE); int shadow_mode = registry.Get_Int (VALUE_NAME_SHADOW_MODE, PhysicsSceneClass::SHADOW_MODE_BLOBS_PLUS); int texture_red = registry.Get_Int (VALUE_NAME_TEXTURE_RES, 0); int surface_effect = registry.Get_Int (VALUE_NAME_SURFACE_EFFECT, 1); int particle_detail = registry.Get_Int (VALUE_NAME_PARTICLE_DETAIL, 1); StringClass tmp; tmp.Format("Dynamic LOD budget: %d\r\n",dynamic_lod); str+=tmp; tmp.Format("Static LOD budget: %d\r\n",static_lod); str+=tmp; str+="Shadow Mode: "; switch (shadow_mode) { case PhysicsSceneClass::SHADOW_MODE_NONE: str+="None\r\n"; break; case PhysicsSceneClass::SHADOW_MODE_BLOBS: str+="Blobs\r\n"; break; case PhysicsSceneClass::SHADOW_MODE_BLOBS_PLUS: str+="Blobs Plus\r\n"; break; case PhysicsSceneClass::SHADOW_MODE_HARDWARE: str+="Hardware\r\n"; break; default: str+="???\r\n"; break; } tmp.Format("Dynamic Shadows: %s\r\n",dynamic_shadows ? "On" : "Off"); str+=tmp; tmp.Format("Static Shadows: %s\r\n",static_shadows ? "On" : "Off"); str+=tmp; str+="Prelit Mode: "; switch (prelit_mode) { case WW3D::PRELIT_MODE_VERTEX: str+="Vertex\r\n"; break; case WW3D::PRELIT_MODE_LIGHTMAP_MULTI_PASS: str+="Multipass\r\n"; break; case WW3D::PRELIT_MODE_LIGHTMAP_MULTI_TEXTURE: str+="Multitexture\r\n"; break; default: str+="???\r\n"; break; } tmp.Format("Texture Resolution: %d\r\n",texture_red); str+=tmp; tmp.Format("Surface Effects (0-2): %d\r\n",surface_effect); str+=tmp; tmp.Format("Particle Detail(0-2): %d\r\n",particle_detail); str+=tmp; str+="Texture Filter Mode: "; switch (texture_filter) { case TextureClass::TEXTURE_FILTER_BILINEAR: str+="Bilinear\r\n"; break; case TextureClass::TEXTURE_FILTER_TRILINEAR: str+="Trilinear\r\n"; break; case TextureClass::TEXTURE_FILTER_ANISOTROPIC: str+="Anisotropic\r\n"; break; default: str+="???\r\n"; break; } tmp.Format("Screen UV Bias: %s\r\n",WW3D::Is_Screen_UV_Biased() ? "Enabled" : "Disabled"); str+=tmp; // NPatch level str+="NPatch level: "; if (DX8Wrapper::Get_Current_Caps() && DX8Wrapper::Get_Current_Caps()->Support_NPatches()) { if (WW3D::Get_NPatches_Level()<=1) { str+="Disabled\r\n"; } else { tmp.Format("%d\r\n",WW3D::Get_NPatches_Level()); str+=tmp; } } else { str+="Not supported\r\n"; } int w; int h; int bits; bool windowed; WW3D::Get_Device_Resolution(w,h,bits,windowed); tmp.Format("Display mode: %d * %d, %d bits %s\r\n", w,h,bits,windowed ? "Windowed" : "Fullscreen"); str+=tmp; str+="\r\n"; const char *VALUE_NAME_SOUND_DEVICE_NAME = "device name"; RegistryClass registry_sound( APPLICATION_SUB_KEY_NAME_SOUND ); if ( registry_sound.Is_Valid() ) { char temp_buffer[256] = { 0 }; registry.Get_String (VALUE_NAME_SOUND_DEVICE_NAME, temp_buffer, sizeof (temp_buffer)); tmp.Format("Sound device: %s\r\n",temp_buffer); str+=tmp; } WWAudioClass* audio=WWAudioClass::Get_Instance(); if (audio) { tmp.Format("Sound effects: %s\r\n",audio->Are_Sound_Effects_On() ? "Enabled" : "Disabled"); str+=tmp; tmp.Format("Sound effects volume: %2.2f\r\n",audio->Get_Sound_Effects_Volume()); str+=tmp; tmp.Format("Music: %s\r\n",audio->Is_Music_On() ? "Enabled" : "Disabled"); str+=tmp; tmp.Format("Music volume: %2.2f\r\n",audio->Get_Music_Volume()); str+=tmp; } } } void Get_Compact_Detail_String(StringClass& str) { str=""; RegistryClass registry (APPLICATION_SUB_KEY_NAME_SYSTEM_SETTINGS); if (registry.Is_Valid ()) { // // Read the values from the registry // int dynamic_lod = registry.Get_Int (VALUE_NAME_DYN_LOD, 3000); int static_lod = registry.Get_Int (VALUE_NAME_STATIC_LOD, 3000); int dynamic_shadows = registry.Get_Int (VALUE_NAME_DYN_SHADOWS, 1); int static_shadows = registry.Get_Int (VALUE_NAME_STATIC_SHADOWS, 1); int texture_filter = registry.Get_Int (VALUE_NAME_TEXTURE_FILTER_MODE, TextureClass::TEXTURE_FILTER_BILINEAR); int prelit_mode = registry.Get_Int (VALUE_NAME_PRELIT_MODE, WW3D::PRELIT_MODE_LIGHTMAP_MULTI_TEXTURE); int shadow_mode = registry.Get_Int (VALUE_NAME_SHADOW_MODE, PhysicsSceneClass::SHADOW_MODE_BLOBS_PLUS); int texture_red = registry.Get_Int (VALUE_NAME_TEXTURE_RES, 0); int surface_effect = registry.Get_Int (VALUE_NAME_SURFACE_EFFECT, 1); int particle_detail = registry.Get_Int (VALUE_NAME_PARTICLE_DETAIL, 1); StringClass tmp; tmp.Format("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",dynamic_lod,static_lod,shadow_mode,dynamic_shadows,static_shadows,prelit_mode,texture_red,surface_effect,particle_detail,texture_filter); str+=tmp; tmp.Format("%d\t",1);//WW3D::Get_Texture_Compression_Mode()); str+=tmp; int w; int h; int bits; bool windowed; WW3D::Get_Device_Resolution(w,h,bits,windowed); tmp.Format("%d\t%d\t%d\t%d\t",w,h,bits,windowed); str+=tmp; } } static class SysInfoCopyThreadClass : public ThreadClass { public: StringClass String; StringClass Filename; SysInfoCopyThreadClass() : ThreadClass("SysInfoCopyThread", &Exception_Handler) {} void Thread_Function() { DWORD written; HANDLE file = CreateFile(Filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != file) { WriteFile(file, String, strlen(String), &written, NULL); CloseHandle(file); } } } SysInfoCopyThread; // For debug purposes, log system information to \\Mordane\marketin\transfer\users\Jani\SYSINFO static void Log_System_Information() { if (!DX8Wrapper::Is_Initted()) { return; } if (DX8Wrapper::Get_Current_Caps() == NULL) { return; } char name[MAX_COMPUTERNAME_LENGTH + 1]; DWORD size = sizeof(name); ::GetComputerName(name, &size); char user[UNLEN+1]; DWORD userlen=sizeof(user); ::GetUserName(user, &userlen); StringClass string; // This will be a long string so don't allocate locally! string.Format("Computer name: %s\r\nUser name: %s\r\n\r\n",name,user); string+=CPUDetectClass::Get_Processor_Log(); if (DX8Wrapper::Get_Current_Caps()) { string+=DX8Wrapper::Get_Current_Caps()->Get_Log(); } string+="\r\n"; string+="Compact tab-delimited version:\r\n"; StringClass tmp; // This will be long so no local alloc needed string+=CPUDetectClass::Get_Compact_Log(); if (DX8Wrapper::Get_Current_Caps()) { string+=DX8Wrapper::Get_Current_Caps()->Get_Compact_Log(); } Get_Compact_Detail_String(tmp); string+=tmp; SystemInfoLog::Get_Compact_Log(tmp); string+=tmp; string+="\r\n"; Get_Detail_String(tmp); string+=tmp; string+="\r\n"; SystemInfoLog::Get_Log(tmp); string+=tmp; // Write log to network folder DWORD written; HANDLE file; #ifdef WWDEBUG RegistryClass registry( APPLICATION_SUB_KEY_NAME_DEBUG ); if ( registry.Is_Valid() ) { int disable=registry.Get_Int( SYSTEM_INFO_LOG_DISABLE ); if (!disable) { if (!SysInfoCopyThread.Is_Running()) { StringClass filename(0,true); // filename="\\\\havoc\\rock\\projects\\renegade\\logs\\"; filename="\\\\tanya\\game\\Projects\\Renegade\\_sysinfo_logs\\"; tmp.Format("%d_%d_",DX8Wrapper::Get_Current_Caps()->Get_Vendor(),DX8Wrapper::Get_Current_Caps()->Get_Device()); filename+=tmp; filename+=name; filename+=".txt"; SysInfoCopyThread.String=string; SysInfoCopyThread.Filename=filename; SysInfoCopyThread.Execute(); } } } #endif // Write log to local work folder file = CreateFile("sysinfo.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (INVALID_HANDLE_VALUE != file) { WriteFile(file, string, strlen(string), &written, NULL); CloseHandle(file); } } /* ** */ void Debug_Refs(void); /* ** */ void Game_Shutdown(void) { Log_System_Information(); BINKMovie::Shutdown(); CampaignManager::Shutdown(); SystemSettings::Shutdown(); CombatManager::Shutdown(); //Analyze_Soldier_Bandwidth(); cNetwork::Onetime_Shutdown(); cServerFps::Destroy_Instance(); cPlayerManager::Onetime_Shutdown(); cTeamManager::Onetime_Shutdown(); cGameData::Onetime_Shutdown(); cBandwidthGraph::Onetime_Shutdown(); DebugManager::Set_Display_Handler( NULL ); GameModeManager::Destroy_All(); EncyclopediaMgrClass::Shutdown(); RenegadeDialogMgrClass::Shutdown(); // Free the sound library if (WWAudioClass::Get_Instance () != NULL) { //WWAudioClass::Get_Instance ()->Shutdown (); delete WWAudioClass::Get_Instance (); } //GameSettings::Shutdown(); SkinPackageMgrClass::Shutdown (); ModPackageMgrClass::Shutdown (); TranslateDBClass::Shutdown(); // // Shutdown the input control system // InputConfigMgrClass::Shutdown(); Input::Save_Registry( APPLICATION_SUB_KEY_NAME_CONTROLS ); Input::Shutdown(); DiagLogClass::Shutdown(); // CommandoAssetManager::Shutdown(); WW3D::_Invalidate_Textures(); WWASSERT( WW3DAssetManager::Get_Instance() ); WW3DAssetManager::Delete_This(); // if ( WW3DAssetManager::Get_Instance() ) { // delete WW3DAssetManager::Get_Instance(); // } cDiagnostics::Close(); //cHelpText::Close(); PhysicsSceneClass * scene = PhysicsSceneClass::Get_Instance(); scene->Set_Max_Simultaneous_Shadows(0); PathMgrClass::Shutdown(); WWMath::Shutdown(); WWSaveLoad::Shutdown(); WW3D::Shutdown(); WWPhys::Shutdown(); // WW3DAssetManager::Get_Instance()->Free_Assets(); Debug_Refs(); DebugManager::Save_Registry_Settings( APPLICATION_SUB_KEY_NAME_DEBUG ); DebugManager::Shutdown(); WSA_CHECK(WSACleanup()); /* ** Remove any old file factories still lying around. */ if (FileFactoryListClass::Get_Instance() != NULL) { FileFactoryListClass::Get_Instance()->Remove_FileFactory(&RenegadeBaseFileFactory); } while (FileFactoryListClass::Get_Instance() != NULL) { FileFactoryClass * factory = FileFactoryListClass::Get_Instance()->Remove_FileFactory(); if (factory != NULL) { WWDEBUG_SAY(("Removing pesky old file factory\n")); delete factory; } else { break; } } RegistryClass registry( APPLICATION_SUB_KEY_NAME_DEBUG ); if ( registry.Is_Valid() ) { registry.Set_Int( VALUE_NAME_APPLICATION_CRASH_VERSION, 0 ); } #ifdef FREEDEDICATEDSERVER Copy_Logs(DebugManager::Get_Version_Number()); #endif //FREEDEDICATEDSERVER return ; } /* ** */ void Debug_Refs(void) { #ifndef NDEBUG // Debug_Say(("Detecting Active Refs...\r\n")); RefCountNodeClass * first = RefCountClass::ActiveRefList.First(); RefCountNodeClass * node = first; while (node->Is_Valid()) { RefCountClass * obj = node->Get(); ActiveRefStruct * ref = &(obj->ActiveRefInfo); bool display = true; int count = 0; RefCountNodeClass * search = first; while (search->Is_Valid()) { if (search == node) { // if this is not the first one if (count != 0) { display = false; break; } } RefCountClass * search_obj = search->Get(); ActiveRefStruct * search_ref = &(search_obj->ActiveRefInfo); if ( ref->File && search_ref->File && !strcmp(search_ref->File, ref->File) && (search_ref->Line == ref->Line) ) { count++; } else if ( (ref->File == NULL) && (search_ref->File == NULL) ) { count++; } search = search->Next(); } if ( display ) { Debug_Say(( "%d Active Ref: %s %d %p\n", count, ref->File,ref->Line,obj)); static int num_printed = 0; if (++num_printed > 20) { Debug_Say(( "And Many More......\n")); break; } } node = node->Next(); } // Debug_Say(("Done.\r\n")); #endif }