From a1e88017b5bb6cc7a6d62d9308f7251c880bf844 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sun, 22 Dec 2019 20:28:16 -0800 Subject: [PATCH 01/66] Port over to FO4 F4SE 0.6.20 from SSE (SKSE), issues are expected. --- CBPSSE.sln | 32 +- CBPSSE/CBPSSE.vcxproj | 57 +- CBPSSE/CBPSSE.vcxproj.filters | 54 +- CBPSSE/SimObj.cpp | 41 +- CBPSSE/SimObj.h | 2 +- CBPSSE/Thing.cpp | 172 +- CBPSSE/Thing.h | 12 +- CBPSSE/config.cpp | 4 +- CBPSSE/exports.def | 4 +- CBPSSE/hookD3D.cpp | 8 +- CBPSSE/log.cpp | 2 +- CBPSSE/main.cpp | 72 +- CBPSSE/scan.cpp | 73 +- common/IArchive.cpp | 102 + common/IArchive.h | 95 + common/IBufferStream.cpp | 58 + common/IBufferStream.h | 35 + common/IConsole.cpp | 116 + common/IConsole.h | 26 + common/ICriticalSection.h | 35 + common/IDataStream.cpp | 472 +++ common/IDataStream.h | 102 + common/IDatabase.cpp | 1 + common/IDatabase.h | 116 + common/IDatabase.inc | 55 + common/IDebugLog.cpp | 324 ++ common/IDebugLog.h | 133 + common/IDirectoryIterator.cpp | 45 + common/IDirectoryIterator.h | 24 + common/IDynamicCreate.cpp | 38 + common/IDynamicCreate.h | 118 + common/IErrors.cpp | 59 + common/IErrors.h | 32 + common/IEvent.cpp | 48 + common/IEvent.h | 22 + common/IFIFO.cpp | 85 + common/IFIFO.h | 27 + common/IFileStream.cpp | 240 ++ common/IFileStream.h | 37 + common/IInterlockedLong.cpp | 3 + common/IInterlockedLong.h | 19 + common/ILinkedList.h | 91 + common/IMemPool.cpp | 43 + common/IMemPool.h | 312 ++ common/IMutex.cpp | 34 + common/IMutex.h | 16 + common/IPipeClient.cpp | 60 + common/IPipeClient.h | 20 + common/IPipeServer.cpp | 74 + common/IPipeServer.h | 25 + common/IPrefix.cpp | 1 + common/IPrefix.h | 26 + common/IRangeMap.cpp | 1 + common/IRangeMap.h | 215 + common/IReadWriteLock.cpp | 43 + common/IReadWriteLock.h | 24 + common/ISegmentStream.cpp | 76 + common/ISegmentStream.h | 44 + common/ISingleton.cpp | 3 + common/ISingleton.h | 53 + common/ITextParser.cpp | 83 + common/ITextParser.h | 25 + common/IThread.cpp | 65 + common/IThread.h | 32 + common/ITimer.cpp | 133 + common/ITimer.h | 38 + common/ITypes.cpp | 66 + common/ITypes.h | 344 ++ common/common.vcproj | 419 ++ common/common.vcxproj | 154 + common/common.vcxproj.filters | 196 + common/common_license.txt | 22 + common/common_vc11.sln | 24 + common/common_vc11.vcxproj | 183 + common/common_vc11.vcxproj.filters | 107 + common/common_vc14.sln | 28 + common/common_vc14.vcxproj | 130 + common/common_vc14.vcxproj.filters | 104 + common/common_vc9.vcproj | 420 ++ f4se/f4se/BSCollision.cpp | 1 + f4se/f4se/BSCollision.h | 105 + f4se/f4se/BSGeometry.cpp | 3 + f4se/f4se/BSGeometry.h | 241 ++ f4se/f4se/BSGraphics.cpp | 19 + f4se/f4se/BSGraphics.h | 82 + f4se/f4se/BSLight.cpp | 1 + f4se/f4se/BSLight.h | 83 + f4se/f4se/BSModelDB.cpp | 4 + f4se/f4se/BSModelDB.h | 50 + f4se/f4se/BSParticleShaderEmitter.cpp | 1 + f4se/f4se/BSParticleShaderEmitter.h | 67 + f4se/f4se/BSSkin.cpp | 1 + f4se/f4se/BSSkin.h | 44 + f4se/f4se/CustomMenu.cpp | 108 + f4se/f4se/CustomMenu.h | 36 + f4se/f4se/GameAPI.cpp | 41 + f4se/f4se/GameAPI.h | 31 + f4se/f4se/GameCamera.cpp | 14 + f4se/f4se/GameCamera.h | 139 + f4se/f4se/GameCustomization.cpp | 170 + f4se/f4se/GameCustomization.h | 476 +++ f4se/f4se/GameData.cpp | 95 + f4se/f4se/GameData.h | 421 ++ f4se/f4se/GameEvents.cpp | 7 + f4se/f4se/GameEvents.h | 241 ++ f4se/f4se/GameExtraData.cpp | 160 + f4se/f4se/GameExtraData.h | 416 ++ f4se/f4se/GameFormComponents.cpp | 30 + f4se/f4se/GameFormComponents.h | 1543 +++++++ f4se/f4se/GameForms.cpp | 10 + f4se/f4se/GameForms.h | 1921 +++++++++ f4se/f4se/GameInput.cpp | 87 + f4se/f4se/GameInput.h | 391 ++ f4se/f4se/GameMemory.cpp | 1 + f4se/f4se/GameMemory.h | 135 + f4se/f4se/GameMenus.cpp | 135 + f4se/f4se/GameMenus.h | 448 ++ f4se/f4se/GameMessages.cpp | 4 + f4se/f4se/GameMessages.h | 52 + f4se/f4se/GameObjects.cpp | 61 + f4se/f4se/GameObjects.h | 803 ++++ f4se/f4se/GameRTTI.cpp | 17 + f4se/f4se/GameRTTI.h | 3981 ++++++++++++++++++ f4se/f4se/GameRTTI.inl | 3975 +++++++++++++++++ f4se/f4se/GameReferences.cpp | 85 + f4se/f4se/GameReferences.h | 521 +++ f4se/f4se/GameSettings.cpp | 137 + f4se/f4se/GameSettings.h | 141 + f4se/f4se/GameStreams.cpp | 79 + f4se/f4se/GameStreams.h | 68 + f4se/f4se/GameThreads.cpp | 1 + f4se/f4se/GameThreads.h | 53 + f4se/f4se/GameTypes.cpp | 232 + f4se/f4se/GameTypes.h | 1280 ++++++ f4se/f4se/GameUtilities.cpp | 4 + f4se/f4se/GameUtilities.h | 9 + f4se/f4se/GameWorkshop.cpp | 11 + f4se/f4se/GameWorkshop.h | 40 + f4se/f4se/Hooks_Camera.cpp | 64 + f4se/f4se/Hooks_Camera.h | 4 + f4se/f4se/Hooks_Debug.cpp | 141 + f4se/f4se/Hooks_Debug.h | 4 + f4se/f4se/Hooks_GameData.cpp | 45 + f4se/f4se/Hooks_GameData.h | 4 + f4se/f4se/Hooks_Gameplay.cpp | 92 + f4se/f4se/Hooks_Gameplay.h | 4 + f4se/f4se/Hooks_Input.cpp | 281 ++ f4se/f4se/Hooks_Input.h | 4 + f4se/f4se/Hooks_Memory.cpp | 100 + f4se/f4se/Hooks_Memory.h | 6 + f4se/f4se/Hooks_ObScript.cpp | 54 + f4se/f4se/Hooks_ObScript.h | 4 + f4se/f4se/Hooks_Papyrus.cpp | 338 ++ f4se/f4se/Hooks_Papyrus.h | 9 + f4se/f4se/Hooks_SaveLoad.cpp | 164 + f4se/f4se/Hooks_SaveLoad.h | 4 + f4se/f4se/Hooks_Scaleform.cpp | 696 +++ f4se/f4se/Hooks_Scaleform.h | 9 + f4se/f4se/Hooks_Threads.cpp | 103 + f4se/f4se/Hooks_Threads.h | 12 + f4se/f4se/InputMap.cpp | 50 + f4se/f4se/InputMap.h | 47 + f4se/f4se/InternalSerialization.cpp | 326 ++ f4se/f4se/InternalSerialization.h | 4 + f4se/f4se/NiCloningProcess.h | 19 + f4se/f4se/NiExtraData.cpp | 74 + f4se/f4se/NiExtraData.h | 141 + f4se/f4se/NiMaterials.cpp | 6 + f4se/f4se/NiMaterials.h | 525 +++ f4se/f4se/NiNodes.cpp | 8 + f4se/f4se/NiNodes.h | 78 + f4se/f4se/NiObjects.cpp | 41 + f4se/f4se/NiObjects.h | 224 + f4se/f4se/NiProperties.cpp | 15 + f4se/f4se/NiProperties.h | 197 + f4se/f4se/NiRTTI.cpp | 22 + f4se/f4se/NiRTTI.h | 22 + f4se/f4se/NiSerialization.cpp | 1 + f4se/f4se/NiSerialization.h | 44 + f4se/f4se/NiTextures.cpp | 7 + f4se/f4se/NiTextures.h | 94 + f4se/f4se/NiTypes.cpp | 192 + f4se/f4se/NiTypes.h | 413 ++ f4se/f4se/ObScript.cpp | 6 + f4se/f4se/ObScript.h | 50 + f4se/f4se/PapyrusActor.cpp | 140 + f4se/f4se/PapyrusActor.h | 11 + f4se/f4se/PapyrusActorBase.cpp | 104 + f4se/f4se/PapyrusActorBase.h | 10 + f4se/f4se/PapyrusArgs.cpp | 490 +++ f4se/f4se/PapyrusArgs.h | 523 +++ f4se/f4se/PapyrusArmor.cpp | 31 + f4se/f4se/PapyrusArmor.h | 8 + f4se/f4se/PapyrusArmorAddon.cpp | 33 + f4se/f4se/PapyrusArmorAddon.h | 8 + f4se/f4se/PapyrusCell.cpp | 39 + f4se/f4se/PapyrusCell.h | 9 + f4se/f4se/PapyrusComponent.cpp | 51 + f4se/f4se/PapyrusComponent.h | 9 + f4se/f4se/PapyrusConstructibleObject.cpp | 142 + f4se/f4se/PapyrusConstructibleObject.h | 9 + f4se/f4se/PapyrusDefaultObject.cpp | 40 + f4se/f4se/PapyrusDefaultObject.h | 10 + f4se/f4se/PapyrusDelayFunctors.cpp | 569 +++ f4se/f4se/PapyrusDelayFunctors.h | 205 + f4se/f4se/PapyrusDelayFunctorsDef.inl | 9 + f4se/f4se/PapyrusDelayFunctorsDef_Base.inl | 444 ++ f4se/f4se/PapyrusEncounterZone.cpp | 127 + f4se/f4se/PapyrusEncounterZone.h | 8 + f4se/f4se/PapyrusEquipSlot.cpp | 34 + f4se/f4se/PapyrusEquipSlot.h | 8 + f4se/f4se/PapyrusEvents.cpp | 37 + f4se/f4se/PapyrusEvents.h | 604 +++ f4se/f4se/PapyrusEventsDef.inl | 13 + f4se/f4se/PapyrusEventsDef_Base.inl | 150 + f4se/f4se/PapyrusF4SE.cpp | 79 + f4se/f4se/PapyrusF4SE.h | 17 + f4se/f4se/PapyrusFavoritesManager.cpp | 172 + f4se/f4se/PapyrusFavoritesManager.h | 11 + f4se/f4se/PapyrusForm.cpp | 363 ++ f4se/f4se/PapyrusForm.h | 11 + f4se/f4se/PapyrusGame.cpp | 221 + f4se/f4se/PapyrusGame.h | 11 + f4se/f4se/PapyrusHeadPart.cpp | 76 + f4se/f4se/PapyrusHeadPart.h | 13 + f4se/f4se/PapyrusInput.cpp | 107 + f4se/f4se/PapyrusInput.h | 9 + f4se/f4se/PapyrusInstanceData.cpp | 844 ++++ f4se/f4se/PapyrusInstanceData.h | 9 + f4se/f4se/PapyrusInterfaces.cpp | 21 + f4se/f4se/PapyrusInterfaces.h | 230 + f4se/f4se/PapyrusLocation.cpp | 57 + f4se/f4se/PapyrusLocation.h | 8 + f4se/f4se/PapyrusMaterialSwap.cpp | 64 + f4se/f4se/PapyrusMaterialSwap.h | 9 + f4se/f4se/PapyrusMath.cpp | 85 + f4se/f4se/PapyrusMath.h | 9 + f4se/f4se/PapyrusMiscObject.cpp | 72 + f4se/f4se/PapyrusMiscObject.h | 9 + f4se/f4se/PapyrusNativeFunctionDef.inl | 27 + f4se/f4se/PapyrusNativeFunctionDef_Base.inl | 308 ++ f4se/f4se/PapyrusNativeFunctions.cpp | 1 + f4se/f4se/PapyrusNativeFunctions.h | 189 + f4se/f4se/PapyrusObjectMod.cpp | 179 + f4se/f4se/PapyrusObjectMod.h | 9 + f4se/f4se/PapyrusObjectReference.cpp | 750 ++++ f4se/f4se/PapyrusObjectReference.h | 10 + f4se/f4se/PapyrusObjects.cpp | 334 ++ f4se/f4se/PapyrusObjects.h | 170 + f4se/f4se/PapyrusPerk.cpp | 78 + f4se/f4se/PapyrusPerk.h | 9 + f4se/f4se/PapyrusScaleformAdapter.cpp | 283 ++ f4se/f4se/PapyrusScaleformAdapter.h | 15 + f4se/f4se/PapyrusScriptObject.cpp | 263 ++ f4se/f4se/PapyrusScriptObject.h | 9 + f4se/f4se/PapyrusSerialization.cpp | 522 +++ f4se/f4se/PapyrusSerialization.h | 13 + f4se/f4se/PapyrusStruct.cpp | 23 + f4se/f4se/PapyrusStruct.h | 197 + f4se/f4se/PapyrusUI.cpp | 372 ++ f4se/f4se/PapyrusUI.h | 9 + f4se/f4se/PapyrusUtilities.cpp | 22 + f4se/f4se/PapyrusUtilities.h | 7 + f4se/f4se/PapyrusUtility.cpp | 33 + f4se/f4se/PapyrusUtility.h | 9 + f4se/f4se/PapyrusVM.cpp | 10 + f4se/f4se/PapyrusVM.h | 302 ++ f4se/f4se/PapyrusValue.cpp | 297 ++ f4se/f4se/PapyrusValue.h | 260 ++ f4se/f4se/PapyrusWaterType.cpp | 53 + f4se/f4se/PapyrusWaterType.h | 9 + f4se/f4se/PapyrusWeapon.cpp | 33 + f4se/f4se/PapyrusWeapon.h | 9 + f4se/f4se/PluginAPI.h | 278 ++ f4se/f4se/PluginManager.cpp | 604 +++ f4se/f4se/PluginManager.h | 66 + f4se/f4se/ScaleformAPI.cpp | 14 + f4se/f4se/ScaleformAPI.h | 26 + f4se/f4se/ScaleformCallbacks.cpp | 15 + f4se/f4se/ScaleformCallbacks.h | 87 + f4se/f4se/ScaleformLoader.cpp | 4 + f4se/f4se/ScaleformLoader.h | 49 + f4se/f4se/ScaleformMovie.cpp | 1 + f4se/f4se/ScaleformMovie.h | 92 + f4se/f4se/ScaleformSerialization.cpp | 81 + f4se/f4se/ScaleformSerialization.h | 15 + f4se/f4se/ScaleformState.cpp | 6 + f4se/f4se/ScaleformState.h | 127 + f4se/f4se/ScaleformTranslator.cpp | 6 + f4se/f4se/ScaleformTranslator.h | 59 + f4se/f4se/ScaleformTypes.cpp | 1 + f4se/f4se/ScaleformTypes.h | 56 + f4se/f4se/ScaleformValue.cpp | 270 ++ f4se/f4se/ScaleformValue.h | 381 ++ f4se/f4se/Serialization.cpp | 634 +++ f4se/f4se/Serialization.h | 90 + f4se/f4se/Translation.cpp | 120 + f4se/f4se/Translation.h | 9 + f4se/f4se/bhkWorld.cpp | 1 + f4se/f4se/bhkWorld.h | 117 + f4se/f4se/exports.def | 2 + f4se/f4se/f4se.cpp | 138 + f4se/f4se/f4se.vcxproj | 353 ++ f4se/f4se/f4se.vcxproj.filters | 706 ++++ f4se/f4se_common/BranchTrampoline.cpp | 254 ++ f4se/f4se_common/BranchTrampoline.h | 43 + f4se/f4se_common/Relocation.cpp | 30 + f4se/f4se_common/Relocation.h | 89 + f4se/f4se_common/SafeWrite.cpp | 66 + f4se/f4se_common/SafeWrite.h | 13 + f4se/f4se_common/Utilities.cpp | 255 ++ f4se/f4se_common/Utilities.h | 192 + f4se/f4se_common/f4se_common.vcxproj | 114 + f4se/f4se_common/f4se_common.vcxproj.filters | 19 + f4se/f4se_common/f4se_version.h | 78 + f4se/f4se_common/f4se_version.rc | 32 + f4se/f4se_readme.txt | 55 + f4se/f4se_whatsnew.txt | 389 ++ f4se/xbyak/COPYRIGHT | 47 + f4se/xbyak/xbyak.h | 2234 ++++++++++ f4se/xbyak/xbyak_bin2hex.h | 258 ++ f4se/xbyak/xbyak_mnemonic.h | 1461 +++++++ f4se/xbyak/xbyak_util.h | 533 +++ 323 files changed, 52370 insertions(+), 190 deletions(-) create mode 100644 common/IArchive.cpp create mode 100644 common/IArchive.h create mode 100644 common/IBufferStream.cpp create mode 100644 common/IBufferStream.h create mode 100644 common/IConsole.cpp create mode 100644 common/IConsole.h create mode 100644 common/ICriticalSection.h create mode 100644 common/IDataStream.cpp create mode 100644 common/IDataStream.h create mode 100644 common/IDatabase.cpp create mode 100644 common/IDatabase.h create mode 100644 common/IDatabase.inc create mode 100644 common/IDebugLog.cpp create mode 100644 common/IDebugLog.h create mode 100644 common/IDirectoryIterator.cpp create mode 100644 common/IDirectoryIterator.h create mode 100644 common/IDynamicCreate.cpp create mode 100644 common/IDynamicCreate.h create mode 100644 common/IErrors.cpp create mode 100644 common/IErrors.h create mode 100644 common/IEvent.cpp create mode 100644 common/IEvent.h create mode 100644 common/IFIFO.cpp create mode 100644 common/IFIFO.h create mode 100644 common/IFileStream.cpp create mode 100644 common/IFileStream.h create mode 100644 common/IInterlockedLong.cpp create mode 100644 common/IInterlockedLong.h create mode 100644 common/ILinkedList.h create mode 100644 common/IMemPool.cpp create mode 100644 common/IMemPool.h create mode 100644 common/IMutex.cpp create mode 100644 common/IMutex.h create mode 100644 common/IPipeClient.cpp create mode 100644 common/IPipeClient.h create mode 100644 common/IPipeServer.cpp create mode 100644 common/IPipeServer.h create mode 100644 common/IPrefix.cpp create mode 100644 common/IPrefix.h create mode 100644 common/IRangeMap.cpp create mode 100644 common/IRangeMap.h create mode 100644 common/IReadWriteLock.cpp create mode 100644 common/IReadWriteLock.h create mode 100644 common/ISegmentStream.cpp create mode 100644 common/ISegmentStream.h create mode 100644 common/ISingleton.cpp create mode 100644 common/ISingleton.h create mode 100644 common/ITextParser.cpp create mode 100644 common/ITextParser.h create mode 100644 common/IThread.cpp create mode 100644 common/IThread.h create mode 100644 common/ITimer.cpp create mode 100644 common/ITimer.h create mode 100644 common/ITypes.cpp create mode 100644 common/ITypes.h create mode 100644 common/common.vcproj create mode 100644 common/common.vcxproj create mode 100644 common/common.vcxproj.filters create mode 100644 common/common_license.txt create mode 100644 common/common_vc11.sln create mode 100644 common/common_vc11.vcxproj create mode 100644 common/common_vc11.vcxproj.filters create mode 100644 common/common_vc14.sln create mode 100644 common/common_vc14.vcxproj create mode 100644 common/common_vc14.vcxproj.filters create mode 100644 common/common_vc9.vcproj create mode 100644 f4se/f4se/BSCollision.cpp create mode 100644 f4se/f4se/BSCollision.h create mode 100644 f4se/f4se/BSGeometry.cpp create mode 100644 f4se/f4se/BSGeometry.h create mode 100644 f4se/f4se/BSGraphics.cpp create mode 100644 f4se/f4se/BSGraphics.h create mode 100644 f4se/f4se/BSLight.cpp create mode 100644 f4se/f4se/BSLight.h create mode 100644 f4se/f4se/BSModelDB.cpp create mode 100644 f4se/f4se/BSModelDB.h create mode 100644 f4se/f4se/BSParticleShaderEmitter.cpp create mode 100644 f4se/f4se/BSParticleShaderEmitter.h create mode 100644 f4se/f4se/BSSkin.cpp create mode 100644 f4se/f4se/BSSkin.h create mode 100644 f4se/f4se/CustomMenu.cpp create mode 100644 f4se/f4se/CustomMenu.h create mode 100644 f4se/f4se/GameAPI.cpp create mode 100644 f4se/f4se/GameAPI.h create mode 100644 f4se/f4se/GameCamera.cpp create mode 100644 f4se/f4se/GameCamera.h create mode 100644 f4se/f4se/GameCustomization.cpp create mode 100644 f4se/f4se/GameCustomization.h create mode 100644 f4se/f4se/GameData.cpp create mode 100644 f4se/f4se/GameData.h create mode 100644 f4se/f4se/GameEvents.cpp create mode 100644 f4se/f4se/GameEvents.h create mode 100644 f4se/f4se/GameExtraData.cpp create mode 100644 f4se/f4se/GameExtraData.h create mode 100644 f4se/f4se/GameFormComponents.cpp create mode 100644 f4se/f4se/GameFormComponents.h create mode 100644 f4se/f4se/GameForms.cpp create mode 100644 f4se/f4se/GameForms.h create mode 100644 f4se/f4se/GameInput.cpp create mode 100644 f4se/f4se/GameInput.h create mode 100644 f4se/f4se/GameMemory.cpp create mode 100644 f4se/f4se/GameMemory.h create mode 100644 f4se/f4se/GameMenus.cpp create mode 100644 f4se/f4se/GameMenus.h create mode 100644 f4se/f4se/GameMessages.cpp create mode 100644 f4se/f4se/GameMessages.h create mode 100644 f4se/f4se/GameObjects.cpp create mode 100644 f4se/f4se/GameObjects.h create mode 100644 f4se/f4se/GameRTTI.cpp create mode 100644 f4se/f4se/GameRTTI.h create mode 100644 f4se/f4se/GameRTTI.inl create mode 100644 f4se/f4se/GameReferences.cpp create mode 100644 f4se/f4se/GameReferences.h create mode 100644 f4se/f4se/GameSettings.cpp create mode 100644 f4se/f4se/GameSettings.h create mode 100644 f4se/f4se/GameStreams.cpp create mode 100644 f4se/f4se/GameStreams.h create mode 100644 f4se/f4se/GameThreads.cpp create mode 100644 f4se/f4se/GameThreads.h create mode 100644 f4se/f4se/GameTypes.cpp create mode 100644 f4se/f4se/GameTypes.h create mode 100644 f4se/f4se/GameUtilities.cpp create mode 100644 f4se/f4se/GameUtilities.h create mode 100644 f4se/f4se/GameWorkshop.cpp create mode 100644 f4se/f4se/GameWorkshop.h create mode 100644 f4se/f4se/Hooks_Camera.cpp create mode 100644 f4se/f4se/Hooks_Camera.h create mode 100644 f4se/f4se/Hooks_Debug.cpp create mode 100644 f4se/f4se/Hooks_Debug.h create mode 100644 f4se/f4se/Hooks_GameData.cpp create mode 100644 f4se/f4se/Hooks_GameData.h create mode 100644 f4se/f4se/Hooks_Gameplay.cpp create mode 100644 f4se/f4se/Hooks_Gameplay.h create mode 100644 f4se/f4se/Hooks_Input.cpp create mode 100644 f4se/f4se/Hooks_Input.h create mode 100644 f4se/f4se/Hooks_Memory.cpp create mode 100644 f4se/f4se/Hooks_Memory.h create mode 100644 f4se/f4se/Hooks_ObScript.cpp create mode 100644 f4se/f4se/Hooks_ObScript.h create mode 100644 f4se/f4se/Hooks_Papyrus.cpp create mode 100644 f4se/f4se/Hooks_Papyrus.h create mode 100644 f4se/f4se/Hooks_SaveLoad.cpp create mode 100644 f4se/f4se/Hooks_SaveLoad.h create mode 100644 f4se/f4se/Hooks_Scaleform.cpp create mode 100644 f4se/f4se/Hooks_Scaleform.h create mode 100644 f4se/f4se/Hooks_Threads.cpp create mode 100644 f4se/f4se/Hooks_Threads.h create mode 100644 f4se/f4se/InputMap.cpp create mode 100644 f4se/f4se/InputMap.h create mode 100644 f4se/f4se/InternalSerialization.cpp create mode 100644 f4se/f4se/InternalSerialization.h create mode 100644 f4se/f4se/NiCloningProcess.h create mode 100644 f4se/f4se/NiExtraData.cpp create mode 100644 f4se/f4se/NiExtraData.h create mode 100644 f4se/f4se/NiMaterials.cpp create mode 100644 f4se/f4se/NiMaterials.h create mode 100644 f4se/f4se/NiNodes.cpp create mode 100644 f4se/f4se/NiNodes.h create mode 100644 f4se/f4se/NiObjects.cpp create mode 100644 f4se/f4se/NiObjects.h create mode 100644 f4se/f4se/NiProperties.cpp create mode 100644 f4se/f4se/NiProperties.h create mode 100644 f4se/f4se/NiRTTI.cpp create mode 100644 f4se/f4se/NiRTTI.h create mode 100644 f4se/f4se/NiSerialization.cpp create mode 100644 f4se/f4se/NiSerialization.h create mode 100644 f4se/f4se/NiTextures.cpp create mode 100644 f4se/f4se/NiTextures.h create mode 100644 f4se/f4se/NiTypes.cpp create mode 100644 f4se/f4se/NiTypes.h create mode 100644 f4se/f4se/ObScript.cpp create mode 100644 f4se/f4se/ObScript.h create mode 100644 f4se/f4se/PapyrusActor.cpp create mode 100644 f4se/f4se/PapyrusActor.h create mode 100644 f4se/f4se/PapyrusActorBase.cpp create mode 100644 f4se/f4se/PapyrusActorBase.h create mode 100644 f4se/f4se/PapyrusArgs.cpp create mode 100644 f4se/f4se/PapyrusArgs.h create mode 100644 f4se/f4se/PapyrusArmor.cpp create mode 100644 f4se/f4se/PapyrusArmor.h create mode 100644 f4se/f4se/PapyrusArmorAddon.cpp create mode 100644 f4se/f4se/PapyrusArmorAddon.h create mode 100644 f4se/f4se/PapyrusCell.cpp create mode 100644 f4se/f4se/PapyrusCell.h create mode 100644 f4se/f4se/PapyrusComponent.cpp create mode 100644 f4se/f4se/PapyrusComponent.h create mode 100644 f4se/f4se/PapyrusConstructibleObject.cpp create mode 100644 f4se/f4se/PapyrusConstructibleObject.h create mode 100644 f4se/f4se/PapyrusDefaultObject.cpp create mode 100644 f4se/f4se/PapyrusDefaultObject.h create mode 100644 f4se/f4se/PapyrusDelayFunctors.cpp create mode 100644 f4se/f4se/PapyrusDelayFunctors.h create mode 100644 f4se/f4se/PapyrusDelayFunctorsDef.inl create mode 100644 f4se/f4se/PapyrusDelayFunctorsDef_Base.inl create mode 100644 f4se/f4se/PapyrusEncounterZone.cpp create mode 100644 f4se/f4se/PapyrusEncounterZone.h create mode 100644 f4se/f4se/PapyrusEquipSlot.cpp create mode 100644 f4se/f4se/PapyrusEquipSlot.h create mode 100644 f4se/f4se/PapyrusEvents.cpp create mode 100644 f4se/f4se/PapyrusEvents.h create mode 100644 f4se/f4se/PapyrusEventsDef.inl create mode 100644 f4se/f4se/PapyrusEventsDef_Base.inl create mode 100644 f4se/f4se/PapyrusF4SE.cpp create mode 100644 f4se/f4se/PapyrusF4SE.h create mode 100644 f4se/f4se/PapyrusFavoritesManager.cpp create mode 100644 f4se/f4se/PapyrusFavoritesManager.h create mode 100644 f4se/f4se/PapyrusForm.cpp create mode 100644 f4se/f4se/PapyrusForm.h create mode 100644 f4se/f4se/PapyrusGame.cpp create mode 100644 f4se/f4se/PapyrusGame.h create mode 100644 f4se/f4se/PapyrusHeadPart.cpp create mode 100644 f4se/f4se/PapyrusHeadPart.h create mode 100644 f4se/f4se/PapyrusInput.cpp create mode 100644 f4se/f4se/PapyrusInput.h create mode 100644 f4se/f4se/PapyrusInstanceData.cpp create mode 100644 f4se/f4se/PapyrusInstanceData.h create mode 100644 f4se/f4se/PapyrusInterfaces.cpp create mode 100644 f4se/f4se/PapyrusInterfaces.h create mode 100644 f4se/f4se/PapyrusLocation.cpp create mode 100644 f4se/f4se/PapyrusLocation.h create mode 100644 f4se/f4se/PapyrusMaterialSwap.cpp create mode 100644 f4se/f4se/PapyrusMaterialSwap.h create mode 100644 f4se/f4se/PapyrusMath.cpp create mode 100644 f4se/f4se/PapyrusMath.h create mode 100644 f4se/f4se/PapyrusMiscObject.cpp create mode 100644 f4se/f4se/PapyrusMiscObject.h create mode 100644 f4se/f4se/PapyrusNativeFunctionDef.inl create mode 100644 f4se/f4se/PapyrusNativeFunctionDef_Base.inl create mode 100644 f4se/f4se/PapyrusNativeFunctions.cpp create mode 100644 f4se/f4se/PapyrusNativeFunctions.h create mode 100644 f4se/f4se/PapyrusObjectMod.cpp create mode 100644 f4se/f4se/PapyrusObjectMod.h create mode 100644 f4se/f4se/PapyrusObjectReference.cpp create mode 100644 f4se/f4se/PapyrusObjectReference.h create mode 100644 f4se/f4se/PapyrusObjects.cpp create mode 100644 f4se/f4se/PapyrusObjects.h create mode 100644 f4se/f4se/PapyrusPerk.cpp create mode 100644 f4se/f4se/PapyrusPerk.h create mode 100644 f4se/f4se/PapyrusScaleformAdapter.cpp create mode 100644 f4se/f4se/PapyrusScaleformAdapter.h create mode 100644 f4se/f4se/PapyrusScriptObject.cpp create mode 100644 f4se/f4se/PapyrusScriptObject.h create mode 100644 f4se/f4se/PapyrusSerialization.cpp create mode 100644 f4se/f4se/PapyrusSerialization.h create mode 100644 f4se/f4se/PapyrusStruct.cpp create mode 100644 f4se/f4se/PapyrusStruct.h create mode 100644 f4se/f4se/PapyrusUI.cpp create mode 100644 f4se/f4se/PapyrusUI.h create mode 100644 f4se/f4se/PapyrusUtilities.cpp create mode 100644 f4se/f4se/PapyrusUtilities.h create mode 100644 f4se/f4se/PapyrusUtility.cpp create mode 100644 f4se/f4se/PapyrusUtility.h create mode 100644 f4se/f4se/PapyrusVM.cpp create mode 100644 f4se/f4se/PapyrusVM.h create mode 100644 f4se/f4se/PapyrusValue.cpp create mode 100644 f4se/f4se/PapyrusValue.h create mode 100644 f4se/f4se/PapyrusWaterType.cpp create mode 100644 f4se/f4se/PapyrusWaterType.h create mode 100644 f4se/f4se/PapyrusWeapon.cpp create mode 100644 f4se/f4se/PapyrusWeapon.h create mode 100644 f4se/f4se/PluginAPI.h create mode 100644 f4se/f4se/PluginManager.cpp create mode 100644 f4se/f4se/PluginManager.h create mode 100644 f4se/f4se/ScaleformAPI.cpp create mode 100644 f4se/f4se/ScaleformAPI.h create mode 100644 f4se/f4se/ScaleformCallbacks.cpp create mode 100644 f4se/f4se/ScaleformCallbacks.h create mode 100644 f4se/f4se/ScaleformLoader.cpp create mode 100644 f4se/f4se/ScaleformLoader.h create mode 100644 f4se/f4se/ScaleformMovie.cpp create mode 100644 f4se/f4se/ScaleformMovie.h create mode 100644 f4se/f4se/ScaleformSerialization.cpp create mode 100644 f4se/f4se/ScaleformSerialization.h create mode 100644 f4se/f4se/ScaleformState.cpp create mode 100644 f4se/f4se/ScaleformState.h create mode 100644 f4se/f4se/ScaleformTranslator.cpp create mode 100644 f4se/f4se/ScaleformTranslator.h create mode 100644 f4se/f4se/ScaleformTypes.cpp create mode 100644 f4se/f4se/ScaleformTypes.h create mode 100644 f4se/f4se/ScaleformValue.cpp create mode 100644 f4se/f4se/ScaleformValue.h create mode 100644 f4se/f4se/Serialization.cpp create mode 100644 f4se/f4se/Serialization.h create mode 100644 f4se/f4se/Translation.cpp create mode 100644 f4se/f4se/Translation.h create mode 100644 f4se/f4se/bhkWorld.cpp create mode 100644 f4se/f4se/bhkWorld.h create mode 100644 f4se/f4se/exports.def create mode 100644 f4se/f4se/f4se.cpp create mode 100644 f4se/f4se/f4se.vcxproj create mode 100644 f4se/f4se/f4se.vcxproj.filters create mode 100644 f4se/f4se_common/BranchTrampoline.cpp create mode 100644 f4se/f4se_common/BranchTrampoline.h create mode 100644 f4se/f4se_common/Relocation.cpp create mode 100644 f4se/f4se_common/Relocation.h create mode 100644 f4se/f4se_common/SafeWrite.cpp create mode 100644 f4se/f4se_common/SafeWrite.h create mode 100644 f4se/f4se_common/Utilities.cpp create mode 100644 f4se/f4se_common/Utilities.h create mode 100644 f4se/f4se_common/f4se_common.vcxproj create mode 100644 f4se/f4se_common/f4se_common.vcxproj.filters create mode 100644 f4se/f4se_common/f4se_version.h create mode 100644 f4se/f4se_common/f4se_version.rc create mode 100644 f4se/f4se_readme.txt create mode 100644 f4se/f4se_whatsnew.txt create mode 100644 f4se/xbyak/COPYRIGHT create mode 100644 f4se/xbyak/xbyak.h create mode 100644 f4se/xbyak/xbyak_bin2hex.h create mode 100644 f4se/xbyak/xbyak_mnemonic.h create mode 100644 f4se/xbyak/xbyak_util.h diff --git a/CBPSSE.sln b/CBPSSE.sln index 26381bd..503cafe 100644 --- a/CBPSSE.sln +++ b/CBPSSE.sln @@ -1,15 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.12 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29609.76 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CBPSSE", "CBPSSE\CBPSSE.vcxproj", "{49438A48-1C92-4D07-97C0-BDF0744386F9}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common_vc14", "skse64\src\common\common_vc14.vcxproj", "{472E19AB-DEF0-42DF-819B-18722E8DC822}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4se", "f4se\f4se\f4se.vcxproj", "{A236F69D-8FF9-4491-AC5F-45BF49448BBE}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "skse64_common", "skse64\src\skse64\skse64_common\skse64_common.vcxproj", "{5FD1C08D-DB80-480C-A1C6-F0920005CD13}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "f4se_common", "f4se\f4se_common\f4se_common.vcxproj", "{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "skse64", "skse64\src\skse64\skse64\skse64.vcxproj", "{7028B79C-06E3-4D9A-B38C-1DC3680B1BDB}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common_vc14", "common\common_vc14.vcxproj", "{472E19AB-DEF0-42DF-819B-18722E8DC822}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -27,22 +27,24 @@ Global {49438A48-1C92-4D07-97C0-BDF0744386F9}.Release|x64.Build.0 = Release|x64 {49438A48-1C92-4D07-97C0-BDF0744386F9}.Release|x86.ActiveCfg = Release|Win32 {49438A48-1C92-4D07-97C0-BDF0744386F9}.Release|x86.Build.0 = Release|Win32 + {A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Debug|x64.ActiveCfg = Debug|x64 + {A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Debug|x64.Build.0 = Debug|x64 + {A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Debug|x86.ActiveCfg = Debug|x64 + {A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Release|x64.ActiveCfg = Release|x64 + {A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Release|x64.Build.0 = Release|x64 + {A236F69D-8FF9-4491-AC5F-45BF49448BBE}.Release|x86.ActiveCfg = Release|x64 + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Debug|x64.ActiveCfg = Debug|x64 + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Debug|x64.Build.0 = Debug|x64 + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Debug|x86.ActiveCfg = Debug|x64 + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Release|x64.ActiveCfg = Release|x64 + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Release|x64.Build.0 = Release|x64 + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6}.Release|x86.ActiveCfg = Release|x64 {472E19AB-DEF0-42DF-819B-18722E8DC822}.Debug|x64.ActiveCfg = Debug|x64 {472E19AB-DEF0-42DF-819B-18722E8DC822}.Debug|x64.Build.0 = Debug|x64 {472E19AB-DEF0-42DF-819B-18722E8DC822}.Debug|x86.ActiveCfg = Debug|x64 {472E19AB-DEF0-42DF-819B-18722E8DC822}.Release|x64.ActiveCfg = Release|x64 {472E19AB-DEF0-42DF-819B-18722E8DC822}.Release|x64.Build.0 = Release|x64 {472E19AB-DEF0-42DF-819B-18722E8DC822}.Release|x86.ActiveCfg = Release|x64 - {5FD1C08D-DB80-480C-A1C6-F0920005CD13}.Debug|x64.ActiveCfg = Debug|x64 - {5FD1C08D-DB80-480C-A1C6-F0920005CD13}.Debug|x64.Build.0 = Debug|x64 - {5FD1C08D-DB80-480C-A1C6-F0920005CD13}.Debug|x86.ActiveCfg = Debug|x64 - {5FD1C08D-DB80-480C-A1C6-F0920005CD13}.Release|x64.ActiveCfg = Release|x64 - {5FD1C08D-DB80-480C-A1C6-F0920005CD13}.Release|x64.Build.0 = Release|x64 - {5FD1C08D-DB80-480C-A1C6-F0920005CD13}.Release|x86.ActiveCfg = Release|x64 - {7028B79C-06E3-4D9A-B38C-1DC3680B1BDB}.Debug|x64.ActiveCfg = Debug|x64 - {7028B79C-06E3-4D9A-B38C-1DC3680B1BDB}.Debug|x86.ActiveCfg = Debug|x64 - {7028B79C-06E3-4D9A-B38C-1DC3680B1BDB}.Release|x64.ActiveCfg = Release|x64 - {7028B79C-06E3-4D9A-B38C-1DC3680B1BDB}.Release|x86.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index 65f3d98..11603d6 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -22,7 +22,7 @@ 15.0 {49438A48-1C92-4D07-97C0-BDF0744386F9} CBBPSSE - 10.0.15063.0 + 10.0.18362.0 CBPSSE @@ -72,14 +72,14 @@ .dll - D:\SteamLibrary\steamapps\common\Skyrim Special Edition\Data\SKSE\Plugins cbp .dll - D:\SteamLibrary\steamapps\common\Skyrim Special Edition\Data\SKSE\Plugins cbp false + $(LibraryPath) + $(IncludePath) @@ -97,8 +97,8 @@ Disabled - $(SolutionDir)\skse64\src\;$(SolutionDir)\skse64\src\skse64;%(AdditionalIncludeDirectories) - _USRDLL;PLUGIN_EXAMPLE_EXPORTS;RUNTIME;RUNTIME_VERSION=0x01050610;%(PreprocessorDefinitions) + $(SolutionDir)f4se;$(SolutionDir);%(AdditionalIncludeDirectories) + _USRDLL;PLUGIN_EXAMPLE_EXPORTS;RUNTIME;RUNTIME_VERSION=0x010A0A30;%(PreprocessorDefinitions) common/IPrefix.h;%(ForcedIncludeFiles) MultiThreadedDebug ProgramDatabase @@ -131,8 +131,8 @@ true - $(SolutionDir)\skse64\src\;$(SolutionDir)\skse64\src\skse64;%(AdditionalIncludeDirectories) - _USRDLL;PLUGIN_EXAMPLE_EXPORTS;RUNTIME;RUNTIME_VERSION=0x01050610;%(PreprocessorDefinitions) + $(SolutionDir)f4se;$(SolutionDir);%(AdditionalIncludeDirectories) + _USRDLL;PLUGIN_EXAMPLE_EXPORTS;RUNTIME;RUNTIME_VERSION=0x010A0A30;%(PreprocessorDefinitions) MultiThreaded common/IPrefix.h;%(ForcedIncludeFiles) @@ -140,20 +140,21 @@ true true exports.def - %(AdditionalDependencies) Windows true + %(AdditionalLibraryDirectories) + - - - - - - - + + + + + + + @@ -161,13 +162,13 @@ - - - - - - - + + + + + + + @@ -177,12 +178,18 @@ - - - {5fd1c08d-db80-480c-a1c6-f0920005cd13} + + {472e19ab-def0-42df-819b-18722e8dc822} + + + {a236f69d-8ff9-4491-ac5f-45bf49448bbe} + true + + + {20c6411c-596f-4b85-be4e-8bc91f59d8a6} diff --git a/CBPSSE/CBPSSE.vcxproj.filters b/CBPSSE/CBPSSE.vcxproj.filters index b32ea4b..567fa75 100644 --- a/CBPSSE/CBPSSE.vcxproj.filters +++ b/CBPSSE/CBPSSE.vcxproj.filters @@ -13,12 +13,15 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - {dd544c35-f8aa-4e6c-9504-0565cc379822} - {850dfb75-eff3-4420-adde-cfae8d537337} + + {644f2ce2-9716-41b9-99ab-5ca319adf7be} + + + {15ad9f91-fd32-4588-bc5c-830673dc512a} + @@ -36,26 +39,26 @@ Header Files - - Source Files\api + + Header Files\api - - Source Files\api + + Header Files\api - - Source Files\api + + Header Files\api - - Source Files\api + + Header Files\api - - Source Files\api + + Header Files\api - - Source Files\api + + Header Files\api - - Source Files\api + + Header Files\api @@ -83,25 +86,25 @@ Source Files - + Source Files\api - + Source Files\api - + Source Files\api - + Source Files\api - + Source Files\api - + Source Files\api - + Source Files\api @@ -109,9 +112,6 @@ Source Files - - Source Files\api - diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 009be0d..145dd70 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -1,29 +1,29 @@ #include "SimObj.h" -#include "skse64/NiNodes.h" -#include "skse64/GameForms.h" -#include "skse64/GameRTTI.h" +#include "f4se/NiNodes.h" +#include "f4se/GameForms.h" +#include "f4se/GameRTTI.h" #include "log.h" // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory -const char *leftBreastName = "NPC L Breast"; -const char *rightBreastName = "NPC R Breast"; -const char *leftButtName = "NPC L Butt"; -const char *rightButtName = "NPC R Butt"; +const char *leftBreastName = "Breast_CBP_R_02"; +const char *rightBreastName = "Breast_CBP_L_02"; +const char *leftButtName = "Butt_CBP_R_01"; +const char *rightButtName = "Butt_CBP_L_01"; const char *bellyName = "HDT Belly"; const char *scrotumName = "NPC GenitalsScrotum [GenScrot]"; -const char *leftScrotumName = "NPC L GenitalsScrotum [LGenScrot]"; -const char *rightScrotumName = "NPC R GenitalsScrotum [RGenScrot]"; +const char *leftScrotumName = "Penis_Balls_CBP_01"; +const char *rightScrotumName = "Penis_Balls_CBP_02"; std::unordered_map configMap = { {leftBreastName, "Breast"}, {rightBreastName, "Breast"}, {leftButtName, "Butt"}, {rightButtName, "Butt"}, - {bellyName, "Belly"} }; + /*{bellyName, "Belly"}*/ }; -std::vector femaleBones = { leftBreastName, rightBreastName, leftButtName, rightButtName, bellyName }; +std::vector femaleBones = { leftBreastName, rightBreastName, leftButtName, rightButtName, /*bellyName*/ }; SimObj::SimObj(Actor *actor, config_t &config) : things(5){ @@ -36,20 +36,21 @@ SimObj::~SimObj() { bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &config) { - //logger.error("bind\n"); + logger.error("bind\n"); - auto loadedState = actor->loadedState; - if (loadedState && loadedState->node) { + auto loadedData = actor->unkF0; + if (loadedData && loadedData->rootNode) { bound = true; things.clear(); for (const char * &b : boneNames) { BSFixedString cs(b); - auto bone = loadedState->node->GetObjectByName(&cs.data); + auto bone = loadedData->rootNode->GetObjectByName(&cs); if (!bone) { - logger.info("Failed to find Bone %s for actor %d\n", b, actor->formID); + logger.info("Failed to find Bone %s for actor %08x\n", b, actor->formID); } else { + logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); things.emplace(b, Thing(bone, cs)); } } @@ -60,9 +61,9 @@ bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t & } bool SimObj::actorValid(Actor *actor) { - if (actor->flags & TESForm::kFlagIsDeleted) + if (actor->flags & TESForm::kFlag_IsDeleted) return false; - if (actor && actor->loadedState && actor->loadedState->node) + if (actor && actor->unkF0 && actor->unkF0->rootNode) return true; return false; } @@ -71,11 +72,11 @@ bool SimObj::actorValid(Actor *actor) { void SimObj::update(Actor *actor) { if (!bound) return; - //logger.error("update\n"); + logger.error("update\n"); for (auto &t : things) { t.second.update(actor); } - //logger.error("end SimObj update\n"); + logger.error("end SimObj update\n"); } bool SimObj::updateConfig(config_t & config) { diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index 1d98c0a..80b5d38 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -2,7 +2,7 @@ #include #include -#include "skse64/GameReferences.h" +#include "f4se/GameReferences.h" #include "Thing.h" #include "config.h" diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index a333a02..3c70772 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -1,14 +1,19 @@ #include "Thing.h" #include "log.h" -#include "skse64\NiNodes.h" +#include "f4se\NiNodes.h" #include -Thing::Thing(NiAVObject *obj, BSFixedString &name) +#define PI 3.14159265 +#define DEBUG 0 + +Thing::Thing(NiAVObject* obj, BSFixedString& name) : boneName(name) , velocity(NiPoint3(0, 0, 0)) { oldWorldPos = obj->m_worldTransform.pos; + time = clock(); + firstRun = true; } Thing::~Thing() { @@ -18,10 +23,10 @@ void showPos(NiPoint3 &p) { logger.info("%8.2f %8.2f %8.2f\n", p.x, p.y, p.z); } -void showRot(NiMatrix33 &r) { - logger.info("%8.2f %8.2f %8.2f\n", r.data[0][0], r.data[0][1], r.data[0][2]); - logger.info("%8.2f %8.2f %8.2f\n", r.data[1][0], r.data[1][1], r.data[1][2]); - logger.info("%8.2f %8.2f %8.2f\n", r.data[2][0], r.data[2][1], r.data[2][2]); +void showRot(NiMatrix43 &r) { + logger.info("%8.2f %8.2f %8.2f %8.2f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); + logger.info("%8.2f %8.2f %8.2f %8.2f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); + logger.info("%8.2f %8.2f %8.2f %8.2f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); } @@ -89,24 +94,89 @@ void Thing::update(Actor *actor) { if (deltaT > 64) deltaT = 64; if (deltaT < 8) deltaT = 8; - auto loadedState = actor->loadedState; - if (!loadedState || !loadedState->node) { + auto loadedState = actor->unkF0; + if (!loadedState || !loadedState->rootNode) { logger.error("No loaded state for actor %08x\n", actor->formID); return; } - auto obj = loadedState->node->GetObjectByName(&boneName.data); - if (!obj) + auto obj = loadedState->rootNode->GetObjectByName(&boneName); + + if (!obj) { + logger.error("Couldn't get name for loaded state for actor %08x\n", actor->formID); + return; + } + + if (!obj->m_parent) { + logger.error("Couldn't get bone %s parent for actor %08x\n", boneName.c_str() , actor->formID); return; + } + +#if DEBUG + auto scene_obj = obj; + while (scene_obj->m_parent && scene_obj->m_name != "skeleton.nif") + { + logger.info(scene_obj->m_name); + logger.info("\n---\n"); + showPos(scene_obj->m_localTransform.pos); + showPos(scene_obj->m_worldTransform.pos); + logger.info("---\n"); + showRot(scene_obj->m_localTransform.rot); + showRot(scene_obj->m_worldTransform.rot); + logger.info("---\n"); + if (scene_obj->m_parent) { + showPos((scene_obj->m_worldTransform.rot * scene_obj->m_localTransform.pos) + scene_obj->m_parent->m_worldTransform.pos); // m_worldTransform.pos + showRot(scene_obj->m_localTransform.rot * scene_obj->m_parent->m_worldTransform.rot); // m_worldTransform.rot + } + scene_obj = scene_obj->m_parent; + } +#endif - //Offset to move Center of Mass make rotaional motion more significant - NiPoint3 target = obj->m_parent->m_worldTransform * NiPoint3(0, cogOffset, 0); - //logger.error("Target: "); - //showPos(target); + if (firstRun) { + orig_local_pos = obj->m_localTransform.pos; + orig_local_rot = obj->m_localTransform.rot; + } + +#if DEBUG + logger.error("bone %s for actor %08x\n", boneName.c_str(), actor->formID); + //showPos(obj->m_parent->m_worldTransform.pos + obj->m_localTransform.pos); + showPos((obj->m_localTransform.rot.Transpose() * obj->m_localTransform.pos)); +#endif + + // Offset to move Center of Mass make rotaional motion more significant + // This target is + NiPoint3 target; + + // TODO: left and right with same parents transforms should be different... example: the butt + // Relative Left + //if (obj->m_localTransform.pos.x < 0.0) { + target = (obj->m_worldTransform.rot * NiPoint3(0, cogOffset, 0)) + obj->m_worldTransform.pos; + //} + //// Relative Right + //else { + // target = (obj->m_worldTransform.rot * + // (obj->m_localTransform.pos + NiPoint3(0, 0, 0))) + obj->m_parent->m_worldTransform.pos; + //} + +#if DEBUG + logger.error("World Position: "); + showPos(obj->m_worldTransform.pos); + logger.error("Target: "); + showPos(target); +#endif + + // diff is Difference in position between old and new world position NiPoint3 diff = target - oldWorldPos; - diff += obj->m_parent->m_worldTransform.rot * NiPoint3(0, 0, gravityCorrection); + + // move up in rotated angle for gravity correction + diff += obj->m_worldTransform.rot * NiPoint3(0, 0, gravityCorrection); + +#if DEBUG + logger.error("Diff after gravity correction: "); + showPos(diff); +#endif if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { - //logger.error("transform reset\n"); + logger.error("transform reset\n"); obj->m_localTransform.pos = NiPoint3(0, 0, 0); oldWorldPos = target; velocity = NiPoint3(0, 0, 0); @@ -119,8 +189,13 @@ void Thing::update(Actor *actor) { // Compute the "Spring" Force NiPoint3 diff2(diff.x * diff.x * sgn(diff.x), diff.y * diff.y * sgn(diff.y), diff.z * diff.z * sgn(diff.z)); NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - NiPoint3(0, 0, gravityBias); - //showPos(diff); - //showPos(force); + +#if DEBUG + logger.error("Diff2: "); + showPos(diff2); + logger.error("Force: "); + showPos(force); +#endif do { // Assume mass is 1, so Accelleration is Force, can vary mass by changinf force @@ -134,35 +209,66 @@ void Thing::update(Actor *actor) { NiPoint3 newPos = oldWorldPos + posDelta; +#if DEBUG + logger.error("posDelta: "); + showPos(posDelta); + logger.error("newPos: "); + showPos(newPos); +#endif // clamp the difference to stop the breast severely lagging at low framerates - auto diff = newPos - target; + diff = newPos- target; + diff.x = clamp(diff.x, -maxOffset, maxOffset); diff.y = clamp(diff.y, -maxOffset, maxOffset); diff.z = clamp(diff.z-gravityCorrection, -maxOffset, maxOffset) + gravityCorrection; - oldWorldPos = diff + target; + //oldWorldPos = diff + target; + +#if DEBUG + logger.error("diff from newPos: "); + showPos(diff); +#endif //logger.error("set positions\n"); // move the bones based on the supplied weightings // Convert the world translations into local coordinates - auto invRot = obj->m_parent->m_worldTransform.rot.Transpose(); - auto ldiff = invRot * diff; + auto invRot = obj->m_localTransform.rot * obj->m_worldTransform.rot.Transpose(); + auto local_diff = invRot * diff; - // remove component along bone - might want something closer to worldY + //showPos(diff); + //showPos(local_diff); + // remove component along bone - might want something closer to world //ldiff.y = 0; - oldWorldPos = (obj->m_parent->m_worldTransform.rot * ldiff) + target; - - obj->m_localTransform.pos.x = ldiff.x * linearX; - obj->m_localTransform.pos.y = ldiff.y * linearY; - obj->m_localTransform.pos.z = ldiff.z * linearZ; - auto rdiff = ldiff * rotational; - obj->m_localTransform.rot.SetEulerAngles(0, 0, rdiff.z); - + oldWorldPos = diff + target; + //showRot(obj->m_parent->m_worldTransform.rot * invRot); + +#if DEBUG + logger.error("localTransform.pos: "); + showPos(obj->m_localTransform.pos); + logger.error("local_diff: "); + showPos(local_diff); +#endif + // scale positions from config + obj->m_localTransform.pos.x = orig_local_pos.x + (local_diff.x * linearX); + obj->m_localTransform.pos.y = orig_local_pos.y + (local_diff.y * linearY); + obj->m_localTransform.pos.z = orig_local_pos.z + (local_diff.z * linearZ); + + // do some rotation + auto rdiff = local_diff * rotational; +#if DEBUG + logger.error("rdiff: "); + showPos(rdiff); +#endif + float heading, attitude, bank; + orig_local_rot.GetEulerAngles(&heading, &attitude, &bank); + obj->m_localTransform.rot.SetEulerAngles(heading + rdiff.x, attitude + rdiff.y, bank + rdiff.z); } - //logger.error("end update()\n"); - + firstRun = false; +#if DEBUG + logger.error("end update()\n"); +#endif diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index a3faf14..db11ca3 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -1,7 +1,7 @@ #pragma once -#include -#include -#include +#include +#include +#include #include #include "config.h" @@ -9,6 +9,8 @@ class Thing { BSFixedString boneName; NiPoint3 oldWorldPos; NiPoint3 velocity; + NiPoint3 orig_local_pos; + NiMatrix43 orig_local_rot; clock_t time; public: @@ -27,6 +29,10 @@ class Thing { float rotational = 0.1; float timeStep = 1.0f; + boolean firstRun; + + //std::unordered_map boneRotationMatrices; + Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 4bfd55c..986a111 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -16,8 +16,8 @@ config_t config; void loadConfig() { char buffer[1024]; - //logger.info("loadConfig\n"); - FILE *fh = fopen("Data\\SKSE\\Plugins\\CBPConfig.txt", "r"); + logger.info("loadConfig\n"); + FILE *fh = fopen("Data\\F4SE\\Plugins\\CBPConfig.txt", "r"); if (!fh) { logger.error("Failed to open config file CBPConfig.txt\n"); //Console_Print("Failed to open config file CBPConfig.txt"); diff --git a/CBPSSE/exports.def b/CBPSSE/exports.def index 8ac5543..cff87a7 100644 --- a/CBPSSE/exports.def +++ b/CBPSSE/exports.def @@ -1,4 +1,4 @@ LIBRARY "cbp" EXPORTS -SKSEPlugin_Query -SKSEPlugin_Load +F4SEPlugin_Query +F4SEPlugin_Load diff --git a/CBPSSE/hookD3D.cpp b/CBPSSE/hookD3D.cpp index 031a928..c993d75 100644 --- a/CBPSSE/hookD3D.cpp +++ b/CBPSSE/hookD3D.cpp @@ -5,7 +5,7 @@ #include "log.h" #include "../detourxs-master/detourxs.h" -#include "skse64_common/Utilities.h" +#include "f4se_common/Utilities.h" //#define SAFE_RELEASE(x) if((x)) {x->Release(); x = nullptr;} // @@ -162,8 +162,8 @@ UINT64 __cdecl Render(void *This, UINT64 arg) //RelocPtr ProcessTasks_HookTarget_Enter(0x005B34A0); //RelocPtr ProcessTasks_HookTarget_Enter(0x005B31E0); //RelocPtr ProcessTasks_HookTarget_Enter(0x005B31E0); -RelocPtr ProcessTasks_HookTarget_Enter(0x005B2FF0); - +//RelocPtr ProcessTasks_HookTarget_Enter(0x005B2FF0); +RelocPtr ProcessEventQueue_Internal(0x0211CF80); @@ -175,6 +175,6 @@ void DoHook() { //CreateThread(NULL, 0, HookCreateFn, NULL, 0, NULL); //renderDetour.Create(render.GetPtr(), Render, &(LPVOID)orender); - renderDetour.Create(ProcessTasks_HookTarget_Enter.GetPtr(), Render, &(LPVOID)orender); + renderDetour.Create((LPVOID)ProcessEventQueue_Internal.GetPtr(), Render, &(LPVOID)orender); //orender = (renderHook)renderDetour.GetTrampoline(); } \ No newline at end of file diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index 88da45b..61ffa4c 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -30,4 +30,4 @@ void Logger::error(const char *fmt...) { } } -Logger logger("Data\\SKSE\\Plugins\\cbp.log"); +Logger logger("Data\\F4SE\\Plugins\\cbp.log"); diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index 38c3078..7ac8752 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -1,10 +1,10 @@ #include "common/ITypes.h" #include -#include "skse64/PluginAPI.h" -#include "skse64_common/skse_version.h" -#include "skse64_common/SafeWrite.h" -#include "skse64/GameAPI.h" -#include "skse64/GameEvents.h" +#include "f4se/PluginAPI.h" +#include "f4se_common/f4se_version.h" +#include "f4se_common/SafeWrite.h" +#include "f4se/GameAPI.h" +#include "f4se/GameEvents.h" #include "log.h" #include "config.h" @@ -12,63 +12,73 @@ PluginHandle g_pluginHandle = kPluginHandle_Invalid; -//SKSEMessagingInterface * g_messagingInterface = NULL; +//F4SEMessagingInterface * g_messagingInterface = NULL; -//SKSEScaleformInterface * g_scaleform = NULL; -//SKSESerializationInterface * g_serialization = NULL; -SKSETaskInterface * g_task = nullptr; -//IDebugLog gLog("Data\\SKSE\\Plugins\\hook.log"); +//F4SEScaleformInterface * g_scaleform = NULL; +//F4SESerializationInterface * g_serialization = NULL; +F4SETaskInterface * g_task = nullptr; +//IDebugLog gLog("Data\\F4SE\\Plugins\\hook.log"); void DoHook(); -void MessageHandler(SKSEMessagingInterface::Message * msg) +void MessageHandler(F4SEMessagingInterface::Message * msg) { switch (msg->type) { - case SKSEMessagingInterface::kMessage_DataLoaded: + case F4SEMessagingInterface::kMessage_GameDataReady: { - logger.info("kMessage_DataLoaded\n"); + logger.info("kMessage_GameDataReady\n"); } break; - case SKSEMessagingInterface::kMessage_NewGame: + case F4SEMessagingInterface::kMessage_GameLoaded: + { + logger.info("kMessage_GameLoaded\n"); + } + break; + case F4SEMessagingInterface::kMessage_NewGame: { logger.info("kMessage_NewGame\n"); } break; - case SKSEMessagingInterface::kMessage_PreLoadGame: + case F4SEMessagingInterface::kMessage_PreLoadGame: { logger.info("kMessage_PreLoadGame\n"); } break; - case SKSEMessagingInterface::kMessage_PostLoad: + case F4SEMessagingInterface::kMessage_PostLoad: { logger.info("kMessage_PostLoad\n"); } break; - case SKSEMessagingInterface::kMessage_PostPostLoad: + case F4SEMessagingInterface::kMessage_PostPostLoad: { logger.info("kMessage_PostPostLoad\n"); } break; - case SKSEMessagingInterface::kMessage_PostLoadGame: + case F4SEMessagingInterface::kMessage_PostLoadGame: { logger.info("kMessage_PostLoadGame\n"); } break; - case SKSEMessagingInterface::kMessage_SaveGame: + case F4SEMessagingInterface::kMessage_PreSaveGame: + { + logger.info("kMessage_PreSaveGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostSaveGame: { - logger.info("kMessage_SaveGame\n"); + logger.info("kMessage_PostSaveGame\n"); } break; - case SKSEMessagingInterface::kMessage_DeleteGame: + case F4SEMessagingInterface::kMessage_DeleteGame: { logger.info("kMessage_DeleteGame\n"); } break; - case SKSEMessagingInterface::kMessage_InputLoaded: + case F4SEMessagingInterface::kMessage_InputLoaded: { logger.info("kMessage_InputLoaded\n"); } @@ -81,9 +91,9 @@ void MessageHandler(SKSEMessagingInterface::Message * msg) extern "C" { - bool SKSEPlugin_Query(const SKSEInterface * skse, PluginInfo * info) + bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) { - logger.info("CBP Physics SKSE Plugin\n"); + logger.info("CBP Physics F4SE Plugin\n"); logger.error("Query called\n"); @@ -93,16 +103,16 @@ extern "C" info->version = 24; // store plugin handle so we can identify ourselves later - g_pluginHandle = skse->GetPluginHandle(); + g_pluginHandle = f4se->GetPluginHandle(); - if (skse->isEditor) + if (f4se->isEditor) { logger.error("loaded in editor, marking as incompatible\n"); return false; } - else if (skse->runtimeVersion != RUNTIME_VERSION) + else if (f4se->runtimeVersion != RUNTIME_VERSION) { - logger.error("unsupported runtime version %08X", skse->runtimeVersion); + logger.error("unsupported runtime version %08X", f4se->runtimeVersion); return false; } // supported runtime version @@ -111,11 +121,11 @@ extern "C" return true; } - bool SKSEPlugin_Load(const SKSEInterface * skse) + bool F4SEPlugin_Load(const F4SEInterface * f4se) { logger.error("CBP Loading\n"); - g_task = (SKSETaskInterface *)skse->QueryInterface(kInterface_Task); + g_task = (F4SETaskInterface *)f4se->QueryInterface(kInterface_Task); if (!g_task) { logger.error("Couldn't get Task interface\n"); @@ -125,7 +135,7 @@ extern "C" // Load initial config before the hook. logger.error("Loading Config\n"); loadConfig(); - //g_messagingInterface->RegisterListener(0, "SKSE", MessageHandler); + //g_messagingInterface->RegisterListener(0, "F4SE", MessageHandler); logger.error("Hooking Game\n"); DoHook(); logger.error("CBP Load Complete\n"); diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 216f02d..7227384 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -26,21 +26,21 @@ #include "Thing.h" #include "config.h" #include "SimObj.h" -#include "skse64/GameRTTI.h" -#include "skse64/GameForms.h" -#include "skse64/GameReferences.h" -#include "skse64/NiTypes.h" -#include "skse64/NiNodes.h" -#include "skse64/NiGeometry.h" -#include "skse64/GameThreads.h" -#include "skse64/PluginAPI.h" -#include "skse64/GameStreams.h" +#include "f4se/GameRTTI.h" +#include "f4se/GameForms.h" +#include "f4se/GameReferences.h" +#include "f4se/NiTypes.h" +#include "f4se/NiNodes.h" +#include "f4se/BSGeometry.h" +#include "f4se/GameThreads.h" +#include "f4se/PluginAPI.h" +#include "f4se/GameStreams.h" #pragma warning(disable : 4996) -extern SKSETaskInterface *g_task; +extern F4SETaskInterface *g_task; //void UpdateWorldDataToChild(NiAVObject) @@ -83,13 +83,13 @@ std::string spaces(int n) { bool printStuff(NiAVObject *avObj, int depth) { std::string sss = spaces(depth); const char *ss = sss.c_str(); - logger.info("%savObj Name = %s, RTTI = %s\n", ss, avObj->m_name, avObj->GetRTTI()->name); + //logger.info("%savObj Name = %s, RTTI = %s\n", ss, avObj->m_name, avObj->GetRTTI()->name); - NiNode *node = avObj->GetAsNiNode(); - if (node) { - logger.info("%snode %s, RTTI %s\n", ss, node->m_name, node->GetRTTI()->name); - } - return false; + //NiNode *node = avObj->GetAsNiNode(); + //if (node) { + // logger.info("%snode %s, RTTI %s\n", ss, node->m_name, node->GetRTTI()->name); + //} + //return false; } @@ -133,7 +133,7 @@ void updateActors() { //logger.error("scan Cell\n"); auto player = DYNAMIC_CAST(LookupFormByID(0x14), TESForm, Actor); - if (!player || !player->loadedState) goto FAILED; + if (!player || !player->unkF0) goto FAILED; auto cell = player->parentCell; if (!cell) goto FAILED; @@ -143,14 +143,16 @@ void updateActors() { curCell = cell; actors.clear(); } else { - for (int i = 0; i < cell->refData.maxSize; i++) { - auto ref = cell->refData.refArray[i]; - if (ref.unk08 != NULL && ref.ref) { - auto actor = DYNAMIC_CAST(ref.ref, TESObjectREFR, Actor); - if (actor && actor->loadedState) { + // Attempt to get cell's objects + for (int i = 0; i < cell->objectList.count; i++) { + auto ref = cell->objectList[i]; + if (ref) { + // Attempt to get actors + auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); + if (actor && actor->unkF0) { auto soIt = actors.find(actor->formID); - if (soIt == actors.end()) { - //logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); + if (soIt == actors.end() && actor->formID == 0x14) { + logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); auto obj = SimObj(actor, config); if (obj.actorValid(actor)) { actors.emplace(actor->formID, obj); @@ -162,6 +164,25 @@ void updateActors() { } } } + //for (int i = 0; i < cell->refData.maxSize; i++) { + // auto ref = cell->refData.refArray[i]; + // if (ref.unk08 != NULL && ref.ref) { + // auto actor = DYNAMIC_CAST(ref.ref, TESObjectREFR, Actor); + // if (actor && actor->unkF0) { + // auto soIt = actors.find(actor->formID); + // if (soIt == actors.end()) { + // //logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); + // auto obj = SimObj(actor, config); + // if (obj.actorValid(actor)) { + // actors.emplace(actor->formID, obj); + // actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); + // } + // } else if (soIt->second.actorValid(actor)) { + // actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); + // } + // } + // } + //} } //static bool done = false; @@ -186,7 +207,7 @@ void updateActors() { a.second.updateConfig(config); } } - //logger.error("Updating %d entites\n", actorEntries.size()); + logger.error("Updating %d entites\n", actorEntries.size()); for (auto &a : actorEntries) { auto objIt = actors.find(a.id); if (objIt == actors.end()) { @@ -211,7 +232,7 @@ void updateActors() { } -class ScanDelegate : public TaskDelegate { +class ScanDelegate : public ITaskDelegate { public: virtual void Run() { updateActors(); diff --git a/common/IArchive.cpp b/common/IArchive.cpp new file mode 100644 index 0000000..268bd42 --- /dev/null +++ b/common/IArchive.cpp @@ -0,0 +1,102 @@ +#include "IArchive.h" +#include "IErrors.h" + +#if ENABLE_IDYNAMICCREATE + +IDynamic * IArchive::iterator::Instantiate(void) +{ + IDataSubStream subStream(owner->theStream, GetDataOffset(), GetDataLength()); + + return IClassRegistry::Instantiate(GetTypeID(), &subStream); +} + +void * IArchive::iterator::GetBuffer(UInt32 * outLength) +{ + HeaderEntry * entry = GetData(); + UInt8 * buf = new UInt8[entry->dataLength]; + + owner->theStream->SetOffset(entry->dataOffset); + owner->theStream->ReadBuf(buf, entry->dataLength); + + if(outLength) + *outLength = entry->dataLength; + + return buf; +} + +void IArchive::iterator::NextOfType(UInt32 typeID) +{ + idx++; + + while((GetData()->typeID != typeID) && (idx < owner->header.numEntries)) + idx++; +} + +void IArchive::iterator::PrevOfType(UInt32 typeID) +{ + idx--; + + while((GetData()->typeID != typeID) && (idx > 0)) + idx--; +} + +IArchive::IArchive() +:theStream(NULL), entries(NULL), nameTable(NULL) +{ + +} + +IArchive::IArchive(IDataStream * stream) +:theStream(NULL), entries(NULL), nameTable(NULL) +{ + AttachStream(stream); +} + +IArchive::~IArchive() +{ + Dispose(); +} + +void IArchive::AttachStream(IDataStream * inStream) +{ + Dispose(); + + theStream = inStream; +} + +void IArchive::Dispose(void) +{ + if(entries) + { + delete entries; + entries = NULL; + } + + if(nameTable) + { + delete nameTable; + nameTable = NULL; + } +} + +void IArchive::ReadHeader(void) +{ + ASSERT(theStream); + + theStream->Rewind(); + + theStream->ReadBuf(&header, sizeof(FileHeader)); + + entries = new HeaderEntry[header.numEntries]; + theStream->ReadBuf(entries, header.numEntries * sizeof(HeaderEntry)); + + if(header.nameTableLength) + { + nameTable = new char[header.nameTableLength]; + + theStream->SetOffset(header.nameTableOffset); + theStream->ReadBuf(nameTable, header.nameTableLength); + } +} + +#endif diff --git a/common/IArchive.h b/common/IArchive.h new file mode 100644 index 0000000..261a344 --- /dev/null +++ b/common/IArchive.h @@ -0,0 +1,95 @@ +#pragma once + +#include "common/IDataStream.h" +#include "common/IDynamicCreate.h" + +#if ENABLE_IDYNAMICCREATE + +/** + * An object archive + * + * This class implements reading and instantiating objects from an object archive. + */ +class IArchive +{ + public: + class iterator; + friend iterator; + + IArchive(); + IArchive(IDataStream * inStream); + ~IArchive(); + + void AttachStream(IDataStream * inStream); + void Dispose(void); + + iterator begin(void) { return iterator(0, this); } + iterator end(void) { return iterator(header.numEntries, this); } + + static const UInt32 kFileID = CHAR_CODE(0x00, 'A', 'R', 0x01); + static const UInt32 kCurrentVersion = VERSION_CODE(1, 0, 0); + + private: + struct FileHeader + { + UInt32 fileID; // IArchive::kFileID + UInt32 version; // IArchive::kCurrentVersion + UInt32 numEntries; + UInt32 nameTableOffset; + UInt32 nameTableLength; + }; + + struct HeaderEntry + { + UInt32 typeID; + UInt32 subID; + UInt32 dataOffset; + UInt32 dataLength; + UInt32 nameOffset; + }; + + void ReadHeader(void); + + IDataStream * theStream; + + FileHeader header; + HeaderEntry * entries; + + char * nameTable; + + public: + class iterator + { + public: + iterator() { idx = 0; owner = NULL; } + iterator(UInt32 inIdx, IArchive * inArchive) { idx = inIdx; owner = inArchive; } + ~iterator() { } + + IDynamic * Instantiate(void); + + UInt32 GetTypeID(void) { return GetData()->typeID; } + UInt32 GetSubID(void) { return GetData()->subID; } + UInt32 GetDataLength(void) { return GetData()->dataLength; } + char * GetName(void) { return &owner->nameTable[GetData()->nameOffset]; } + void * GetBuffer(UInt32 * outLength); + + iterator & operator++() { Next(); return *this; } + iterator & operator--() { Prev(); return *this; } + + void NextOfType(UInt32 typeID); + void Next(void) { idx++; } + + void PrevOfType(UInt32 typeID); + void Prev(void) { idx--; } + + private: + HeaderEntry * GetData(void) { return &owner->entries[idx]; } + + UInt32 GetDataOffset(void) { return GetData()->dataOffset; } + + UInt32 idx; + IArchive * owner; + }; +}; + +#endif diff --git a/common/IBufferStream.cpp b/common/IBufferStream.cpp new file mode 100644 index 0000000..dd68a6b --- /dev/null +++ b/common/IBufferStream.cpp @@ -0,0 +1,58 @@ +#include "IBufferStream.h" + +IBufferStream::IBufferStream() +:streamBuf(NULL), flags(0) +{ + +} + +IBufferStream::IBufferStream(const IBufferStream & rhs) +{ + // explicitly not supporting copy constructor for self-owned buffers + ASSERT((flags & kFlag_OwnedBuf) == 0); +} + +IBufferStream::IBufferStream(void * buf, UInt64 inLength) +:streamBuf(NULL), flags(0) +{ + SetBuffer(buf, inLength); +} + +IBufferStream::~IBufferStream() +{ + if(flags & kFlag_OwnedBuf) + { + delete [] streamBuf; + } +} + +IBufferStream & IBufferStream::operator=(IBufferStream & rhs) +{ + // explicitly not supporting copying for self-owned buffers + ASSERT((flags & kFlag_OwnedBuf) == 0); + + streamBuf = rhs.streamBuf; + flags = rhs.flags; + + return *this; +} + +void IBufferStream::SetBuffer(void * buf, UInt64 inLength) +{ + streamBuf = (UInt8 *)buf; + streamLength = inLength; + + Rewind(); +} + +void IBufferStream::ReadBuf(void * buf, UInt32 inLength) +{ + memcpy(buf, &streamBuf[streamOffset], inLength); + streamOffset += inLength; +} + +void IBufferStream::WriteBuf(const void * buf, UInt32 inLength) +{ + memcpy(&streamBuf[streamOffset], buf, inLength); + streamOffset += inLength; +} diff --git a/common/IBufferStream.h b/common/IBufferStream.h new file mode 100644 index 0000000..9e20aba --- /dev/null +++ b/common/IBufferStream.h @@ -0,0 +1,35 @@ +#pragma once + +#include "common/IDataStream.h" + +class IBufferStream : public IDataStream +{ + public: + IBufferStream(); + IBufferStream(const IBufferStream & rhs); + IBufferStream(void * buf, UInt64 inLength); + virtual ~IBufferStream(); + + IBufferStream & operator=(IBufferStream & rhs); + + void SetBuffer(void * buf, UInt64 inLength); + void * GetBuffer(void) { return streamBuf; } + + void OwnBuffer(void) { flags |= kFlag_OwnedBuf; } + void DisownBuffer(void) { flags &= ~kFlag_OwnedBuf; } + + // read + virtual void ReadBuf(void * buf, UInt32 inLength); + + // write + virtual void WriteBuf(const void * buf, UInt32 inLength); + + protected: + UInt8 * streamBuf; + UInt32 flags; + + enum + { + kFlag_OwnedBuf = 1 << 0 + }; +}; diff --git a/common/IConsole.cpp b/common/IConsole.cpp new file mode 100644 index 0000000..c638577 --- /dev/null +++ b/common/IConsole.cpp @@ -0,0 +1,116 @@ +#include "common/IConsole.h" +#include +#include +#include + +IConsole::IConsole() +{ + AllocConsole(); + + SetConsoleTitle("Console"); + + inputHandle = GetStdHandle(STD_INPUT_HANDLE); + outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); + + ASSERT_STR(inputHandle, "IConsole: couldn't get input handle"); + ASSERT_STR(outputHandle, "IConsole: couldn't get output handle"); + + SetConsoleMode(inputHandle, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT); + SetConsoleMode(outputHandle, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); +} + +IConsole::~IConsole() +{ + +} + +/** + * Writes a string to the console + */ +void IConsole::Write(char * buf) +{ + UInt32 charsWritten; + + WriteConsole(outputHandle, buf, std::strlen(buf), &charsWritten, NULL); +} + +/** + * Writes a formatted string to the console + * + * You may specify a temp buffer to use for the formatted text, but if NULL + * is used a local buffer will be provided. + * + * @param buf a temporary buffer, or NULL to use the internal buffer + * @param fmt the format string + */ +void IConsole::Write(char * buf, UInt32 bufLen, const char * fmt, ...) +{ + static char tempBuf[4096]; + + if(!buf) + { + buf = tempBuf; + bufLen = sizeof(tempBuf); + } + + va_list args; + + va_start(args, fmt); + vsprintf_s(buf, bufLen, fmt, args); + va_end(args); + + Write(buf); +} + +/** + * Reads a single character from the console + */ +char IConsole::ReadChar(void) +{ + char data; + UInt32 charsRead; + + ReadConsole(inputHandle, &data, 1, &charsRead, NULL); + + return data; +} + +/** + * Reads a newline-terminated string from the console + * + * @param buf output buffer + * @param len buffer size + * @return number of characters read + */ +UInt32 IConsole::ReadBuf(char * buf, UInt32 len) +{ + UInt32 charsRead; + + buf[0] = 0; + + do + { + ReadConsole(inputHandle, buf, len, &charsRead, NULL); + } + while(!charsRead); + + int done = 0; + for(UInt32 i = charsRead - 1; (i > 0) && !done; i--) + { + switch(buf[i]) + { + case 0x0A: + case 0x0D: + buf[i] = 0; + break; + + default: + done = 1; + break; + } + } + + buf[charsRead] = 0; + + return charsRead; +} diff --git a/common/IConsole.h b/common/IConsole.h new file mode 100644 index 0000000..70c0c87 --- /dev/null +++ b/common/IConsole.h @@ -0,0 +1,26 @@ +#pragma once + +#include "common/ITypes.h" +#include "common/ISingleton.h" +#include + +/** + * Wrapper class for a standard Windows console + * + * @todo make nonblocking + */ +class IConsole : public ISingleton +{ + public: + IConsole(); + ~IConsole(); + + void Write(char * buf); + void Write(char * buf, UInt32 bufLen, const char * fmt, ...); + + char ReadChar(void); + UInt32 ReadBuf(char * buf, UInt32 len); + + private: + HANDLE inputHandle, outputHandle; +}; diff --git a/common/ICriticalSection.h b/common/ICriticalSection.h new file mode 100644 index 0000000..84b405d --- /dev/null +++ b/common/ICriticalSection.h @@ -0,0 +1,35 @@ +#pragma once + +class ICriticalSection +{ + public: + ICriticalSection() { InitializeCriticalSection(&critSection); } + ~ICriticalSection() { DeleteCriticalSection(&critSection); } + + void Enter(void) { EnterCriticalSection(&critSection); } + void Leave(void) { LeaveCriticalSection(&critSection); } + bool TryEnter(void) { return TryEnterCriticalSection(&critSection) != 0; } + + private: + CRITICAL_SECTION critSection; +}; + +class IScopedCriticalSection +{ +public: + IScopedCriticalSection(ICriticalSection * cs) + :m_cs(cs) + { + m_cs->Enter(); + } + + ~IScopedCriticalSection() + { + m_cs->Leave(); + } + +private: + IScopedCriticalSection(); // undefined + + ICriticalSection * m_cs; +}; diff --git a/common/IDataStream.cpp b/common/IDataStream.cpp new file mode 100644 index 0000000..51fda67 --- /dev/null +++ b/common/IDataStream.cpp @@ -0,0 +1,472 @@ +#include "IDataStream.h" + +/**** IDataStream *************************************************************/ + +IDataStream::IDataStream() +:streamLength(0), streamOffset(0), swapBytes(false) +{ + +} + +IDataStream::~IDataStream() +{ + +} + +/** + * Reads and returns an 8-bit value from the stream + */ +UInt8 IDataStream::Read8(void) +{ + UInt8 out; + + ReadBuf(&out, sizeof(UInt8)); + + return out; +} + +/** + * Reads and returns a 16-bit value from the stream + */ +UInt16 IDataStream::Read16(void) +{ + UInt16 out; + + ReadBuf(&out, sizeof(UInt16)); + + if(swapBytes) + out = Swap16(out); + + return out; +} + +/** + * Reads and returns a 32-bit value from the stream + */ +UInt32 IDataStream::Read32(void) +{ + UInt32 out; + + ReadBuf(&out, sizeof(UInt32)); + + if(swapBytes) + out = Swap32(out); + + return out; +} + +/** + * Reads and returns a 64-bit value from the stream + */ +UInt64 IDataStream::Read64(void) +{ + UInt64 out; + + ReadBuf(&out, sizeof(UInt64)); + + if(swapBytes) + out = Swap64(out); + + return out; +} + +/** + * Reads and returns a 32-bit floating point value from the stream + */ +float IDataStream::ReadFloat(void) +{ + UInt32 out = Read32(); + + return *((float *)&out); +} + +/** + * Reads a null-or-return-terminated string from the stream + * + * If the buffer is too small to hold the entire string, it is truncated and + * properly terminated. + * + * @param buf the output buffer + * @param bufLength the size of the output buffer + * @return the number of characters written to the buffer + */ +UInt32 IDataStream::ReadString(char * buf, UInt32 bufLength, char altTerminator, char altTerminator2) +{ + char * traverse = buf; + bool breakOnReturns = false; + + if((altTerminator == '\n') || (altTerminator2 == '\n')) + breakOnReturns = true; + + ASSERT_STR(bufLength > 0, "IDataStream::ReadString: zero-sized buffer"); + + if(bufLength == 1) + { + buf[0] = 0; + return 0; + } + + bufLength--; + + for(UInt32 i = 0; i < bufLength; i++) + { + if(HitEOF()) break; + + UInt8 data = Read8(); + + if(breakOnReturns) + { + if(data == 0x0D) + { + if(Peek8() == 0x0A) + Skip(1); + + break; + } + } + + if(!data || (data == altTerminator) || (data == altTerminator2)) + { + break; + } + + *traverse++ = data; + } + + *traverse++ = 0; + + return traverse - buf - 1; +} + +/** + * Reads and returns an 8-bit value from the stream without advancing the stream's position + */ +UInt8 IDataStream::Peek8(void) +{ + IDataStream_PositionSaver saver(this); + + return Read8(); +} + +/** + * Reads and returns a 16-bit value from the stream without advancing the stream's position + */ +UInt16 IDataStream::Peek16(void) +{ + IDataStream_PositionSaver saver(this); + + return Read16(); +} + +/** + * Reads and returns a 32-bit value from the stream without advancing the stream's position + */ +UInt32 IDataStream::Peek32(void) +{ + IDataStream_PositionSaver saver(this); + + return Read32(); +} + +/** + * Reads and returns a 32-bit value from the stream without advancing the stream's position + */ +UInt64 IDataStream::Peek64(void) +{ + IDataStream_PositionSaver saver(this); + + return Read64(); +} + +/** + * Reads and returns a 32-bit floating point value from the stream without advancing the stream's position + */ +float IDataStream::PeekFloat(void) +{ + IDataStream_PositionSaver saver(this); + + return ReadFloat(); +} + +/** + * Reads raw data into a buffer without advancing the stream's position + */ +void IDataStream::PeekBuf(void * buf, UInt32 inLength) +{ + IDataStream_PositionSaver saver(this); + + ReadBuf(buf, inLength); +} + +/** + * Skips a specified number of bytes down the stream + */ +void IDataStream::Skip(SInt64 inBytes) +{ + SetOffset(GetOffset() + inBytes); +} + +/** + * Writes an 8-bit value to the stream. + */ +void IDataStream::Write8(UInt8 inData) +{ + WriteBuf(&inData, sizeof(UInt8)); +} + +/** + * Writes a 16-bit value to the stream. + */ +void IDataStream::Write16(UInt16 inData) +{ + if(swapBytes) + inData = Swap16(inData); + + WriteBuf(&inData, sizeof(UInt16)); +} + +/** + * Writes a 32-bit value to the stream. + */ +void IDataStream::Write32(UInt32 inData) +{ + if(swapBytes) + inData = Swap32(inData); + + WriteBuf(&inData, sizeof(UInt32)); +} + +/** + * Writes a 64-bit value to the stream. + */ +void IDataStream::Write64(UInt64 inData) +{ + if(swapBytes) + inData = Swap64(inData); + + WriteBuf(&inData, sizeof(UInt64)); +} + +/** + * Writes a 32-bit floating point value to the stream. + */ +void IDataStream::WriteFloat(float inData) +{ + if(swapBytes) + { + UInt32 temp = *((UInt32 *)&inData); + + temp = Swap32(temp); + + WriteBuf(&temp, sizeof(UInt32)); + } + else + { + WriteBuf(&inData, sizeof(float)); + } +} + +/** + * Writes a null-terminated string to the stream. + */ +void IDataStream::WriteString(const char * buf) +{ + WriteBuf(buf, std::strlen(buf) + 1); +} + +/** + * Returns the length of the stream + */ +SInt64 IDataStream::GetLength(void) +{ + return streamLength; +} + +/** + * Returns the number of bytes remaining in the stream + */ +SInt64 IDataStream::GetRemain(void) +{ + return streamLength - streamOffset; +} + +/** + * Returns the current offset into the stream + */ +SInt64 IDataStream::GetOffset(void) +{ + return streamOffset; +} + +/** + * Returns whether we have reached the end of the stream or not + */ +bool IDataStream::HitEOF(void) +{ + return streamOffset >= streamLength; +} + +/** + * Moves the current offset into the stream + */ +void IDataStream::SetOffset(SInt64 inOffset) +{ + streamOffset = inOffset; +} + +/** + * Enables or disables byte swapping for basic data transfers + */ +void IDataStream::SwapBytes(bool inSwapBytes) +{ + swapBytes = inSwapBytes; +} + +IDataStream * IDataStream::GetRootParent(void) +{ + IDataStream * parent = GetParent(); + + if(parent) + return parent->GetRootParent(); + else + return this; +} + +void IDataStream::CopyStreams(IDataStream * out, IDataStream * in, UInt64 bufferSize, UInt8 * buf) +{ + in->Rewind(); + + bool ourBuffer = false; + + if(!buf) + { + buf = new UInt8[bufferSize]; + ourBuffer = true; + } + + UInt64 remain = in->GetLength(); + + while(remain > 0) + { + UInt64 transferSize = remain; + + if(transferSize > bufferSize) + transferSize = bufferSize; + + in->ReadBuf(buf, transferSize); + out->WriteBuf(buf, transferSize); + + remain -= transferSize; + } + + if(ourBuffer) + delete [] buf; +} + +void IDataStream::CopySubStreams(IDataStream * out, IDataStream * in, UInt64 remain, UInt64 bufferSize, UInt8 * buf) +{ + bool ourBuffer = false; + + if(!buf) + { + buf = new UInt8[bufferSize]; + ourBuffer = true; + } + + while(remain > 0) + { + UInt64 transferSize = remain; + + if(transferSize > bufferSize) + transferSize = bufferSize; + + in->ReadBuf(buf, transferSize); + out->WriteBuf(buf, transferSize); + + remain -= transferSize; + } + + if(ourBuffer) + delete [] buf; +} + +/**** IDataStream_PositionSaver ***********************************************/ + +/** + * The constructor; save the stream's position + */ +IDataStream_PositionSaver::IDataStream_PositionSaver(IDataStream * tgt) +{ + stream = tgt; + offset = tgt->GetOffset(); +} + +/** + * The destructor; restore the stream's saved position + */ +IDataStream_PositionSaver::~IDataStream_PositionSaver() +{ + stream->SetOffset(offset); +} + +/**** IDataSubStream **********************************************************/ + +IDataSubStream::IDataSubStream() +:stream(NULL), subBase(0) +{ + // +} + +IDataSubStream::IDataSubStream(IDataStream * inStream, SInt64 inOffset, SInt64 inLength) +{ + stream = inStream; + subBase = inOffset; + streamLength = inLength; + + stream->SetOffset(inOffset); +} + +IDataSubStream::~IDataSubStream() +{ + +} + +void IDataSubStream::Attach(IDataStream * inStream, SInt64 inOffset, SInt64 inLength) +{ + stream = inStream; + subBase = inOffset; + streamLength = inLength; + + stream->SetOffset(inOffset); +} + +void IDataSubStream::ReadBuf(void * buf, UInt32 inLength) +{ + ASSERT_STR(inLength <= GetRemain(), "IDataSubStream::ReadBuf: hit eof"); + + if(stream->GetOffset() != subBase + streamOffset) + stream->SetOffset(subBase + streamOffset); + + stream->ReadBuf(buf, inLength); + + streamOffset += inLength; +} + +void IDataSubStream::WriteBuf(const void * buf, UInt32 inLength) +{ + if(stream->GetOffset() != subBase + streamOffset) + stream->SetOffset(subBase + streamOffset); + + stream->WriteBuf(buf, inLength); + + streamOffset += inLength; + + if(streamLength < streamOffset) + streamLength = streamOffset; +} + +void IDataSubStream::SetOffset(SInt64 inOffset) +{ + stream->SetOffset(subBase + inOffset); + streamOffset = inOffset; +} diff --git a/common/IDataStream.h b/common/IDataStream.h new file mode 100644 index 0000000..cca0805 --- /dev/null +++ b/common/IDataStream.h @@ -0,0 +1,102 @@ +#pragma once + +#include "common/IErrors.h" + +/** + * An arbitrary data stream + */ +class IDataStream +{ + public: + IDataStream(); + virtual ~IDataStream(); + + // read + virtual UInt8 Read8(void); + virtual UInt16 Read16(void); + virtual UInt32 Read32(void); + virtual UInt64 Read64(void); + virtual float ReadFloat(void); + virtual UInt32 ReadString(char * buf, UInt32 bufLength, char altTerminator = 0, char altTerminator2 = 0); + virtual void ReadBuf(void * buf, UInt32 inLength) = 0; + + // peek + virtual UInt8 Peek8(void); + virtual UInt16 Peek16(void); + virtual UInt32 Peek32(void); + virtual UInt64 Peek64(void); + virtual float PeekFloat(void); + virtual void PeekBuf(void * buf, UInt32 inLength); + + virtual void Skip(SInt64 inBytes); + + // write + virtual void Write8(UInt8 inData); + virtual void Write16(UInt16 inData); + virtual void Write32(UInt32 inData); + virtual void Write64(UInt64 inData); + virtual void WriteFloat(float inData); + virtual void WriteString(const char * buf); + virtual void WriteBuf(const void * buf, UInt32 inLength) = 0; + + SInt64 GetLength(void); + SInt64 GetRemain(void); + SInt64 GetOffset(void); + bool HitEOF(void); + + virtual void SetOffset(SInt64 inOffset); + void Rewind(void) { SetOffset(0); } + + void SwapBytes(bool inSwapBytes); + + virtual SInt64 GetParentOffset(void) { return GetOffset(); } + virtual IDataStream * GetParent(void) { return NULL; } + + IDataStream * GetRootParent(void); + + static void CopyStreams(IDataStream * out, IDataStream * in, UInt64 bufferSize = 1024 * 1024, UInt8 * buf = NULL); + static void CopySubStreams(IDataStream * out, IDataStream * in, UInt64 remain, UInt64 bufferSize = 1024 * 1024, UInt8 * buf = NULL); + + protected: + SInt64 streamLength; + SInt64 streamOffset; + bool swapBytes; +}; + +/** + * A utility class to automatically save and restore the current position of an IDataStream + */ +class IDataStream_PositionSaver +{ + public: + IDataStream_PositionSaver(IDataStream * tgt); + ~IDataStream_PositionSaver(); + + private: + IDataStream * stream; + SInt64 offset; +}; + +class IDataSubStream : public IDataStream +{ + public: + IDataSubStream(); + IDataSubStream(IDataStream * inStream, SInt64 inOffset, SInt64 inLength); + ~IDataSubStream(); + + void Attach(IDataStream * inStream, SInt64 inOffset, SInt64 inLength); + + void ReadBuf(void * buf, UInt32 inLength); + void WriteBuf(const void * buf, UInt32 inLength); + void SetOffset(SInt64 inOffset); + + virtual SInt64 GetParentOffset(void) { return stream->GetOffset(); } + virtual IDataStream * GetParent(void) { return stream; } + + SInt64 GetSubBase(void) { return subBase; } + + private: + IDataStream * stream; + + SInt64 subBase; +}; diff --git a/common/IDatabase.cpp b/common/IDatabase.cpp new file mode 100644 index 0000000..121a4f8 --- /dev/null +++ b/common/IDatabase.cpp @@ -0,0 +1 @@ +#include "IDatabase.h" diff --git a/common/IDatabase.h b/common/IDatabase.h new file mode 100644 index 0000000..3bc85de --- /dev/null +++ b/common/IDatabase.h @@ -0,0 +1,116 @@ +#pragma once + +#include +#include "common/IDataStream.h" +#include "common/IFilestream.h" + +template +class IDatabase +{ + public: + typedef std::map DataMapType; + typedef typename DataMapType::iterator DataMapIterator; + + static const UInt64 kGUIDMask = 0x0FFFFFFFFFFFFFFF; + + IDatabase() { newKeyHint = 1; } + virtual ~IDatabase() { } + + DataType * Get(UInt64 key) + { + key &= kGUIDMask; + + if(!key) + return NULL; + + DataMapType::iterator iter = theDataMap.find(key); + + return (iter == theDataMap.end()) ? NULL : &((*iter).second); + } + + DataType * Alloc(UInt64 key) + { + key &= kGUIDMask; + + if(!key) + return NULL; + + DataMapType::iterator iter = theDataMap.find(key); + + return (iter == theDataMap.end()) ? &theDataMap[key] : NULL; + } + + DataType * Alloc(UInt64 * key) + { + UInt64 newKey = newKeyHint; + + do + { + if(!newKey) + newKey++; + + DataMapType::iterator iter = theDataMap.find(newKey); + + // is 'newKey' unused? + if(iter == theDataMap.end()) + { + *key = newKey; + newKeyHint = (newKey + 1) & kGUIDMask; + return &theDataMap[newKey]; + } + else + { + ++iter; + if(iter == theDataMap.end()) + { + newKey = 1; + } + else + { + UInt64 nextKey = (newKey + 1) & kGUIDMask; + if(iter->first != nextKey) + { + *key = nextKey; + newKeyHint = (nextKey + 1) & kGUIDMask; + return &theDataMap[nextKey]; + } + } + } + } + while(1); + + *key = 0; + + return NULL; + } + + void Delete(UInt64 key) + { + if(key) + { + key &= kGUIDMask; + + theDataMap.erase(key); + + newKeyHint = key; + } + } + + void Save(IDataStream * stream); + void Load(IDataStream * stream); + + bool SaveToFile(char * name); + bool LoadFromFile(char * name); + + DataMapType & GetData(void) { return theDataMap; } + + DataMapIterator Begin(void) { return theDataMap.begin(); } + DataMapIterator End(void) { return theDataMap.end(); } + UInt32 Length(void) { return theDataMap.size(); } + + private: + DataMapType theDataMap; + UInt64 newKeyHint; +}; + +#include "common/IDatabase.inc" diff --git a/common/IDatabase.inc b/common/IDatabase.inc new file mode 100644 index 0000000..cdaa171 --- /dev/null +++ b/common/IDatabase.inc @@ -0,0 +1,55 @@ +template +void IDatabase ::Save(IDataStream * stream) +{ + stream->Write32(theDataMap.size()); + stream->Write64(newKeyHint); + + for(DataMapType::iterator iter = theDataMap.begin(); iter != theDataMap.end(); iter++) + { + stream->Write64((*iter).first); + stream->WriteBuf(&((*iter).second), sizeof(DataType)); + } +} + +template +void IDatabase ::Load(IDataStream * stream) +{ + UInt32 numEntries = stream->Read32(); + newKeyHint = stream->Read64(); + + theDataMap.clear(); + + for(UInt32 i = 0; i < numEntries; i++) + { + UInt64 key = stream->Read64(); + stream->ReadBuf(&(theDataMap[key]), sizeof(DataType)); + } +} + +template +bool IDatabase ::SaveToFile(char * name) +{ + IFileStream stream; + + if(stream.Create(name)) + { + Save(&stream); + return true; + } + + return false; +} + +template +bool IDatabase ::LoadFromFile(char * name) +{ + IFileStream stream; + + if(stream.Open(name)) + { + Load(&stream); + return true; + } + + return false; +} diff --git a/common/IDebugLog.cpp b/common/IDebugLog.cpp new file mode 100644 index 0000000..7f332a3 --- /dev/null +++ b/common/IDebugLog.cpp @@ -0,0 +1,324 @@ +#include "common/IDebugLog.h" +#include +#include "common/IFileStream.h" +#include + +std::FILE * IDebugLog::logFile = NULL; +char IDebugLog::sourceBuf[16] = { 0 }; +char IDebugLog::headerText[16] = { 0 }; +char IDebugLog::formatBuf[8192] = { 0 }; +int IDebugLog::indentLevel = 0; +int IDebugLog::rightMargin = 0; +int IDebugLog::cursorPos = 0; +int IDebugLog::inBlock = 0; +bool IDebugLog::autoFlush = true; +IDebugLog::LogLevel IDebugLog::logLevel = IDebugLog::kLevel_DebugMessage; +IDebugLog::LogLevel IDebugLog::printLevel = IDebugLog::kLevel_Message; + +IDebugLog::IDebugLog() +{ + // +} + +IDebugLog::IDebugLog(const char * name) +{ + Open(name); +} + +IDebugLog::~IDebugLog() +{ + if(logFile) + fclose(logFile); +} + +void IDebugLog::Open(const char * path) +{ + logFile = _fsopen(path, "w", _SH_DENYWR); + + if(!logFile) + { + UInt32 id = 0; + char name[1024]; + + do + { + sprintf_s(name, sizeof(name), "%s%d", path, id); + id++; + + logFile = NULL; + logFile = _fsopen(name, "w", _SH_DENYWR); + } + while(!logFile && (id < 5)); + } +} + +void IDebugLog::OpenRelative(int folderID, const char * relPath) +{ + char path[MAX_PATH]; + + HRESULT err = SHGetFolderPath(NULL, folderID | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, path); + if(!SUCCEEDED(err)) + { + _FATALERROR("SHGetFolderPath %08X failed (result = %08X lasterr = %08X)", folderID, err, GetLastError()); + } + ASSERT_CODE(SUCCEEDED(err), err); + + strcat_s(path, sizeof(path), relPath); + + IFileStream::MakeAllDirs(path); + + Open(path); +} + +/** + * Output a non-formatted message to the log file + * + * @param message the message + * @param source the source of the message, or NULL to use the previous source + */ +void IDebugLog::Message(const char * message, const char * source, bool newLine) +{ + if(source) + SetSource(source); + + if(inBlock) + { + SeekCursor(RoundToTab((indentLevel * 4) + strlen(headerText))); + } + else + { + SeekCursor(indentLevel * 4); + + PrintText(headerText); + } + + PrintText(message); + + if(newLine) + NewLine(); +} + +/** + * Output a formatted message to the log file + * + * @note It is impossible to set the source of a formatted message. + * The previous source will be used. + */ +void IDebugLog::FormattedMessage(const char * fmt, ...) +{ + va_list argList; + + va_start(argList, fmt); + vsprintf_s(formatBuf, sizeof(formatBuf), fmt, argList); + Message(formatBuf); + va_end(argList); +} + +/** + * Output a formatted message to the log file + * + * @note It is impossible to set the source of a formatted message. + * The previous source will be used. + */ +void IDebugLog::FormattedMessage(const char * fmt, va_list args) +{ + vsprintf_s(formatBuf, sizeof(formatBuf), fmt, args); + Message(formatBuf); +} + +void IDebugLog::Log(LogLevel level, const char * fmt, va_list args) +{ + bool log = (level <= logLevel); + bool print = (level <= printLevel); + + if(log || print) + vsprintf_s(formatBuf, sizeof(formatBuf), fmt, args); + + if(log) + Message(formatBuf); + + if(print) + printf("%s\n", formatBuf); +} + +void IDebugLog::LogNNL(LogLevel level, const char * fmt, va_list args) +{ + bool log = (level <= logLevel); + bool print = (level <= printLevel); + + if(log || print) + vsprintf_s(formatBuf, sizeof(formatBuf), fmt, args); + + if(log) + Message(formatBuf, NULL, false); + + if(print) + printf("%s", formatBuf); +} + +/** + * Set the current message source + */ +void IDebugLog::SetSource(const char * source) +{ + strcpy_s(sourceBuf, sizeof(sourceBuf), source); + strcpy_s(headerText, sizeof(headerText), "[ ]\t"); + + char * tgt = headerText + 1; + char * src = sourceBuf; + + for(int i = 0; (i < 8) && *src; i++, tgt++, src++) + *tgt = *src; +} + +/** + * Clear the current message source + */ +void IDebugLog::ClearSource(void) +{ + sourceBuf[0] = 0; +} + +/** + * Increase the indentation level + */ +void IDebugLog::Indent(void) +{ + indentLevel++; +} + +/** + * Decrease the indentation level + */ +void IDebugLog::Outdent(void) +{ + if(indentLevel) + indentLevel--; +} + +/** + * Enter a logical block + */ +void IDebugLog::OpenBlock(void) +{ + SeekCursor(indentLevel * 4); + + PrintText(headerText); + + inBlock = 1; +} + +/** + * Close a logical block + */ +void IDebugLog::CloseBlock(void) +{ + inBlock = 0; +} + +/** + * Enable/disable autoflush + * + * @param inAutoFlush autoflush state + */ +void IDebugLog::SetAutoFlush(bool inAutoFlush) +{ + autoFlush = inAutoFlush; +} + +/** + * Print spaces to the log + * + * If possible, tabs are used instead of spaces. + */ +void IDebugLog::PrintSpaces(int numSpaces) +{ + int originalNumSpaces = numSpaces; + + if(logFile) + { + while(numSpaces > 0) + { + if(numSpaces >= TabSize()) + { + numSpaces -= TabSize(); + fputc('\t', logFile); + } + else + { + numSpaces--; + fputc(' ', logFile); + } + } + } + + cursorPos += originalNumSpaces; +} + +/** + * Prints raw text to the log file + */ +void IDebugLog::PrintText(const char * buf) +{ + if(logFile) + { + fputs(buf, logFile); + if(autoFlush) + fflush(logFile); + } + + const char * traverse = buf; + char data; + + while(data = *traverse++) + { + if(data == '\t') + cursorPos += TabSize(); + else + cursorPos++; + } +} + +/** + * Moves to the next line of the log file + */ +void IDebugLog::NewLine(void) +{ + if(logFile) + { + fputc('\n', logFile); + + if(autoFlush) + fflush(logFile); + } + + cursorPos = 0; +} + +/** + * Prints spaces to align the cursor to the requested position + * + * @note The cursor move will not be performed if the request would move the cursor + * backwards. + */ +void IDebugLog::SeekCursor(int position) +{ + if(position > cursorPos) + PrintSpaces(position - cursorPos); +} + +/** + * Returns the number of spaces a tab would occupy at the current cursor position + */ +int IDebugLog::TabSize(void) +{ + return ((~cursorPos) & 3) + 1; +} + +/** + * Rounds a number of spaces to the nearest tab + */ +int IDebugLog::RoundToTab(int spaces) +{ + return (spaces + 3) & ~3; +} diff --git a/common/IDebugLog.h b/common/IDebugLog.h new file mode 100644 index 0000000..31cc36b --- /dev/null +++ b/common/IDebugLog.h @@ -0,0 +1,133 @@ +#pragma once + +#include + +/** + * A simple debug log file + * + * This class supports prefix blocks describing the source of the log event. + * It also allows logical blocks and outlining.\n + */ +class IDebugLog +{ + public: + IDebugLog(); + IDebugLog(const char * name); + ~IDebugLog(); + + static void Open(const char * path); + static void OpenRelative(int folderID, const char * relPath); + + static void Message(const char * message, const char * source = NULL, bool newLine = true); + static void FormattedMessage(const char * fmt, ...); + static void FormattedMessage(const char * fmt, va_list args); + + enum LogLevel + { + kLevel_FatalError = 0, + kLevel_Error, + kLevel_Warning, + kLevel_Message, + kLevel_VerboseMessage, + kLevel_DebugMessage + }; + + static void Log(LogLevel level, const char * fmt, va_list args); + static void LogNNL(LogLevel level, const char * fmt, va_list args); // No new line + + static void SetSource(const char * source); + static void ClearSource(void); + + static void Indent(void); + static void Outdent(void); + + static void OpenBlock(void); + static void CloseBlock(void); + + static void SetAutoFlush(bool inAutoFlush); + + static void SetLogLevel(LogLevel in) { logLevel = in; } + static void SetPrintLevel(LogLevel in) { printLevel = in; } + + private: + static void PrintSpaces(int numSpaces); + static void PrintText(const char * buf); + static void NewLine(void); + + static void SeekCursor(int position); + + static int TabSize(void); + static int RoundToTab(int spaces); + + static FILE * logFile; //!< the output file + + static char sourceBuf[16]; //!< name of current source, used in prefix + static char headerText[16]; //!< current text to use as line prefix + static char formatBuf[8192]; //!< temp buffer used for formatted messages + + static int indentLevel; //!< the current indentation level (in tabs) + static int rightMargin; //!< the column at which text should be wrapped + static int cursorPos; //!< current cursor position + static int inBlock; //!< are we in a block? + + static bool autoFlush; //!< automatically flush the file after writing + + static LogLevel logLevel; //!< least important log level to write + static LogLevel printLevel; //!< least important log level to print +}; + +extern IDebugLog gLog; + +inline void _FATALERROR(const char * fmt, ...) +{ + va_list args; + + va_start(args, fmt); + gLog.Log(IDebugLog::kLevel_FatalError, fmt, args); + va_end(args); +} + +inline void _ERROR(const char * fmt, ...) +{ + va_list args; + + va_start(args, fmt); + gLog.Log(IDebugLog::kLevel_Error, fmt, args); + va_end(args); +} + +inline void _WARNING(const char * fmt, ...) +{ + va_list args; + + va_start(args, fmt); + gLog.Log(IDebugLog::kLevel_Warning, fmt, args); + va_end(args); +} + +inline void _MESSAGE(const char * fmt, ...) +{ + va_list args; + + va_start(args, fmt); + gLog.Log(IDebugLog::kLevel_Message, fmt, args); + va_end(args); +} + +inline void _VMESSAGE(const char * fmt, ...) +{ + va_list args; + + va_start(args, fmt); + gLog.Log(IDebugLog::kLevel_VerboseMessage, fmt, args); + va_end(args); +} + +inline void _DMESSAGE(const char * fmt, ...) +{ + va_list args; + + va_start(args, fmt); + gLog.Log(IDebugLog::kLevel_DebugMessage, fmt, args); + va_end(args); +} diff --git a/common/IDirectoryIterator.cpp b/common/IDirectoryIterator.cpp new file mode 100644 index 0000000..b815f13 --- /dev/null +++ b/common/IDirectoryIterator.cpp @@ -0,0 +1,45 @@ +#include "IDirectoryIterator.h" +#include + +IDirectoryIterator::IDirectoryIterator(const char * path, const char * match) +:m_searchHandle(INVALID_HANDLE_VALUE), m_done(false) +{ + if(!match) match = "*"; + + strcpy_s(m_path, sizeof(m_path), path); + + char wildcardPath[MAX_PATH]; + sprintf_s(wildcardPath, sizeof(wildcardPath), "%s\\%s", path, match); + + m_searchHandle = FindFirstFile(wildcardPath, &m_result); + if(m_searchHandle == INVALID_HANDLE_VALUE) + m_done = true; +} + +IDirectoryIterator::~IDirectoryIterator() +{ + if(m_searchHandle != INVALID_HANDLE_VALUE) + FindClose(m_searchHandle); +} + +void IDirectoryIterator::GetFullPath(char * out, UInt32 outLen) +{ + sprintf_s(out, outLen, "%s\\%s", m_path, m_result.cFileName); +} + +std::string IDirectoryIterator::GetFullPath(void) +{ + return std::string(m_path) + "\\" + std::string(m_result.cFileName); +} + +void IDirectoryIterator::Next(void) +{ + BOOL result = FindNextFile(m_searchHandle, &m_result); + if(!result) + m_done = true; +} + +bool IDirectoryIterator::Done(void) +{ + return m_done; +} diff --git a/common/IDirectoryIterator.h b/common/IDirectoryIterator.h new file mode 100644 index 0000000..7759c21 --- /dev/null +++ b/common/IDirectoryIterator.h @@ -0,0 +1,24 @@ +#pragma once + +class IDirectoryIterator +{ +public: + IDirectoryIterator(const char * path, const char * match = NULL); + virtual ~IDirectoryIterator(); + + WIN32_FIND_DATA * Get(void) { return &m_result; } + void GetFullPath(char * out, UInt32 outLen); + std::string GetFullPath(void); + + void Next(void); + bool Done(void); + +private: + IDirectoryIterator(); // undefined, disallow + + HANDLE m_searchHandle; + WIN32_FIND_DATA m_result; + bool m_done; + + char m_path[MAX_PATH]; +}; diff --git a/common/IDynamicCreate.cpp b/common/IDynamicCreate.cpp new file mode 100644 index 0000000..44467b3 --- /dev/null +++ b/common/IDynamicCreate.cpp @@ -0,0 +1,38 @@ +#include "IDynamicCreate.h" + +#if ENABLE_IDYNAMICCREATE + +IClassRegistry _gClassRegistry; + +IClassRegistry::IClassRegistry() +{ + // +} + +IClassRegistry::~IClassRegistry() +{ + // +} + +void IClassRegistry::RegisterClassInfo(UInt32 id, IDynamicType * typeInfo) +{ + theClassRegistry[id] = typeInfo; +} + +IDynamicType * IClassRegistry::LookupClassInfo(UInt32 id) +{ + ClassRegistryType::iterator iter = theClassRegistry.find(id); + + return (iter == theClassRegistry.end()) ? NULL : (*iter).second; +} + +IDynamicType * IClassRegistry::LookupClassInfo(char * name) +{ + for(ClassRegistryType::iterator iter = theClassRegistry.begin(); iter != theClassRegistry.end(); iter++) + if(!strcmp((*iter).second->GetName(), name)) + return (*iter).second; + + return NULL; +} + +#endif diff --git a/common/IDynamicCreate.h b/common/IDynamicCreate.h new file mode 100644 index 0000000..7d58374 --- /dev/null +++ b/common/IDynamicCreate.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include "common/IDataStream.h" +#include "common/IErrors.h" + +// this screws with edit-and-continue and we don't use it +#define ENABLE_IDYNAMICCREATE 0 + +#if ENABLE_IDYNAMICCREATE + +//! Get a pointer to the IDynamicType for a class. +//! @note This is not a function; the parameter must be constant. +#define GetDynType(name) (&(##name##::__DYN_DynamicType)) + +//! Declare the members used for dynamic class creation +#define DYNAMIC_DECLARE(name) \ +public: \ + class __DYN_##name##_DynamicType : public IDynamicType \ + { \ + public: \ + __DYN_##name##_DynamicType() { } \ + ~__DYN_##name##_DynamicType() { } \ + \ + virtual IDynamic * Create(void) { return new name; } \ + virtual char * GetName(void) { return #name; } \ + virtual IDynamic * Instantiate(IDataStream * stream); \ + }; \ + \ + static __DYN_##name##_DynamicType __DYN_DynamicType; \ + virtual IDynamicType * __DYN_GetDynamicType(void) { return &__DYN_DynamicType; } \ + \ + friend __DYN_##name##_DynamicType; + +//! Define the members used for dynamic class creation +#define DYNAMIC_DEFINE(name) name##::__DYN_##name##_DynamicType name##::__DYN_DynamicType; + +//! Define a dynamic instantiation handler +#define DYNAMIC_INSTANTIATE_HANDLER(name) IDynamic * name##::__DYN_##name##_DynamicType::Instantiate(IDataStream * stream) { name * object = new name; +#define END_DYNAMIC_INSTANTIATE_HANDLER return object; } + +//! Specifies that a dynamic class should not be instantiated automatically +#define NO_DYNAMIC_INSTANTIATE_HANDLER(name) DYNAMIC_INSTANTIATE_HANDLER(name) { HALT("attempted to instantiate " #name); } END_DYNAMIC_INSTANTIATE_HANDLER + +//! Casts +#define CAST(ptr, type) _DynamicCast (ptr); + +class IDynamicType; + +/** + * Pure virtual base class allowing dynamic creation of objects + * + * To allow dynamic creation of a class, publicly inherit IDynamic, add the + * macro DYNAMIC_DECLARE(classname) first in the class declaration, and add + * the macro DYNAMIC_DEFINE(classname) somewhere in the class definition file. + */ +class IDynamic +{ + public: + IDynamic() { } + virtual ~IDynamic() { } + + virtual IDynamicType * __DYN_GetDynamicType(void) = 0; +}; + +/** + * Pure virtual base class allowing class instantiation and information retrieval + */ +class IDynamicType +{ + public: + IDynamicType() { } + virtual ~IDynamicType() { } + + virtual IDynamic * Create(void) = 0; + virtual char * GetName(void) = 0; + + virtual IDynamic * Instantiate(IDataStream * stream) = 0; +}; + +//! +template +T * _DynamicCast(IDynamic * ptr) +{ + if(ptr && (&T::__DYN_DynamicType == ptr->__DYN_GetDynamicType())) + return static_cast(ptr); + + return NULL; +} + +/** + * Registry of dynamic classes + */ +class IClassRegistry +{ + public: + IClassRegistry(); + ~IClassRegistry(); + + static void RegisterClassInfo(UInt32 id, IDynamicType * typeInfo); + + static IDynamicType * LookupClassInfo(UInt32 id); + static IDynamicType * LookupClassInfo(char * name); + + static IDynamic * Create(UInt32 id) { IDynamicType * info = LookupClassInfo(id); return info ? info->Create() : NULL; } + static IDynamic * Create(char * name) { IDynamicType * info = LookupClassInfo(name); return info ? info->Create() : NULL; } + + static IDynamic * Instantiate(UInt32 id, IDataStream * stream) { IDynamicType * info = LookupClassInfo(id); return info ? info->Instantiate(stream) : NULL; } + static IDynamic * Instantiate(char * name, IDataStream * stream) { IDynamicType * info = LookupClassInfo(name); return info ? info->Instantiate(stream) : NULL; } + + static char * GetName(UInt32 id) { IDynamicType * info = LookupClassInfo(id); return info ? info->GetName() : NULL; } + + private: + typedef std::map ClassRegistryType; + static ClassRegistryType theClassRegistry; +}; + +#endif diff --git a/common/IErrors.cpp b/common/IErrors.cpp new file mode 100644 index 0000000..3fb8ac5 --- /dev/null +++ b/common/IErrors.cpp @@ -0,0 +1,59 @@ +#include "common/IErrors.h" +#include "common/IDebugLog.h" +#include + +__declspec(noreturn) static void IErrors_Halt(void) +{ + // crash + *((int *)0) = 0xDEADBEEF; +} + +/** + * Report a failed assertion and exit the program + * + * @param file the file where the error occured + * @param line the line number where the error occured + * @param desc an error message + */ +void _AssertionFailed(const char * file, unsigned long line, const char * desc) +{ + _FATALERROR("Assertion failed in %s (%d): %s", file, line, desc); + + IErrors_Halt(); +} + +/** + * Report a failed assertion and exit the program + * + * @param file the file where the error occured + * @param line the line number where the error occured + * @param desc an error message + * @param code the error code + */ +void _AssertionFailed_ErrCode(const char * file, unsigned long line, const char * desc, unsigned long long code) +{ + if(code & 0xFFFFFFFF00000000) + _FATALERROR("Assertion failed in %s (%d): %s (code = %16I64X (%I64d))", file, line, desc, code, code); + else + { + UInt32 code32 = code; + _FATALERROR("Assertion failed in %s (%d): %s (code = %08X (%d))", file, line, desc, code32, code32); + } + + IErrors_Halt(); +} + +/** + * Report a failed assertion and exit the program + * + * @param file the file where the error occured + * @param line the line number where the error occured + * @param desc an error message + * @param code the error code + */ +void _AssertionFailed_ErrCode(const char * file, unsigned long line, const char * desc, const char * code) +{ + _FATALERROR("Assertion failed in %s (%d): %s (code = %s)", file, line, desc, code); + + IErrors_Halt(); +} diff --git a/common/IErrors.h b/common/IErrors.h new file mode 100644 index 0000000..d90c5fc --- /dev/null +++ b/common/IErrors.h @@ -0,0 +1,32 @@ +#pragma once + +void _AssertionFailed(const char * file, unsigned long line, const char * desc); +void _AssertionFailed_ErrCode(const char * file, unsigned long line, const char * desc, unsigned long long code); +void _AssertionFailed_ErrCode(const char * file, unsigned long line, const char * desc, const char * code); + +//! Exit the program if the condition is not true +#define ASSERT(a) do { if(!(a)) _AssertionFailed(__FILE__, __LINE__, #a); } while(0) +//! Exit the program if the condition is not true, with an error message +#define ASSERT_STR(a, b) do { if(!(a)) _AssertionFailed(__FILE__, __LINE__, b); } while(0) +//! Exit the program if the condition is not true, reporting an error code +#define ASSERT_CODE(a, b) do { if(!(a)) _AssertionFailed_ErrCode(__FILE__, __LINE__, #a, b); } while(0) +//! Exit the program if the condition is not true, reporting an error code and message +#define ASSERT_STR_CODE(a, b, c) do { if(!(a)) _AssertionFailed_ErrCode(__FILE__, __LINE__, b, c); } while(0) +//! Exit the program with an error message +#define HALT(a) do { _AssertionFailed(__FILE__, __LINE__, a); } while(0) +//! Exit the program with and error code and message +#define HALT_CODE(a, b) do { _AssertionFailed_ErrCode(__FILE__, __LINE__, a, b); } while(0) + +// based on the boost implementation of static asserts +template struct StaticAssertFailure; +template <> struct StaticAssertFailure { enum { a = 1 }; }; +template struct static_assert_test { }; + +#define __MACRO_JOIN__(a, b) __MACRO_JOIN_2__(a, b) +#define __MACRO_JOIN_2__(a, b) __MACRO_JOIN_3__(a, b) +#define __MACRO_JOIN_3__(a, b) a##b +#define __PREPRO_TOKEN_STR2__(a) #a +#define __PREPRO_TOKEN_STR__(a) __PREPRO_TOKEN_STR2__(a) +#define __LOC__ __FILE__ "("__PREPRO_TOKEN_STR__(__LINE__)") : " + +#define STATIC_ASSERT(a) typedef static_assert_test )> __MACRO_JOIN__(static_assert_typedef_, __COUNTER__) diff --git a/common/IEvent.cpp b/common/IEvent.cpp new file mode 100644 index 0000000..d36a464 --- /dev/null +++ b/common/IEvent.cpp @@ -0,0 +1,48 @@ +#include "IEvent.h" + +IEvent::IEvent() +{ + theEvent = CreateEvent(NULL, true, true, NULL); + ASSERT(theEvent); + + blockCount.Set(0); +} + +IEvent::~IEvent() +{ + CloseHandle(theEvent); +} + +bool IEvent::Block(void) +{ + if(blockCount.Increment() == 1) + return (ResetEvent(theEvent) != 0); + else + return true; +} + +bool IEvent::UnBlock(void) +{ + if(blockCount.Decrement() == 0) + return (SetEvent(theEvent) != 0); + else + return true; +} + +bool IEvent::Wait(UInt32 timeout) +{ + switch(WaitForSingleObject(theEvent, timeout)) + { + case WAIT_ABANDONED: + HALT("IEvent::Wait: got abandoned event"); + return false; + + case WAIT_OBJECT_0: + return true; + + default: + case WAIT_TIMEOUT: + gLog.FormattedMessage("IEvent::Wait: timeout"); + return false; + } +} diff --git a/common/IEvent.h b/common/IEvent.h new file mode 100644 index 0000000..2ebf3db --- /dev/null +++ b/common/IEvent.h @@ -0,0 +1,22 @@ +#pragma once + +#include "common/IInterlockedLong.h" + +class IEvent +{ + public: + static const UInt32 kDefaultTimeout = 1000 * 10; + + IEvent(); + ~IEvent(); + + bool Block(void); + bool UnBlock(void); + bool Wait(UInt32 timeout = kDefaultTimeout); + + bool IsBlocked(void) { return blockCount.Get() > 0; } + + private: + HANDLE theEvent; + IInterlockedLong blockCount; +}; diff --git a/common/IFIFO.cpp b/common/IFIFO.cpp new file mode 100644 index 0000000..be27865 --- /dev/null +++ b/common/IFIFO.cpp @@ -0,0 +1,85 @@ +#include "IFIFO.h" + +IFIFO::IFIFO(UInt32 length) +{ + fifoBuf = new UInt8[length]; + fifoBufSize = length; + fifoBase = 0; + fifoDataLength = 0; +} + +IFIFO::~IFIFO() +{ + delete fifoBuf; +} + +bool IFIFO::Push(UInt8 * buf, UInt32 length) +{ + // would that overflow the buffer? + if(length > GetBufferRemain()) + return false; + + UInt32 writeOffset = GetWriteOffset(); + + // will this cross the end of the buffer? + if(writeOffset + length > fifoBufSize) + { + UInt32 segmentLength = fifoBufSize - writeOffset; + + std::memcpy(&fifoBuf[writeOffset], buf, segmentLength); + std::memcpy(fifoBuf, &buf[segmentLength], length - segmentLength); + } + else + { + std::memcpy(&fifoBuf[writeOffset], buf, length); + } + + // update pointers + fifoDataLength += length; + + return true; +} + +bool IFIFO::Pop(UInt8 * buf, UInt32 length) +{ + bool result = Peek(buf, length); + + // update pointers if we were successful + if(result) + { + fifoDataLength -= length; + fifoBase = ToRawOffset(fifoBase + length); + } + + return result; +} + +bool IFIFO::Peek(UInt8 * buf, UInt32 length) +{ + // would that underflow the buffer? + if(length > fifoDataLength) + return false; + + // will this cross the end of the buffer? + if(fifoBase + length > fifoBufSize) + { + UInt32 segmentLength = fifoBufSize - fifoBase; + + std::memcpy(buf, &fifoBuf[fifoBase], segmentLength); + std::memcpy(&buf[segmentLength], fifoBuf, length - segmentLength); + } + else + { + std::memcpy(buf, &fifoBuf[fifoBase], length); + } + + return true; +} + +void IFIFO::Clear(void) +{ + fifoDataLength = 0; + + // this isn't needed, but staying away from the buffer end is always good + fifoBase = 0; +} diff --git a/common/IFIFO.h b/common/IFIFO.h new file mode 100644 index 0000000..89fdfaf --- /dev/null +++ b/common/IFIFO.h @@ -0,0 +1,27 @@ +#pragma once + +class IFIFO +{ + public: + IFIFO(UInt32 length = 0); + virtual ~IFIFO(); + + virtual bool Push(UInt8 * buf, UInt32 length); + virtual bool Pop(UInt8 * buf, UInt32 length); + virtual bool Peek(UInt8 * buf, UInt32 length); + virtual void Clear(void); + + UInt32 GetBufferSize(void) { return fifoBufSize; } + UInt32 GetBufferRemain(void) { return fifoBufSize - fifoDataLength; } + UInt32 GetDataLength(void) { return fifoDataLength; } + + private: + UInt32 ToRawOffset(UInt32 in) { return in % fifoBufSize; } + UInt32 ToDataOffset(UInt32 in) { return ToRawOffset(fifoBase + in); } + UInt32 GetWriteOffset(void) { return ToDataOffset(fifoDataLength); } + + UInt8 * fifoBuf; + UInt32 fifoBufSize; // size of the buffer (in bytes) + UInt32 fifoBase; // pointer to the beginning of the data block + UInt32 fifoDataLength; // size of the data block +}; diff --git a/common/IFileStream.cpp b/common/IFileStream.cpp new file mode 100644 index 0000000..a8ebe4f --- /dev/null +++ b/common/IFileStream.cpp @@ -0,0 +1,240 @@ +#include "IFileStream.h" +#include "IDebugLog.h" +#include "IErrors.h" +#include + +IFileStream::IFileStream() +:theFile(INVALID_HANDLE_VALUE) +{ + +} + +IFileStream::IFileStream(const char * name) +:theFile(INVALID_HANDLE_VALUE) +{ + Open(name); +} + +IFileStream::~IFileStream() +{ + Close(); +} + +/** + * Opens a file for reading and attaches it to the stream + */ +bool IFileStream::Open(const char * name) +{ + Close(); + + theFile = CreateFile(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if(theFile != INVALID_HANDLE_VALUE) + { + LARGE_INTEGER temp; + + GetFileSizeEx(theFile, &temp); + + streamLength = temp.QuadPart; + streamOffset = 0; + } + + return theFile != INVALID_HANDLE_VALUE; +} + +static UINT_PTR CALLBACK BrowseEventProc(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return 0; +} + +bool IFileStream::BrowseOpen(void) +{ + bool result = false; + OPENFILENAME info; + char path[4096]; + + path[0] = 0; + + info.lStructSize = sizeof(info); + info.hwndOwner = NULL; + info.hInstance = NULL; + info.lpstrFilter = NULL; + info.lpstrCustomFilter = NULL; + info.nMaxCustFilter = 0; + info.nFilterIndex = 0; + info.lpstrFile = path; + info.nMaxFile = sizeof(path); + info.lpstrFileTitle = NULL; + info.nMaxFileTitle = 0; + info.lpstrInitialDir = NULL; + info.lpstrTitle = NULL; + info.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_FILEMUSTEXIST | OFN_ENABLEHOOK | OFN_NOCHANGEDIR; + info.lpstrDefExt = NULL; + info.lCustData = NULL; + info.lpfnHook = BrowseEventProc; + info.lpTemplateName = NULL; +// info.pvReserved = NULL; +// info.dwReserved = NULL; +// info.FlagsEx = 0; + + if(GetOpenFileName(&info)) + { + result = Open(path); + } + + return result; +} + +/** + * Creates a new file for writing, overwriting any previously-existing files, + * and attaches it to the stream + */ +bool IFileStream::Create(const char * name) +{ + Close(); + + theFile = CreateFile(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if(theFile != INVALID_HANDLE_VALUE) + { + streamLength = 0; + streamOffset = 0; + } + + return theFile != INVALID_HANDLE_VALUE; +} + +bool IFileStream::BrowseCreate(const char * defaultName, const char * defaultPath, const char * title) +{ + bool result = false; + OPENFILENAME info; + char path[4096]; + + if(defaultName) + strcpy_s(path, sizeof(path), defaultName); + + info.lStructSize = sizeof(info); + info.hwndOwner = NULL; + info.hInstance = NULL; + info.lpstrFilter = NULL; + info.lpstrCustomFilter = NULL; + info.nMaxCustFilter = 0; + info.nFilterIndex = 0; + info.lpstrFile = path; + info.nMaxFile = sizeof(path); + info.lpstrFileTitle = NULL; + info.nMaxFileTitle = 0; + info.lpstrInitialDir = defaultPath; + info.lpstrTitle = title; + info.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_ENABLEHOOK | + OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; + info.lpstrDefExt = NULL; + info.lCustData = NULL; + info.lpfnHook = BrowseEventProc; + info.lpTemplateName = NULL; +// info.pvReserved = NULL; +// info.dwReserved = NULL; +// info.FlagsEx = 0; + + if(GetSaveFileName(&info)) + { + result = Create(path); + } + + return result; +} + +/** + * Closes the current file + */ +void IFileStream::Close(void) +{ + if(theFile) + { + CloseHandle(theFile); + theFile = INVALID_HANDLE_VALUE; + } +} + +void IFileStream::ReadBuf(void * buf, UInt32 inLength) +{ + UInt32 bytesRead; + + ReadFile(theFile, buf, inLength, &bytesRead, NULL); + + streamOffset += bytesRead; +} + +void IFileStream::WriteBuf(const void * buf, UInt32 inLength) +{ + UInt32 bytesWritten; + + // check for file expansion + if(streamOffset > streamLength) + SetEndOfFile(theFile); + + WriteFile(theFile, buf, inLength, &bytesWritten, NULL); + + streamOffset += bytesWritten; + + if(streamLength < streamOffset) + streamLength = streamOffset; +} + +void IFileStream::SetOffset(SInt64 inOffset) +{ + LARGE_INTEGER temp; + + temp.QuadPart = inOffset; + + SetFilePointerEx(theFile, temp, NULL, FILE_BEGIN); + streamOffset = inOffset; +} + +void IFileStream::SetLength(UInt64 length) +{ + SetOffset(length); + SetEndOfFile(theFile); + + streamLength = length; +} + +// ### TODO: get rid of buf +void IFileStream::MakeAllDirs(const char * path) +{ + char buf[1024]; + char * traverse = buf; + + while(1) + { + char data = *path++; + + if(!data) + break; + + if((data == '\\') || (data == '/')) + { + *traverse = 0; + _mkdir(buf); + } + + *traverse++ = data; + } +} + +char * IFileStream::ExtractFileName(char * path) +{ + char * traverse = path; + char * lastSlash = NULL; + + while(1) + { + char data = *traverse++; + + if((data == '\\') || (data == '/')) + lastSlash = traverse; + + if(!data) + break; + } + + return lastSlash; +} diff --git a/common/IFileStream.h b/common/IFileStream.h new file mode 100644 index 0000000..01aa955 --- /dev/null +++ b/common/IFileStream.h @@ -0,0 +1,37 @@ +#pragma once + +#include "common/IDataStream.h" + +/** + * An input file stream + */ +class IFileStream : public IDataStream +{ + public: + IFileStream(); + IFileStream(const char * name); + ~IFileStream(); + + bool Open(const char * name); + bool BrowseOpen(void); + + bool Create(const char * name); + bool BrowseCreate(const char * defaultName = NULL, const char * defaultPath = NULL, const char * title = NULL); + + void Close(void); + + HANDLE GetHandle(void) { return theFile; } + + virtual void ReadBuf(void * buf, UInt32 inLength); + virtual void WriteBuf(const void * buf, UInt32 inLength); + virtual void SetOffset(SInt64 inOffset); + + // can truncate. implicitly seeks to the end of the file + void SetLength(UInt64 length); + + static void MakeAllDirs(const char * path); + static char * ExtractFileName(char * path); + + protected: + HANDLE theFile; +}; diff --git a/common/IInterlockedLong.cpp b/common/IInterlockedLong.cpp new file mode 100644 index 0000000..fd3ec9e --- /dev/null +++ b/common/IInterlockedLong.cpp @@ -0,0 +1,3 @@ +#include "IInterlockedLong.h" + +// all functions are inlined diff --git a/common/IInterlockedLong.h b/common/IInterlockedLong.h new file mode 100644 index 0000000..73b4486 --- /dev/null +++ b/common/IInterlockedLong.h @@ -0,0 +1,19 @@ +#pragma once + +struct IInterlockedLong +{ + public: + long Increment(void) { return InterlockedIncrement(&value); } + long Decrement(void) { return InterlockedDecrement(&value); } + long Get(void) { return value; } + long Set(long in) { return InterlockedExchange(&value, in); } + long TrySetIf(long newValue, long expectedOldValue) + { return InterlockedCompareExchange(&value, newValue, expectedOldValue); } + + // interlock variable semantics + bool Claim(void) { return TrySetIf(1, 0) == 0; } + bool Release(void) { return TrySetIf(0, 1) == 1; } + + private: + volatile long value; +}; diff --git a/common/ILinkedList.h b/common/ILinkedList.h new file mode 100644 index 0000000..3f00dee --- /dev/null +++ b/common/ILinkedList.h @@ -0,0 +1,91 @@ +#pragma once + +// ILink members must be public +template +struct ILink +{ + static const UInt32 s_offset; + + ILink * next; + ILink * prev; + + T * GetObj(void) { return (T *)(((uintptr_t)this) - s_offset); } + + static ILink * GetLink(T * obj) { return (ILink *)(((uintptr_t)obj) + s_offset); } + + void Unlink(void) + { + if(next) next->prev = prev; + if(prev) prev->next = next; + + next = prev = NULL; + } + + void LinkBefore(T * obj) + { + LinkBefore(GetLink(obj)); + } + + void LinkAfter(T * obj) + { + LinkAfter(GetLink(obj)); + } + + void LinkBefore(ILink * link) + { + link->next = this; + link->prev = prev; + + if(prev) + { + prev->next = link; + } + + prev = link; + } + + void LinkAfter(ILink * link) + { + link->next = next; + link->prev = this; + + if(next) + { + next->prev = link; + } + + next = link; + } +}; + +template +struct ILinkedList +{ + ILink begin; + ILink end; + + void Reset(void) + { + begin.next = &end; + begin.prev = NULL; + end.next = NULL; + end.prev = &begin; + } + + void PushFront(T * obj) + { + ILink * objLink = ILink ::GetLink(obj); + + objLink->next = begin.next; + objLink->prev = &begin; + + if(objLink->next) + { + objLink->next->prev = objLink; + } + + begin.next = objLink; + } +}; + +#define ILINK_INIT(baseType, memberName) template const UInt32 ILink ::s_offset = offsetof(baseType, memberName) diff --git a/common/IMemPool.cpp b/common/IMemPool.cpp new file mode 100644 index 0000000..73afe4b --- /dev/null +++ b/common/IMemPool.cpp @@ -0,0 +1,43 @@ +#include "IMemPool.h" + +void Test_IMemPool(void) +{ + IMemPool pool; + + _DMESSAGE("main: pool test"); + gLog.Indent(); + + _DMESSAGE("start"); + pool.Dump(); + + UInt32 * data0, * data1, * data2; + + data0 = pool.Allocate(); + _DMESSAGE("alloc0 = %08X", data0); + pool.Dump(); + + data1 = pool.Allocate(); + _DMESSAGE("alloc1 = %08X", data1); + pool.Dump(); + + data2 = pool.Allocate(); + _DMESSAGE("alloc2 = %08X", data2); + pool.Dump(); + + _DMESSAGE("free0 %08X", data0); + pool.Free(data0); + pool.Dump(); + + data0 = pool.Allocate(); + _DMESSAGE("alloc0 = %08X", data0); + pool.Dump(); + + _DMESSAGE("free2 %08X", data2); + pool.Free(data2); + pool.Dump(); + + _DMESSAGE("done"); + pool.Dump(); + + gLog.Outdent(); +} diff --git a/common/IMemPool.h b/common/IMemPool.h new file mode 100644 index 0000000..b42578c --- /dev/null +++ b/common/IMemPool.h @@ -0,0 +1,312 @@ +#pragma once + +#include "common/ICriticalSection.h" + +template +class IMemPool +{ +public: + IMemPool() + :m_free(NULL), m_alloc(NULL) + { + Reset(); + } + + ~IMemPool() { Clear(); } + + void Reset(void) + { + for(UInt32 i = 0; i < size - 1; i++) + { + m_items[i].next = &m_items[i + 1]; + } + + m_items[size - 1].next = NULL; + m_free = m_items; + m_alloc = NULL; + } + + T * Allocate(void) + { + if(m_free) + { + PoolItem * item = m_free; + m_free = m_free->next; + + item->next = m_alloc; + m_alloc = item; + + T * obj = item->GetObj(); + + new (obj) T; + return obj; + } + + return NULL; + } + + void Free(T * obj) + { + PoolItem * item = reinterpret_cast (obj); + + if(item == m_alloc) + { + m_alloc = item->next; + } + else + { + PoolItem * traverse = m_alloc; + while(traverse->next != item) + traverse = traverse->next; + traverse->next = traverse->next->next; + } + + item->next = m_free; + m_free = item; + + obj->~T(); + } + + UInt32 GetSize(void) { return size; } + + T * Begin(void) + { + T * result = NULL; + + if(m_alloc) + result = m_alloc->GetObj(); + + return result; + } + + T * Next(T * obj) + { + PoolItem * item = reinterpret_cast (obj); + PoolItem * next = item->next; + T * result = NULL; + + if(next) + result = next->GetObj(); + + return result; + } + + void Dump(void) + { + gLog.Indent(); + + _DMESSAGE("free:"); + gLog.Indent(); + for(PoolItem * traverse = m_free; traverse; traverse = traverse->next) + _DMESSAGE("%08X", traverse); + gLog.Outdent(); + + _DMESSAGE("alloc:"); + gLog.Indent(); + for(PoolItem * traverse = m_alloc; traverse; traverse = traverse->next) + _DMESSAGE("%08X", traverse); + gLog.Outdent(); + + gLog.Outdent(); + } + + bool Full(void) + { + return m_free == NULL; + } + + bool Empty(void) + { + return m_alloc == NULL; + } + + void Clear(void) + { + while(m_alloc) + Free(m_alloc->GetObj()); + } + +private: + struct PoolItem + { + UInt8 obj[sizeof(T)]; + PoolItem * next; + + T * GetObj(void) { return reinterpret_cast (obj); } + }; + + PoolItem m_items[size]; + PoolItem * m_free; + PoolItem * m_alloc; +}; + +template +class IBasicMemPool +{ +public: + IBasicMemPool() + :m_free(NULL) + { + Reset(); + } + + ~IBasicMemPool() { } + + void Reset(void) + { + for(UInt32 i = 0; i < size - 1; i++) + { + m_items[i].next = &m_items[i + 1]; + } + + m_items[size - 1].next = NULL; + m_free = m_items; + } + + T * Allocate(void) + { + if(m_free) + { + PoolItem * item = m_free; + m_free = m_free->next; + + T * obj = item->GetObj(); + + new (obj) T; + return obj; + } + + return NULL; + } + + void Free(T * obj) + { + obj->~T(); + + PoolItem * item = reinterpret_cast (obj); + + item->next = m_free; + m_free = item; + } + + UInt32 GetSize(void) { return size; } + + bool Full(void) + { + return m_free == NULL; + } + + UInt32 GetIdx(T * obj) + { + PoolItem * item = reinterpret_cast (obj); + + return item - m_items; + } + + T * GetByID(UInt32 id) + { + return m_items[id].GetObj(); + } + +private: + union PoolItem + { + UInt8 obj[sizeof(T)]; + PoolItem * next; + + T * GetObj(void) { return reinterpret_cast (obj); } + }; + + PoolItem m_items[size]; + PoolItem * m_free; +}; + +template +class IThreadSafeBasicMemPool +{ +public: + IThreadSafeBasicMemPool() + :m_free(NULL) + { + Reset(); + } + + ~IThreadSafeBasicMemPool() { } + + void Reset(void) + { + m_mutex.Enter(); + + for(UInt32 i = 0; i < size - 1; i++) + { + m_items[i].next = &m_items[i + 1]; + } + + m_items[size - 1].next = NULL; + m_free = m_items; + + m_mutex.Leave(); + } + + T * Allocate(void) + { + T * result = NULL; + + m_mutex.Enter(); + + if(m_free) + { + PoolItem * item = m_free; + m_free = m_free->next; + + m_mutex.Leave(); + + result = item->GetObj(); + + new (result) T; + } + else + { + m_mutex.Leave(); + } + + return result; + } + + void Free(T * obj) + { + obj->~T(); + + PoolItem * item = reinterpret_cast (obj); + + m_mutex.Enter(); + + item->next = m_free; + m_free = item; + + m_mutex.Leave(); + } + + UInt32 GetSize(void) { return size; } + + bool Full(void) + { + return m_free == NULL; + } + +private: + union PoolItem + { + UInt8 obj[sizeof(T)]; + PoolItem * next; + + T * GetObj(void) { return reinterpret_cast (obj); } + }; + + PoolItem m_items[size]; + PoolItem * m_free; + + ICriticalSection m_mutex; +}; + +void Test_IMemPool(void); diff --git a/common/IMutex.cpp b/common/IMutex.cpp new file mode 100644 index 0000000..691c0b4 --- /dev/null +++ b/common/IMutex.cpp @@ -0,0 +1,34 @@ +#include "IMutex.h" + +IMutex::IMutex() +{ + theMutex = CreateMutex(NULL, true, NULL); +} + +IMutex::~IMutex() +{ + CloseHandle(theMutex); +} + +bool IMutex::Wait(UInt32 timeout) +{ + switch(WaitForSingleObject(theMutex, timeout)) + { + case WAIT_ABANDONED: + HALT("IMutex::Wait: got abandoned mutex"); + return false; + + case WAIT_OBJECT_0: + return true; + + default: + case WAIT_TIMEOUT: + gLog.FormattedMessage("IMutex::Wait: timeout"); + return false; + } +} + +void IMutex::Release(void) +{ + ASSERT_STR(ReleaseMutex(theMutex), "IMutex::Release: failed to release mutex"); +} diff --git a/common/IMutex.h b/common/IMutex.h new file mode 100644 index 0000000..b0de17c --- /dev/null +++ b/common/IMutex.h @@ -0,0 +1,16 @@ +#pragma once + +class IMutex +{ + public: + static const UInt32 kDefaultTimeout = 1000 * 10; + + IMutex(); + ~IMutex(); + + bool Wait(UInt32 timeout = kDefaultTimeout); + void Release(void); + + private: + HANDLE theMutex; +}; diff --git a/common/IPipeClient.cpp b/common/IPipeClient.cpp new file mode 100644 index 0000000..a9c7427 --- /dev/null +++ b/common/IPipeClient.cpp @@ -0,0 +1,60 @@ +#include "IPipeClient.h" + +IPipeClient::IPipeClient() +:m_pipe(INVALID_HANDLE_VALUE) +{ + // +} + +IPipeClient::~IPipeClient() +{ + Close(); +} + +bool IPipeClient::Open(const char * name) +{ + Close(); + + m_pipe = CreateFile( + name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + + return m_pipe != INVALID_HANDLE_VALUE; +} + +void IPipeClient::Close(void) +{ + if(m_pipe != INVALID_HANDLE_VALUE) + { + CloseHandle(m_pipe); + m_pipe = INVALID_HANDLE_VALUE; + } +} + +bool IPipeClient::ReadMessage(UInt8 * buf, UInt32 length) +{ + UInt32 bytesRead; + + ReadFile(m_pipe, buf, length, &bytesRead, NULL); + + IPipeServer::MessageHeader * header = (IPipeServer::MessageHeader *)buf; + + return + (bytesRead >= sizeof(IPipeServer::MessageHeader)) && // has a valid header + (bytesRead >= (sizeof(IPipeServer::MessageHeader) + header->length)); +} + +bool IPipeClient::WriteMessage(IPipeServer::MessageHeader * msg) +{ + UInt32 bytesWritten; + UInt32 length = sizeof(IPipeServer::MessageHeader) + msg->length; + + WriteFile(m_pipe, msg, length, &bytesWritten, NULL); + + return bytesWritten >= length; +} diff --git a/common/IPipeClient.h b/common/IPipeClient.h new file mode 100644 index 0000000..0cf500d --- /dev/null +++ b/common/IPipeClient.h @@ -0,0 +1,20 @@ +#pragma once + +#include "common/IPipeServer.h" + +class IPipeClient +{ +public: + IPipeClient(); + virtual ~IPipeClient(); + + bool Open(const char * name); + void Close(void); + + bool ReadMessage(UInt8 * buf, UInt32 length); + bool WriteMessage(IPipeServer::MessageHeader * msg); + +private: + HANDLE m_pipe; + std::string m_name; +}; diff --git a/common/IPipeServer.cpp b/common/IPipeServer.cpp new file mode 100644 index 0000000..9356f92 --- /dev/null +++ b/common/IPipeServer.cpp @@ -0,0 +1,74 @@ +#include "IPipeServer.h" + +IPipeServer::IPipeServer() +:m_pipe(INVALID_HANDLE_VALUE) +{ + // +} + +IPipeServer::~IPipeServer() +{ + Close(); +} + +bool IPipeServer::Open(const char * name) +{ + Close(); + + m_pipe = CreateNamedPipe( + name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_MESSAGE | PIPE_TYPE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + 8192, 8192, + 10 * 1000, // 10 seconds + NULL); + + return m_pipe != INVALID_HANDLE_VALUE; +} + +void IPipeServer::Close(void) +{ + if(m_pipe != INVALID_HANDLE_VALUE) + { + CloseHandle(m_pipe); + m_pipe = INVALID_HANDLE_VALUE; + } +} + +bool IPipeServer::WaitForClient(void) +{ + bool result = ConnectNamedPipe(m_pipe, NULL) != 0; + + // already connected? + if(!result) + { + if(GetLastError() == ERROR_PIPE_CONNECTED) + result = true; + } + + return result; +} + +bool IPipeServer::ReadMessage(UInt8 * buf, UInt32 length) +{ + UInt32 bytesRead; + + ReadFile(m_pipe, buf, length, &bytesRead, NULL); + + MessageHeader * header = (MessageHeader *)buf; + + return + (bytesRead >= sizeof(MessageHeader)) && // has a valid header + (bytesRead >= (sizeof(MessageHeader) + header->length)); +} + +bool IPipeServer::WriteMessage(MessageHeader * msg) +{ + UInt32 bytesWritten; + UInt32 length = sizeof(MessageHeader) + msg->length; + + WriteFile(m_pipe, msg, length, &bytesWritten, NULL); + + return bytesWritten >= length; +} diff --git a/common/IPipeServer.h b/common/IPipeServer.h new file mode 100644 index 0000000..bc68c16 --- /dev/null +++ b/common/IPipeServer.h @@ -0,0 +1,25 @@ +#pragma once + +class IPipeServer +{ +public: + struct MessageHeader + { + UInt32 type; + UInt32 length; + }; + + IPipeServer(); + virtual ~IPipeServer(); + + bool Open(const char * name); + void Close(void); + + bool WaitForClient(void); + + bool ReadMessage(UInt8 * buf, UInt32 length); + bool WriteMessage(MessageHeader * msg); + +private: + HANDLE m_pipe; +}; diff --git a/common/IPrefix.cpp b/common/IPrefix.cpp new file mode 100644 index 0000000..6231a06 --- /dev/null +++ b/common/IPrefix.cpp @@ -0,0 +1 @@ +#include "IPrefix.h" diff --git a/common/IPrefix.h b/common/IPrefix.h new file mode 100644 index 0000000..9e09446 --- /dev/null +++ b/common/IPrefix.h @@ -0,0 +1,26 @@ +#pragma once + +// 4018 - signed/unsigned mismatch +// 4200 - zero-sized array +// 4244 - loss of data by assignment +// 4267 - possible loss of data (truncation) +// 4305 - truncation by assignment +// 4288 - disable warning for crap microsoft extension screwing up the scope of variables defined in for loops +// 4311 - pointer truncation +// 4312 - pointer extension +#pragma warning(disable: 4018 4200 4244 4267 4305 4288 4312 4311) + +// winxp and above +#define _WIN32_WINNT 0x0501 + +#include +#include +#include +#include +#include "common/ITypes.h" +#include "common/IErrors.h" +#include "common/IDynamicCreate.h" +#include "common/IDebugLog.h" +#include "common/ISingleton.h" +#include +#include diff --git a/common/IRangeMap.cpp b/common/IRangeMap.cpp new file mode 100644 index 0000000..855568e --- /dev/null +++ b/common/IRangeMap.cpp @@ -0,0 +1 @@ +#include "IRangeMap.h" diff --git a/common/IRangeMap.h b/common/IRangeMap.h new file mode 100644 index 0000000..23416b8 --- /dev/null +++ b/common/IRangeMap.h @@ -0,0 +1,215 @@ +#pragma once + +#include + +// t_key must be a numeric type +// ### you can't create a range taking up the entire range of t_key +// ### (could be done by switching from start/length -> start/end) +template +class IRangeMap +{ +public: + struct Entry + { + bool Contains(t_key addr, t_key base) + { + return (addr >= base) && (addr <= (base + length - 1)); + } + + t_key length; + t_data data; + }; + + typedef std::map EntryMapType; + typedef typename EntryMapType::iterator Iterator; + + IRangeMap() + { + // + } + + virtual ~IRangeMap() + { + // + } + + void Clear(void) + { + m_entries.clear(); + } + + t_data * Add(t_key start, t_key length) + { + t_data * result = NULL; + Entry * entry = NULL; + + t_key end = start + length - 1; + + if(end >= start) // check for overflow ### should also check for overflow on length - 1, but that's pedantic + { + // special-case empty lists + if(m_entries.empty()) + { + entry = &m_entries[start]; + } + else + { + // collision check + + EntryMapType::iterator iter = m_entries.lower_bound(start); + // iter contains the first entry at or after start (or null) + + if(iter == m_entries.begin()) + { + // there can't be anything before this entry + // so we only need to check if it's colliding with us + + if(iter->first > end) + { + // can't provide a hint because we're inserting at the top + entry = &m_entries[start]; + } + } + else + { + // see if this entry doesn't collide + // can be null (null entries don't collide) + if((iter == m_entries.end()) || (iter->first > end)) + { + // we didn't get the first entry in the map + // and there is at least one entry in the map + // therefore there's an entry before iter + EntryMapType::iterator preIter = iter; + preIter--; + + // check if this collides + // guaranteed to be the first entry before start + t_key preEnd = preIter->first + preIter->second.length - 1; + + if(preEnd < start) + { + // cool, everything's fine, allocate it + EntryMapType::iterator newEntry = m_entries.insert(preIter, EntryMapType::value_type(start, Entry())); + entry = &newEntry->second; + } + } + } + } + } + + // set up the entry + if(entry) + { + entry->length = length; + + result = &entry->data; + } + + return result; + } + + t_data * Lookup(t_key addr, t_key * base = NULL, t_key * length = NULL) + { + t_data * result = NULL; + + EntryMapType::iterator iter = LookupIter(addr); + if(iter != m_entries.end()) + { + if(base) *base = iter->first; + if(length) *length = iter->second.length; + + result = &iter->second.data; + } + + return result; + } + + bool Erase(t_key addr, t_key * base = NULL, t_key * length = NULL) + { + bool result = false; + + EntryMapType::iterator iter = LookupIter(addr); + if(iter != m_entries.end()) + { + if(base) *base = iter->first; + if(length) *length = iter->second.length; + + m_entries.erase(iter); + + result = true; + } + + return result; + } + + t_key GetDataRangeLength(t_data * data) + { + Entry * entry = reinterpret_cast (reinterpret_cast (data) - offsetof(Entry, data)); + + return entry->length; + } + + typename EntryMapType::iterator LookupIter(t_key addr) + { + EntryMapType::iterator result = m_entries.end(); + + if(!m_entries.empty()) + { + // we need to find the last entry less than or equal to addr + + // find the first entry not less than addr + EntryMapType::iterator iter = m_entries.lower_bound(addr); + + // iter is either equal to addr, greater than addr, or the end + if(iter == m_entries.end()) + { + // iter is the end + // can only be in the entry before this + // which does exist because map isn't empty + --iter; + + if(iter->second.Contains(addr, iter->first)) + { + result = iter; + } + } + // at this point iter must be valid + else if(iter->first > addr) + { + // iter is greater than addr + // can only be in the entry before this + // but there may not be an entry before this + + if(iter != m_entries.begin()) + { + --iter; + + if(iter->second.Contains(addr, iter->first)) + { + result = iter; + } + } + } + else + { + // iter is equal to addr and matches + result = iter; + } + } + + return result; + } + + typename EntryMapType::iterator Begin(void) + { + return m_entries.begin(); + } + + typename EntryMapType::iterator End(void) + { + return m_entries.end(); + } + +private: + EntryMapType m_entries; +}; diff --git a/common/IReadWriteLock.cpp b/common/IReadWriteLock.cpp new file mode 100644 index 0000000..211917b --- /dev/null +++ b/common/IReadWriteLock.cpp @@ -0,0 +1,43 @@ +#include "IReadWriteLock.h" + +IReadWriteLock::IReadWriteLock() +{ + readCount.Set(0); + readBlocker.UnBlock(); + writeBlocker.UnBlock(); +} + +IReadWriteLock::~IReadWriteLock() +{ + // +} + +void IReadWriteLock::StartRead(void) +{ + enterBlocker.Enter(); + readBlocker.Wait(); + if(readCount.Increment() == 1) + writeBlocker.Block(); + enterBlocker.Leave(); +} + +void IReadWriteLock::EndRead(void) +{ + if(!readCount.Decrement()) + writeBlocker.UnBlock(); +} + +void IReadWriteLock::StartWrite(void) +{ + writeMutex.Enter(); + enterBlocker.Enter(); + readBlocker.Block(); + writeBlocker.Wait(); + enterBlocker.Leave(); +} + +void IReadWriteLock::EndWrite(void) +{ + readBlocker.UnBlock(); + writeMutex.Leave(); +} diff --git a/common/IReadWriteLock.h b/common/IReadWriteLock.h new file mode 100644 index 0000000..d3e0984 --- /dev/null +++ b/common/IReadWriteLock.h @@ -0,0 +1,24 @@ +#pragma once + +#include "common/ICriticalSection.h" +#include "common/IEvent.h" +#include "common/IInterlockedLong.h" + +class IReadWriteLock +{ + public: + IReadWriteLock(); + ~IReadWriteLock(); + + void StartRead(void); + void EndRead(void); + void StartWrite(void); + void EndWrite(void); + + private: + IEvent readBlocker; + IEvent writeBlocker; + ICriticalSection enterBlocker; + ICriticalSection writeMutex; + IInterlockedLong readCount; +}; diff --git a/common/ISegmentStream.cpp b/common/ISegmentStream.cpp new file mode 100644 index 0000000..7fd0618 --- /dev/null +++ b/common/ISegmentStream.cpp @@ -0,0 +1,76 @@ +#include "common/ISegmentStream.h" + +ISegmentStream::ISegmentStream() +{ + streamLength = 0; +} + +ISegmentStream::~ISegmentStream() +{ + +} + +void ISegmentStream::AttachStream(IDataStream * inStream) +{ + parent = inStream; + streamLength = 0; + streamOffset = 0; +} + +void ISegmentStream::AddSegment(UInt64 offset, UInt64 length, UInt64 parentOffset) +{ + segmentInfo.push_back(SegmentInfo(offset, length, parentOffset)); + + if(streamLength < (parentOffset + length)) + streamLength = parentOffset + length; +} + +void ISegmentStream::ReadBuf(void * buf, UInt32 inLength) +{ + UInt32 remain = inLength; + UInt8 * out = (UInt8 *)buf; + + while(remain > 0) + { + SegmentInfo * info = LookupInfo(streamOffset); + ASSERT(info); + + UInt64 segmentOffset = streamOffset - info->offset; + UInt64 transferLength = info->length - segmentOffset; + + if(transferLength > remain) + transferLength = remain; + + parent->SetOffset(info->parentOffset + segmentOffset); + parent->ReadBuf(out, transferLength); + + streamOffset += transferLength; + remain -= transferLength; + } +} + +void ISegmentStream::WriteBuf(const void * buf, UInt32 inLength) +{ + HALT("ISegmentStream::WriteBuf: writing unsupported"); +} + +void ISegmentStream::SetOffset(SInt64 inOffset) +{ + SegmentInfo * info = LookupInfo(inOffset); + ASSERT(info); + + UInt64 segmentOffset = inOffset - info->offset; + + parent->SetOffset(info->parentOffset + segmentOffset); + + streamOffset = inOffset; +} + +ISegmentStream::SegmentInfo * ISegmentStream::LookupInfo(UInt64 offset) +{ + for(SegmentInfoListType::iterator iter = segmentInfo.begin(); iter != segmentInfo.end(); iter++) + if((offset >= (*iter).offset) && (offset < (*iter).offset + (*iter).length)) + return &(*iter); + + return NULL; +} diff --git a/common/ISegmentStream.h b/common/ISegmentStream.h new file mode 100644 index 0000000..6abe880 --- /dev/null +++ b/common/ISegmentStream.h @@ -0,0 +1,44 @@ +#pragma once + +#include "common/IDataStream.h" +#include + +/** + * An stream composed of many non-contiguous segments of a larger stream + */ +class ISegmentStream : public IDataStream +{ + public: + ISegmentStream(); + ~ISegmentStream(); + + void AttachStream(IDataStream * inStream); + + void AddSegment(UInt64 offset, UInt64 length, UInt64 parentOffset); + + virtual void ReadBuf(void * buf, UInt32 inLength); + virtual void WriteBuf(const void * buf, UInt32 inLength); + virtual void SetOffset(SInt64 inOffset); + + protected: + IDataStream * parent; + + struct SegmentInfo + { + SegmentInfo(UInt64 inOffset, UInt64 inLength, UInt64 inParentOffset) + { + offset = inOffset; + length = inLength; + parentOffset = inParentOffset; + } + + UInt64 offset; + UInt64 length; + UInt64 parentOffset; + }; + + typedef std::vector SegmentInfoListType; + SegmentInfoListType segmentInfo; + + SegmentInfo * LookupInfo(UInt64 offset); +}; diff --git a/common/ISingleton.cpp b/common/ISingleton.cpp new file mode 100644 index 0000000..00818f4 --- /dev/null +++ b/common/ISingleton.cpp @@ -0,0 +1,3 @@ +#include "common/ISingleton.h" + +//template T * Singleton ::ms_Singleton = 0; \ No newline at end of file diff --git a/common/ISingleton.h b/common/ISingleton.h new file mode 100644 index 0000000..3cb16a9 --- /dev/null +++ b/common/ISingleton.h @@ -0,0 +1,53 @@ +#pragma once + +#include "common/IErrors.h" + +#pragma warning(push) +#pragma warning(disable: 4311 4312) + +/** + * A singleton base class + * + * Singletons are useful when you have a class that will be instantiated once, + * like a global manager. + */ +template +class ISingleton +{ + static T * ms_Singleton; + + public: + ISingleton() + { + ASSERT(!ms_Singleton); + intptr_t offset = (intptr_t)(T *)1 - (intptr_t)(ISingleton *)(T *)1; + ms_Singleton = (T *)((intptr_t)this + offset); + } + + virtual ~ISingleton() + { + ASSERT(ms_Singleton); + ms_Singleton = 0; + } + + /** + * Returns the single instance of the derived class + */ + static T& GetSingleton(void) + { + ASSERT(ms_Singleton); + return *ms_Singleton; + } + + /** + * Returns a pointer to the single instance of the derived class + */ + static T * GetSingletonPtr(void) + { + return ms_Singleton; + } +}; + +template T * ISingleton ::ms_Singleton = 0; + +#pragma warning(pop) diff --git a/common/ITextParser.cpp b/common/ITextParser.cpp new file mode 100644 index 0000000..e178685 --- /dev/null +++ b/common/ITextParser.cpp @@ -0,0 +1,83 @@ +#include "ITextParser.h" +#include "IDataStream.h" + +ITextParser::ITextParser() +:m_stream(NULL) +{ + // +} + +ITextParser::ITextParser(IDataStream * stream) +:m_stream(stream) +{ + // +} + +ITextParser::~ITextParser() +{ + // +} + +void ITextParser::Attach(IDataStream * stream) +{ + m_stream = stream; +} + +void ITextParser::SkipWhitespace(void) +{ + while(!m_stream->HitEOF()) + { + char data = m_stream->Peek8(); + + if(!isspace(data)) + break; + + m_stream->Skip(1); + } +} + +void ITextParser::SkipLine(void) +{ + while(!m_stream->HitEOF()) + { + char data = m_stream->Peek8(); + + if((data != '\n') && (data != '\r')) + break; + + m_stream->Skip(1); + } +} + +void ITextParser::ReadLine(char * out, UInt32 length) +{ + m_stream->ReadString(out, length, '\n', '\r'); +} + +void ITextParser::ReadToken(char * buf, UInt32 bufLength) +{ + char * traverse = buf; + + ASSERT_STR(bufLength > 0, "ITextParser::ReadToken: zero-sized buffer"); + + if(bufLength == 1) + { + buf[0] = 0; + } + else + { + bufLength--; + + for(UInt32 i = 0; (i < bufLength) && !m_stream->HitEOF(); i++) + { + UInt8 data = m_stream->Read8(); + + if(isspace(data) || !data) + break; + + *traverse++ = data; + } + + *traverse++ = 0; + } +} diff --git a/common/ITextParser.h b/common/ITextParser.h new file mode 100644 index 0000000..212a073 --- /dev/null +++ b/common/ITextParser.h @@ -0,0 +1,25 @@ +#pragma once + +#include "common/IDataStream.h" + +class ITextParser +{ +public: + ITextParser(); + ITextParser(IDataStream * stream); + ~ITextParser(); + + void Attach(IDataStream * stream); + IDataStream * GetStream(void) { return m_stream; } + + bool HitEOF(void) { return m_stream->HitEOF(); } + + void SkipWhitespace(void); + void SkipLine(void); + + void ReadLine(char * out, UInt32 length); + void ReadToken(char * out, UInt32 length); + +private: + IDataStream * m_stream; +}; diff --git a/common/IThread.cpp b/common/IThread.cpp new file mode 100644 index 0000000..9b7e75c --- /dev/null +++ b/common/IThread.cpp @@ -0,0 +1,65 @@ +#include "IThread.h" + +IThread::IThread() +{ + mainProc = NULL; + mainProcParam = NULL; + stopRequested = false; + isRunning = false; + theThread = NULL; + threadID = 0; +} + +IThread::~IThread() +{ + ForceStop(); + + if(theThread) + { + CloseHandle(theThread); + } +} + +void IThread::Start(MainProcPtr proc, void * procParam) +{ + if(!isRunning) + { + isRunning = true; + stopRequested = false; + + mainProc = proc; + mainProcParam = procParam; + + theThread = CreateThread(NULL, 0, _ThreadProc, static_cast(this), 0, &threadID); + } +} + +void IThread::Stop(void) +{ + if(isRunning) + { + stopRequested = true; + } +} + +void IThread::ForceStop(void) +{ + if(isRunning) + { + TerminateThread(theThread, 0); + + isRunning = false; + } +} + +UInt32 IThread::_ThreadProc(void * param) +{ + IThread * _this = (IThread *)param; + + if(_this->mainProc) + _this->mainProc(_this->mainProcParam); + + _this->isRunning = false; + + return 0; +} diff --git a/common/IThread.h b/common/IThread.h new file mode 100644 index 0000000..3601c4a --- /dev/null +++ b/common/IThread.h @@ -0,0 +1,32 @@ +#pragma once + +// TODO: I really don't like the interface for this + +class IThread +{ + public: + typedef void (* MainProcPtr)(void * param); + + IThread(); + ~IThread(); + + void Start(MainProcPtr proc, void * procParam = NULL); + void Stop(void); + void ForceStop(void); + + bool IsRunning(void) { return isRunning; } + bool StopRequested(void) { return stopRequested; } + + HANDLE GetHandle(void) { return theThread; } + + protected: + MainProcPtr mainProc; + void * mainProcParam; + volatile bool stopRequested; + bool isRunning; + HANDLE theThread; + UInt32 threadID; + + private: + static UInt32 WINAPI _ThreadProc(void * param); +}; diff --git a/common/ITimer.cpp b/common/ITimer.cpp new file mode 100644 index 0000000..76fc6e8 --- /dev/null +++ b/common/ITimer.cpp @@ -0,0 +1,133 @@ +#include "ITimer.h" + +// QueryPerformanceCounter is very accurate, but hardware bugs can cause it to return inaccurate results +// this code uses multimedia timers to check for glitches in QPC + +double ITimer::s_secondsPerCount = 0; +TIMECAPS ITimer::s_timecaps = { 0 }; +bool ITimer::s_setTime = false; +UInt64 ITimer::s_lastQPC = 0; +UInt64 ITimer::s_qpcWrapMargin = 0; +bool ITimer::s_hasLastQPC = false; +UInt32 ITimer::s_qpcWrapCount = 0; +UInt32 ITimer::s_qpcInaccurateCount = 0; + +ITimer::ITimer() +:m_qpcBase(0), m_tickBase(0) +{ + Init(); +} + +ITimer::~ITimer() +{ + +} + +void ITimer::Init(void) +{ + if(!s_secondsPerCount) + { + // init qpc + UInt64 countsPerSecond; + BOOL res = QueryPerformanceFrequency((LARGE_INTEGER *)&countsPerSecond); + + ASSERT_STR(res, "ITimer: no high-resolution timer support"); + + s_secondsPerCount = 1.0 / countsPerSecond; + + s_qpcWrapMargin = (UInt64)(-((SInt64)(countsPerSecond * 60))); // detect if we've wrapped around by a delta greater than this - also limits max time + _MESSAGE("s_qpcWrapMargin: %016I64X", s_qpcWrapMargin); + _MESSAGE("wrap time: %fs", ((double)0xFFFFFFFFFFFFFFFF) * s_secondsPerCount); + + // init multimedia timer + timeGetDevCaps(&s_timecaps, sizeof(s_timecaps)); + + _MESSAGE("min timer period = %d", s_timecaps.wPeriodMin); + + s_setTime = (timeBeginPeriod(s_timecaps.wPeriodMin) == TIMERR_NOERROR); + if(!s_setTime) + _WARNING("couldn't change timer period"); + } +} + +void ITimer::DeInit(void) +{ + if(s_secondsPerCount) + { + if(s_setTime) + { + timeEndPeriod(s_timecaps.wPeriodMin); + s_setTime = false; + } + + if(s_qpcWrapCount) + _MESSAGE("s_qpcWrapCount: %d", s_qpcWrapCount); + + s_secondsPerCount = 0; + } +} + +void ITimer::Start(void) +{ + m_qpcBase = GetQPC(); + m_tickBase = timeGetTime(); +} + +double ITimer::GetElapsedTime(void) +{ + UInt64 qpcNow = GetQPC(); + UInt32 tickNow = timeGetTime(); + + UInt64 qpcDelta = qpcNow - m_qpcBase; + UInt64 tickDelta = tickNow - m_tickBase; + + double qpcSeconds = ((double)qpcDelta) * s_secondsPerCount; + double tickSeconds = ((double)tickDelta) * 0.001; // ticks are in milliseconds + double qpcTickDelta = qpcSeconds - tickSeconds; + + if(qpcTickDelta < 0) qpcTickDelta = -qpcTickDelta; + + // if they differ by more than one second, something's wrong, return + if(qpcTickDelta > 1) + { + s_qpcInaccurateCount++; + return tickSeconds; + } + else + { + return qpcSeconds; + } +} + +UInt64 ITimer::GetQPC(void) +{ + UInt64 now; + + QueryPerformanceCounter((LARGE_INTEGER *)&now); + + if(s_hasLastQPC) + { + UInt64 delta = now - s_lastQPC; + + if(delta > s_qpcWrapMargin) + { + // we've gone back in time, return a kludged value + + s_lastQPC = now; + now = s_lastQPC + 1; + + s_qpcWrapCount++; + } + else + { + s_lastQPC = now; + } + } + else + { + s_hasLastQPC = true; + s_lastQPC = now; + } + + return now; +} diff --git a/common/ITimer.h b/common/ITimer.h new file mode 100644 index 0000000..1068518 --- /dev/null +++ b/common/ITimer.h @@ -0,0 +1,38 @@ +#pragma once + +#include "common/ITypes.h" + +/** + * A high-resolution timer. + */ +class ITimer +{ + public: + ITimer(); + ~ITimer(); + + static void Init(void); + static void DeInit(void); + + void Start(void); + + double GetElapsedTime(void); // seconds + + private: + UInt64 m_qpcBase; // QPC + UInt32 m_tickBase; // timeGetTime + + static double s_secondsPerCount; + static TIMECAPS s_timecaps; + static bool s_setTime; + + // safe QPC stuff + static UInt64 GetQPC(void); + + static UInt64 s_lastQPC; + static UInt64 s_qpcWrapMargin; + static bool s_hasLastQPC; + + static UInt32 s_qpcWrapCount; + static UInt32 s_qpcInaccurateCount; +}; diff --git a/common/ITypes.cpp b/common/ITypes.cpp new file mode 100644 index 0000000..6a9539f --- /dev/null +++ b/common/ITypes.cpp @@ -0,0 +1,66 @@ +#include "ITypes.h" + +Bitstring::Bitstring() +:data(NULL) +{ + +} + +Bitstring::Bitstring(UInt32 inLength) +:data(NULL) +{ + Alloc(inLength); +} + +Bitstring::~Bitstring() +{ + Dispose(); +} + +void Bitstring::Alloc(UInt32 inLength) +{ + Dispose(); + + inLength = (inLength + 7) & ~7; + length = inLength >> 3; + + data = new UInt8[length]; +} + +void Bitstring::Dispose(void) +{ + delete [] data; +} + +void Bitstring::Clear(void) +{ + std::memset(data, 0, length); +} + +void Bitstring::Clear(UInt32 idx) +{ + ASSERT_STR(idx < (length << 3), "Bitstring::Clear: out of range"); + + data[idx >> 3] &= ~(1 << (idx & 7)); +} + +void Bitstring::Set(UInt32 idx) +{ + ASSERT_STR(idx < (length << 3), "Bitstring::Set: out of range"); + + data[idx >> 3] |= (1 << (idx & 7)); +} + +bool Bitstring::IsSet(UInt32 idx) +{ + ASSERT_STR(idx < (length << 3), "Bitstring::IsSet: out of range"); + + return (data[idx >> 3] & (1 << (idx & 7))) ? true : false; +} + +bool Bitstring::IsClear(UInt32 idx) +{ + ASSERT_STR(idx < (length << 3), "Bitstring::IsClear: out of range"); + + return (data[idx >> 3] & (1 << (idx & 7))) ? false : true; +} diff --git a/common/ITypes.h b/common/ITypes.h new file mode 100644 index 0000000..eab23c8 --- /dev/null +++ b/common/ITypes.h @@ -0,0 +1,344 @@ +#pragma once + +#include "common/IErrors.h" + +#pragma warning(disable: 4221) +#include + +typedef unsigned char UInt8; //!< An unsigned 8-bit integer value +typedef unsigned short UInt16; //!< An unsigned 16-bit integer value +typedef unsigned long UInt32; //!< An unsigned 32-bit integer value +typedef unsigned long long UInt64; //!< An unsigned 64-bit integer value +typedef signed char SInt8; //!< A signed 8-bit integer value +typedef signed short SInt16; //!< A signed 16-bit integer value +typedef signed long SInt32; //!< A signed 32-bit integer value +typedef signed long long SInt64; //!< A signed 64-bit integer value +typedef float Float32; //!< A 32-bit floating point value +typedef double Float64; //!< A 64-bit floating point value + +inline UInt32 Extend16(UInt32 in) +{ + return (in & 0x8000) ? (0xFFFF0000 | in) : in; +} + +inline UInt32 Extend8(UInt32 in) +{ + return (in & 0x80) ? (0xFFFFFF00 | in) : in; +} + +inline UInt16 Swap16(UInt16 in) +{ + return ((in >> 8) & 0x00FF) | + ((in << 8) & 0xFF00); +} + +inline UInt32 Swap32(UInt32 in) +{ + return ((in >> 24) & 0x000000FF) | + ((in >> 8) & 0x0000FF00) | + ((in << 8) & 0x00FF0000) | + ((in << 24) & 0xFF000000); +} + +inline UInt64 Swap64(UInt64 in) +{ + UInt64 temp; + + temp = Swap32(in); + temp <<= 32; + temp |= Swap32(in >> 32); + + return temp; +} + +inline void SwapFloat(float * in) +{ + UInt32 * temp = (UInt32 *)in; + + *temp = Swap32(*temp); +} + +inline void SwapDouble(double * in) +{ + UInt64 * temp = (UInt64 *)in; + + *temp = Swap64(*temp); +} + +inline bool IsBigEndian(void) +{ + union + { + UInt16 u16; + UInt8 u8[2]; + } temp; + + temp.u16 = 0x1234; + + return temp.u8[0] == 0x12; +} + +inline bool IsLittleEndian(void) +{ + return !IsBigEndian(); +} + +#define CHAR_CODE(a, b, c, d) (((a & 0xFF) << 0) | ((b & 0xFF) << 8) | ((c & 0xFF) << 16) | ((d & 0xFF) << 24)) +#define MACRO_SWAP16(a) ((((a) & 0x00FF) << 8) | (((a) & 0xFF00) >> 8)) +#define MACRO_SWAP32(a) ((((a) & 0x000000FF) << 24) | (((a) & 0x0000FF00) << 8) | (((a) & 0x00FF0000) >> 8) | (((a) & 0xFF000000) >> 24)) + +#define VERSION_CODE(primary, secondary, sub) (((primary & 0xFFF) << 20) | ((secondary & 0xFFF) << 8) | ((sub & 0xFF) << 0)) +#define VERSION_CODE_PRIMARY(in) ((in >> 20) & 0xFFF) +#define VERSION_CODE_SECONDARY(in) ((in >> 8) & 0xFFF) +#define VERSION_CODE_SUB(in) ((in >> 0) & 0xFF) + +#define MAKE_COLOR(a, r, g, b) (((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | ((b & 0xFF) << 0)) +#define COLOR_ALPHA(in) ((in >> 24) & 0xFF) +#define COLOR_RED(in) ((in >> 16) & 0xFF) +#define COLOR_GREEN(in) ((in >> 8) & 0xFF) +#define COLOR_BLUE(in) ((in >> 0) & 0xFF) + +/** + * A 64-bit variable combiner + * + * Useful for endian-independent value extraction. + */ +union VarCombiner +{ + UInt64 u64; + SInt64 s64; + double f64; + struct { UInt32 b; UInt32 a; } u32; + struct { SInt32 b; SInt32 a; } s32; + struct { float b; float a; } f32; + struct { UInt16 d; UInt16 c; UInt16 b; UInt16 a; } u16; + struct { SInt16 d; SInt16 c; SInt16 b; SInt16 a; } s16; + struct { UInt8 h; UInt8 g; UInt8 f; UInt8 e; + UInt8 d; UInt8 c; UInt8 b; UInt8 a; } u8; + struct { SInt8 h; SInt8 g; SInt8 f; SInt8 e; + SInt8 d; SInt8 c; SInt8 b; SInt8 a; } s8; +}; + +/** + * A bitfield. + */ +template +class Bitfield +{ + public: + Bitfield() { } + ~Bitfield() { } + + void Clear(void) { field = 0; } //!< Clears all bits + void RawSet(UInt32 data) { field = data; } //!< Modifies all bits + + void Set(UInt32 data) { field |= data; } //!< Sets individual bits + void Clear(UInt32 data) { field &= ~data; } //!< Clears individual bits + void UnSet(UInt32 data) { Clear(data); } //!< Clears individual bits + void Mask(UInt32 data) { field &= data; } //!< Masks individual bits + void Toggle(UInt32 data) { field ^= data; } //!< Toggles individual bits + void Write(UInt32 data, bool state) + { if(state) Set(data); else Clear(data); } + + T Get(void) const { return field; } //!< Gets all bits + T Get(UInt32 data) const { return field & data; } //!< Gets individual bits + T Extract(UInt32 bit) const { return (field >> bit) & 1; } //!< Extracts a bit + T ExtractField(UInt32 shift, UInt32 length) //!< Extracts a series of bits + { return (field >> shift) & (0xFFFFFFFF >> (32 - length)); } + + bool IsSet(UInt32 data) const { return ((field & data) == data) ? true : false; } //!< Are all these bits set? + bool IsUnSet(UInt32 data) const { return (field & data) ? false : true; } //!< Are all these bits clear? + bool IsClear(UInt32 data) const { return IsUnSet(data); } //!< Are all these bits clear? + + private: + T field; //!< bitfield data +}; + +typedef Bitfield Bitfield8; //!< An 8-bit bitfield +typedef Bitfield Bitfield16; //!< A 16-bit bitfield +typedef Bitfield Bitfield32; //!< A 32-bit bitfield + +STATIC_ASSERT(sizeof(Bitfield8) == 1); +STATIC_ASSERT(sizeof(Bitfield16) == 2); +STATIC_ASSERT(sizeof(Bitfield32) == 4); + +/** + * A bitstring + * + * Essentially a long bitvector. + */ +class Bitstring +{ + public: + Bitstring(); + Bitstring(UInt32 inLength); + ~Bitstring(); + + void Alloc(UInt32 inLength); + void Dispose(void); + + void Clear(void); + void Clear(UInt32 idx); + void Set(UInt32 idx); + + bool IsSet(UInt32 idx); + bool IsClear(UInt32 idx); + + private: + UInt8 * data; + UInt32 length; //!< length in bytes +}; + +/** + * Time storage + */ +class Time +{ + public: + Time() { Clear(); } + ~Time() { } + + //! Deinitialize the class + void Clear(void) { seconds = minutes = hours = 0; hasData = false; } + //! Sets the class to the current time + //! @todo implement this + void SetToNow(void) { Set(1, 2, 3); } + + //! Sets the class to the specified time + void Set(UInt8 inS, UInt8 inM, UInt8 inH) + { seconds = inS; minutes = inM; hours = inH; hasData = true; } + + //! Gets whether the class has been initialized or not + bool IsSet(void) { return hasData; } + + UInt8 GetSeconds(void) { return seconds; } //!< return the seconds portion of the time + UInt8 GetMinutes(void) { return minutes; } //!< return the minutes portion of the time + UInt8 GetHours(void) { return hours; } //!< return the hours portion of the time + + private: + UInt8 seconds, minutes, hours; + bool hasData; +}; + +const float kFloatEpsilon = 0.0001f; + +inline bool FloatEqual(float a, float b) { float magnitude = a - b; if(magnitude < 0) magnitude = -magnitude; return magnitude < kFloatEpsilon; } + +class Vector2 +{ + public: + Vector2() { } + Vector2(const Vector2 & in) { x = in.x; y = in.y; } + Vector2(float inX, float inY) { x = inX; y = inY; } + ~Vector2() { } + + void Set(float inX, float inY) { x = inX; y = inY; } + void SetX(float inX) { x = inX; } + void SetY(float inY) { y = inY; } + void Get(float * outX, float * outY) { *outX = x; *outY = y; } + float GetX(void) { return x; } + float GetY(void) { return y; } + + void Normalize(void) { float mag = Magnitude(); x /= mag; y /= mag; } + float Magnitude(void) { return sqrt(x*x + y*y); } + + void Reverse(void) { float temp = -x; x = -y; y = temp; } + + void Scale(float scale) { x *= scale; y *= scale; } + + void SwapBytes(void) { SwapFloat(&x); SwapFloat(&y); } + + Vector2 & operator+=(const Vector2 & rhs) { x += rhs.x; y += rhs.y; return *this; } + Vector2 & operator-=(const Vector2 & rhs) { x -= rhs.x; y -= rhs.y; return *this; } + Vector2 & operator*=(float rhs) { x *= rhs; y *= rhs; return *this; } + Vector2 & operator/=(float rhs) { x /= rhs; y /= rhs; return *this; } + + float x; + float y; +}; + +inline Vector2 operator+(const Vector2 & lhs, const Vector2 & rhs) +{ + return Vector2(lhs.x + rhs.x, lhs.y + rhs.y); +}; + +inline Vector2 operator-(const Vector2 & lhs, const Vector2 & rhs) +{ + return Vector2(lhs.x - rhs.x, lhs.y - rhs.y); +}; + +inline Vector2 operator*(const Vector2 & lhs, float rhs) +{ + return Vector2(lhs.x * rhs, lhs.y * rhs); +}; + +inline Vector2 operator/(const Vector2 & lhs, float rhs) +{ + return Vector2(lhs.x / rhs, lhs.y / rhs); +}; + +inline bool MaskCompare(void * lhs, void * rhs, void * mask, UInt32 size) +{ + UInt8 * lhs8 = (UInt8 *)lhs; + UInt8 * rhs8 = (UInt8 *)rhs; + UInt8 * mask8 = (UInt8 *)mask; + + for(UInt32 i = 0; i < size; i++) + if((lhs8[i] & mask8[i]) != (rhs8[i] & mask8[i])) + return false; + + return true; +} + +class Vector3 +{ +public: + Vector3() { } + Vector3(const Vector3 & in) { x = in.x; y = in.y; z = in.z; } + Vector3(float inX, float inY, float inZ) { x = inX; y = inY; z = inZ; } + ~Vector3() { } + + void Set(float inX, float inY, float inZ) { x = inX; y = inY; z = inZ; } + void Get(float * outX, float * outY, float * outZ) { *outX = x; *outY = y; *outZ = z; } + + void Normalize(void) { float mag = Magnitude(); x /= mag; y /= mag; z /= mag; } + float Magnitude(void) { return sqrt(x*x + y*y + z*z); } + + void Scale(float scale) { x *= scale; y *= scale; z *= scale; } + + void SwapBytes(void) { SwapFloat(&x); SwapFloat(&y); SwapFloat(&z); } + + Vector3 & operator+=(const Vector3 & rhs) { x += rhs.x; y += rhs.y; z += rhs.z; return *this; } + Vector3 & operator-=(const Vector3 & rhs) { x -= rhs.x; y -= rhs.y; z -= rhs.z; return *this; } + Vector3 & operator*=(const Vector3 & rhs) { x *= rhs.x; y *= rhs.y; z *= rhs.z; return *this; } + Vector3 & operator/=(const Vector3 & rhs) { x /= rhs.x; y /= rhs.y; z /= rhs.z; return *this; } + + union + { + struct + { + float x, y, z; + }; + float d[3]; + }; +}; + +inline Vector3 operator+(const Vector3 & lhs, const Vector3 & rhs) +{ + return Vector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); +} + +inline Vector3 operator-(const Vector3 & lhs, const Vector3 & rhs) +{ + return Vector3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z); +} + +inline Vector3 operator*(const Vector3 & lhs, const Vector3 & rhs) +{ + return Vector3(lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z); +} + +inline Vector3 operator/(const Vector3 & lhs, const Vector3 & rhs) +{ + return Vector3(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z); +} diff --git a/common/common.vcproj b/common/common.vcproj new file mode 100644 index 0000000..ca58e22 --- /dev/null +++ b/common/common.vcproj @@ -0,0 +1,419 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/common.vcxproj b/common/common.vcxproj new file mode 100644 index 0000000..0a5c2b5 --- /dev/null +++ b/common/common.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6} + common + Perforce Project + . + MSSCCI:Perforce SCM + Win32Proj + + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + Debug\ + Debug\ + Release\ + Release\ + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebug + true + + + + + Level3 + EditAndContinue + common/IPrefix.h;%(ForcedIncludeFiles) + + + + + $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + true + + + + + Level3 + ProgramDatabase + common/IPrefix.h;%(ForcedIncludeFiles) + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters new file mode 100644 index 0000000..9352bd3 --- /dev/null +++ b/common/common.vcxproj.filters @@ -0,0 +1,196 @@ + + + + + {3116e955-2cbf-4759-a5ae-1fd8a64cf82f} + + + {2763d79e-c908-489f-98d7-e9033c13ee81} + + + {0189b4d3-578c-4b8e-9e73-ef2a5658a139} + + + {d245593e-d1c3-4f87-b989-1841f48a2837} + + + {454cfcb6-5332-4789-8092-3e68723f9eb2} + + + {324323c6-8765-4754-bc66-3d8cf12d3ad0} + + + {1c594a4b-a331-4eeb-826c-87f764586bbd} + + + {22a19b00-a69e-48b2-a637-78e10e06217b} + + + + + streams + + + streams + + + streams + + + streams + + + debug + + + debug + + + threads + + + threads + + + threads + + + threads + + + threads + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + utilities + + + utilities + + + utilities + + + memory + + + pipe + + + pipe + + + files + + + + + + streams + + + streams + + + streams + + + streams + + + debug + + + debug + + + threads + + + threads + + + threads + + + threads + + + threads + + + threads + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + datatypes + + + utilities + + + utilities + + + utilities + + + memory + + + pipe + + + pipe + + + files + + + + + + datatypes + + + \ No newline at end of file diff --git a/common/common_license.txt b/common/common_license.txt new file mode 100644 index 0000000..de4de6f --- /dev/null +++ b/common/common_license.txt @@ -0,0 +1,22 @@ +This license applies to all of the files in src/common: + +Copyright (c) 2006-2011 Ian Patterson + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. \ No newline at end of file diff --git a/common/common_vc11.sln b/common/common_vc11.sln new file mode 100644 index 0000000..4835e8a --- /dev/null +++ b/common/common_vc11.sln @@ -0,0 +1,24 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common_vc11", "common_vc11.vcxproj", "{D4C128A1-73DC-4941-A453-CE55AF239BA8}" +EndProject +Global + GlobalSection(SourceCodeControl) = preSolution + SccNumberOfProjects = 1 + SccLocalPath0 = . + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x64.ActiveCfg = Debug|x64 + {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Debug|x64.Build.0 = Debug|x64 + {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x64.ActiveCfg = Release|x64 + {D4C128A1-73DC-4941-A453-CE55AF239BA8}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/common/common_vc11.vcxproj b/common/common_vc11.vcxproj new file mode 100644 index 0000000..4d5bdc7 --- /dev/null +++ b/common/common_vc11.vcxproj @@ -0,0 +1,183 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {D4C128A1-73DC-4941-A453-CE55AF239BA8} + Win32Proj + common_vc11 + SAK + SAK + SAK + SAK + + + + StaticLibrary + true + v110 + Unicode + + + StaticLibrary + true + v110 + MultiByte + + + StaticLibrary + false + v110 + true + Unicode + + + StaticLibrary + false + v110 + true + MultiByte + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)_vc11\$(Configuration)\ + $(Platform)_vc11\$(Configuration)\ + + + $(SolutionDir)$(Platform)_vc11\$(Configuration)\ + $(Platform)_vc11\$(Configuration)\ + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\.. + MultiThreadedDebug + common/IPrefix.h + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\.. + MultiThreaded + common/IPrefix.h + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/common/common_vc11.vcxproj.filters b/common/common_vc11.vcxproj.filters new file mode 100644 index 0000000..dcf852c --- /dev/null +++ b/common/common_vc11.vcxproj.filters @@ -0,0 +1,107 @@ + + + + + + streams + + + streams + + + debug + + + debug + + + threads + + + threads + + + threads + + + streams + + + streams + + + datatypes + + + datatypes + + + utilities + + + utilities + + + + + + streams + + + streams + + + debug + + + debug + + + threads + + + threads + + + threads + + + threads + + + streams + + + streams + + + datatypes + + + datatypes + + + utilities + + + utilities + + + + + {9fbaeb6f-acb8-4ee5-ac77-3e3b4473acc0} + + + {c27df8b6-6fe8-4628-a6ee-51346b65cd8c} + + + {0496bc12-7477-42e4-87e7-d9565c164a32} + + + {09a8ab62-d5b8-4cb4-a24e-33761b7c98f9} + + + {0bd6b089-31f2-4bc9-acc5-4bf164af219b} + + + \ No newline at end of file diff --git a/common/common_vc14.sln b/common/common_vc14.sln new file mode 100644 index 0000000..5c220f2 --- /dev/null +++ b/common/common_vc14.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common_vc14", "common_vc14.vcxproj", "{472E19AB-DEF0-42DF-819B-18722E8DC822}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {472E19AB-DEF0-42DF-819B-18722E8DC822}.Debug|x64.ActiveCfg = Debug|x64 + {472E19AB-DEF0-42DF-819B-18722E8DC822}.Debug|x64.Build.0 = Debug|x64 + {472E19AB-DEF0-42DF-819B-18722E8DC822}.Release|x64.ActiveCfg = Release|x64 + {472E19AB-DEF0-42DF-819B-18722E8DC822}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(SourceCodeControl) = preSolution + SccNumberOfProjects = 2 + SccLocalPath0 = . + SccProjectUniqueName1 = common_vc14.vcxproj + SccLocalPath1 = . + EndGlobalSection +EndGlobal diff --git a/common/common_vc14.vcxproj b/common/common_vc14.vcxproj new file mode 100644 index 0000000..40cac95 --- /dev/null +++ b/common/common_vc14.vcxproj @@ -0,0 +1,130 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {472E19AB-DEF0-42DF-819B-18722E8DC822} + Win32Proj + common_vc14 + 10.0.18362.0 + + + + + + + + + + + + StaticLibrary + true + v140 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)_$(PlatformToolset)\$(Configuration)\ + $(Platform)_$(PlatformToolset)\$(Configuration)\ + + + $(SolutionDir)$(Platform)_$(PlatformToolset)\$(Configuration)\ + $(Platform)_$(PlatformToolset)\$(Configuration)\ + + + + + + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) + MultiThreadedDebug + common/IPrefix.h + + + Windows + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\..;%(AdditionalIncludeDirectories) + MultiThreaded + common/IPrefix.h + + + Windows + true + true + + + + + + \ No newline at end of file diff --git a/common/common_vc14.vcxproj.filters b/common/common_vc14.vcxproj.filters new file mode 100644 index 0000000..3cc066f --- /dev/null +++ b/common/common_vc14.vcxproj.filters @@ -0,0 +1,104 @@ + + + + + {0f6691fb-e192-419b-8bff-f445630e86a8} + + + {79afdc7c-80c1-4a8a-9e09-8a7884ac7cf4} + + + {8484df68-4f7a-496e-8427-11d2bdc94cdc} + + + {2fabee45-95d4-476f-b408-d7270ef65fce} + + + {6ae4c15e-7477-4acf-af9e-c571f5c902b4} + + + + + + streams + + + streams + + + debug + + + debug + + + threads + + + threads + + + threads + + + streams + + + streams + + + datatypes + + + datatypes + + + utilities + + + utilities + + + + + + streams + + + streams + + + debug + + + debug + + + threads + + + threads + + + threads + + + streams + + + streams + + + datatypes + + + datatypes + + + utilities + + + utilities + + + \ No newline at end of file diff --git a/common/common_vc9.vcproj b/common/common_vc9.vcproj new file mode 100644 index 0000000..11cfd49 --- /dev/null +++ b/common/common_vc9.vcproj @@ -0,0 +1,420 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/f4se/f4se/BSCollision.cpp b/f4se/f4se/BSCollision.cpp new file mode 100644 index 0000000..6b97c6c --- /dev/null +++ b/f4se/f4se/BSCollision.cpp @@ -0,0 +1 @@ +#include "f4se/BSCollision.h" diff --git a/f4se/f4se/BSCollision.h b/f4se/f4se/BSCollision.h new file mode 100644 index 0000000..b5344ab --- /dev/null +++ b/f4se/f4se/BSCollision.h @@ -0,0 +1,105 @@ +#pragma once + +#include "f4se/NiObjects.h" +#include "f4se/bhkWorld.h" + +class bhkPhysicsSystem; +class bhkMigrationWorld; + +// 18 +class NiCollisionObject : public NiObject +{ +public: + NiAVObject *m_pkSceneObject; // 10 +}; + +// 20 +class bhkNPCollisionObjectBase : public NiCollisionObject +{ +public: + UInt16 m_uFlags; // 18 +}; + +// 28 +class bhkNiCollisionObject : public NiCollisionObject +{ +public: + enum Flags + { + kFlags_Active_Pos = (1 << 0), + kFlags_ResetTrans_Pos = (1 << 1), + kFlags_Notify_Pos = (1 << 2), + kFlags_SetLocal_Pos = (1 << 3), + kFlags_DebugDisplay_Pos = (1 << 4), + kFlags_Usevel_Pos = (1 << 5), + kFlags_Reset_Pos = (1 << 6), + kFlags_Sync_On_Update = (1 << 7), + kFlags_Unused1 = (1 << 8), + kFlags_Unused2 = (1 << 9), + kFlags_Anim_Targeted = (1 << 10), + kFlags_Dismbebered_Limb = (1 << 11), + }; + + UInt16 m_uFlags; // 18 + NiPointer spWorldObject; // 20 +}; + +// 28 +class bhkCollisionObject : public bhkNiCollisionObject +{ +public: + +}; + +// 48 +class bhkBlendCollisionObject : public bhkCollisionObject +{ +public: + enum TransType + { + kTransType_Blend = 0, + kTransType_Havok, + kTransType_Keyframe, + kTransType_Undefined, + }; + + float fHeirGain; // 28 + float fVelGain; // 2C + hkpMotion::MotionType ePrevType; // 30 + bhkMigrationWorld *pStoredWorld; // 38 + int iForceAddCount; // 40 +}; + +// 28 +class bhkPCollisionObject : public bhkNiCollisionObject +{ +public: +}; + +// 30 +class bhkNPCollisionObject : public bhkNPCollisionObjectBase +{ +public: + enum UserBodyFlags + { + kFlag_StartAsKeyframed = 0x10000, + kFlag_KeyframedCollideWithStaticAndKeyframed = 0x4000000, + }; + + NiPointer spSystem; // 20 + UInt32 uiSystemBodyIdx; // 28 +}; + +// 30 +class bhkNPCollisionObjectUnlinked : public bhkNPCollisionObject +{ +public: +}; + +// 70 +class bhkNPCollisionProxyObject : public bhkNPCollisionObjectBase +{ + NiPointer spCollisionObject; // 20 + UInt64 unk28; // 28 + NiTransform OffsetTransf; // 30 +}; diff --git a/f4se/f4se/BSGeometry.cpp b/f4se/f4se/BSGeometry.cpp new file mode 100644 index 0000000..bd916d0 --- /dev/null +++ b/f4se/f4se/BSGeometry.cpp @@ -0,0 +1,3 @@ +#include "f4se/BSGeometry.h" + +RelocAddr <_ConvertHalfToFloat> ConvertHalfToFloat(0x006945C0); diff --git a/f4se/f4se/BSGeometry.h b/f4se/f4se/BSGeometry.h new file mode 100644 index 0000000..7ccbc31 --- /dev/null +++ b/f4se/f4se/BSGeometry.h @@ -0,0 +1,241 @@ +#pragma once + +#include "f4se/NiObjects.h" +#include "f4se/BSSkin.h" + +class NiProperty; +class ID3D11Buffer; + +// 38 +struct BSGeometrySegmentFlagData +{ + tHashSet SegmentDeltas; // 00 + BSFixedString BaseBoneName; // 30 +}; + +// 68 +class BSGeometrySegmentSharedData : public BSIntrusiveRefCounted +{ +public: + BSFixedString SSFFileName; // 08 + UInt32 uiNumSegments; // 10 + UInt32 uiTotalNumSegments; // 14 + UInt32 * pSegmentStarts; // 18 + + // 28 + struct PerSegmentSharedData + { + UInt32 uiUserIndex; // 00 + UInt32 uiBoneID; // 04 + float fValidCutOffsets[8]; // 08 + }; + + PerSegmentSharedData *pPerSegmentSharedData; // 20 + BSGeometrySegmentFlagData SegmentsEnabledData; // 28 + bool bProcessedCutOffsets; // 60 +}; + +// 40 +class BSGeometrySegmentData : public NiObject +{ +public: + // 18 + struct Segment + { + UInt32 uiStartIndex; // 00 + UInt32 uiNumPrimitives; // 04 + UInt32 uiParentArrayIndex; // 08 + UInt32 uiChildCount; // 0C + UInt8 ucDisabledCount; // 10 + }; + + // 8 + struct DrawData + { + UInt32 uiStartIndex; // 00 + UInt32 uiNumPrimitives; // 04 + }; + + BSGeometrySegmentSharedData * spSharedData; // 10 + Segment * pSegments; // 18 + DrawData * pSegmentDrawData; // 20 + UInt32 uiNumDraws; // 28 + UInt32 uiNumSegments; // 2C + UInt32 uiTotalNumSegments; // 30 + UInt32 uiTotalNumPrimitives; // 34 + UInt32 uiSegToZeroMap; // 38 + bool bSegmentsChanged; // 3C + bool bIgnoreSegments; // 3D +}; + +class BSGeometryData +{ +public: + UInt64 vertexDesc; + + struct VertexData + { + ID3D11Buffer * d3d11Buffer; // 00 - const CLayeredObjectWithCLS::CContainedObject::`vftable'{for `CPrivateDataImpl'} + UInt8 * vertexBlock; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + UInt64 unk28; // 28 + UInt64 unk30; // 30 + volatile SInt32 refCount; // 38 + }; + + struct TriangleData + { + ID3D11Buffer * d3d11Buffer; // 00 - Same buffer as VertexData + UInt16 * triangles; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + UInt64 unk28; // 28 + UInt64 unk30; // 30 + volatile SInt32 refCount; // 38 + }; + + VertexData * vertexData; // 08 + TriangleData * triangleData; // 10 + volatile SInt32 refCount; // 18 +}; + +// 160 +class BSGeometry : public NiAVObject +{ +public: + virtual void Unk_39(); + virtual void Unk_3A(); + virtual void Unk_3B(); + virtual void Unk_3C(); + virtual void Unk_3D(); + virtual void Unk_3E(); + virtual void Unk_3F(); + virtual void Unk_40(); + + NiBound kModelBound; // 120 + NiPointer effectState; // 130 + NiPointer shaderProperty; // 138 + NiPointer skinInstance; // 140 + + union VertexDesc + { + struct + { + UInt8 szVertexData : 4; + UInt8 szVertex : 4; // 0 when not dynamic + UInt8 oTexCoord0 : 4; + UInt8 oTexCoord1 : 4; + UInt8 oNormal : 4; + UInt8 oTangent : 4; + UInt8 oColor : 4; + UInt8 oSkinningData : 4; + UInt8 oLandscapeData : 4; + UInt8 oEyeData: 4; + UInt16 vertexFlags : 16; + UInt8 unused : 8; + }; + UInt64 vertexDesc; + }; + + enum : UInt64 + { + kFlag_Unk1 = (1ULL << 40), + kFlag_Unk2 = (1ULL << 41), + kFlag_Unk3 = (1ULL << 42), + kFlag_Unk4 = (1ULL << 43), + kFlag_Vertex = (1ULL << 44), + kFlag_UVs = (1ULL << 45), + kFlag_Unk5 = (1ULL << 46), + kFlag_Normals = (1ULL << 47), + kFlag_Tangents = (1ULL << 48), + kFlag_VertexColors = (1ULL << 49), + kFlag_Skinned = (1ULL << 50), + kFlag_Unk6 = (1ULL << 51), + kFlag_MaleEyes = (1ULL << 52), + kFlag_Unk7 = (1ULL << 53), + kFlag_FullPrecision = (1ULL << 54), + kFlag_Unk8 = (1ULL << 55), + }; + + BSGeometryData * geometryData; // 148 + UInt64 vertexDesc; // 150 + + UInt16 GetVertexSize() const { return (vertexDesc << 2) & 0x3C; } // 0x3C might be a compiler optimization, (vertexDesc & 0xF) << 2 makes more sense + + SInt8 ucType; // 158 + bool Registered; // 159 + UInt16 pad15A; // 15A + UInt32 unk15C; // 15C + + MEMBER_FN_PREFIX(BSGeometry); + // 523E6E56493B00C91D9A86659158A735D8A58371+B + DEFINE_MEMBER_FN(UpdateShaderProperty, UInt32, 0x028201D0); +}; +STATIC_ASSERT(sizeof(BSGeometry) == 0x160); + +// 170 +class BSTriShape : public BSGeometry +{ +public: + UInt32 numTriangles; // 160 + UInt16 numVertices; // 164 + UInt16 unk166; // 166 + float unk168; // 168 + float unk16C; // 16C + + MEMBER_FN_PREFIX(BSTriShape); + DEFINE_MEMBER_FN(CreateDynamicTriShape, BSDynamicTriShape*, 0x01D28150, NiAVObject * unk1); +}; +STATIC_ASSERT(sizeof(BSTriShape) == 0x170); + +// 1A0 +class BSDynamicTriShape : public BSTriShape +{ +public: + UInt32 uiDynamicDataSize; // 170 + UInt32 uiFrameCount; // 174 + SimpleLock lock; // 178 + UInt8 * dynamicVertices; // 180 - geometry pointer, must lock/unlock when altering + NiPointer spSegments; // 188 + void * unk190; // 190 + void * unk198; // 198 + + UInt16 GetDynamicVertexSize() const { return (vertexDesc >> 2) & 0x3C; } +}; +STATIC_ASSERT(sizeof(BSDynamicTriShape) == 0x1A0); + +// 190 +class BSSubIndexTriShape : public BSTriShape +{ +public: + NiPointer spSegments; // 170 + + struct SegmentData + { + ID3D11Buffer * d3d11Buffer; // 00 - const CLayeredObjectWithCLS::CContainedObject::vftable'{forCPrivateDataImpl'} + UInt8 * segmentBlock; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + void * unk28; // 28 + UInt32 unk30; // 30 + UInt32 unk34; // 34 + volatile SInt32 refCount; // 38 + }; + + SegmentData * segmentData; // 178 + UInt32 numIndices; // 180 + UInt32 unk184; // 184 + void * unk188; // 188 +}; +STATIC_ASSERT(sizeof(BSSubIndexTriShape) == 0x190); + +// Offset is the amount of bytes until the next iteration +// e.g. +// ConvertHalfToFloat(&in, 0x04, &out, 0x02, 1); +// Converts a single float to a single half-float +typedef void (* _ConvertHalfToFloat)(float * src, UInt64 offsetFloat, UInt16 * dest, UInt64 offsetHalf, UInt64 count); +extern RelocAddr <_ConvertHalfToFloat> ConvertHalfToFloat; diff --git a/f4se/f4se/BSGraphics.cpp b/f4se/f4se/BSGraphics.cpp new file mode 100644 index 0000000..1dabba7 --- /dev/null +++ b/f4se/f4se/BSGraphics.cpp @@ -0,0 +1,19 @@ +#include "f4se/BSGraphics.h" + +// 2CA5233612B3158658DB6DB9C90FD0258F1836E2+124 +RelocPtr g_renderer(0x067220E8); + +// 6BF9214E9DC5338FC817F85B0716990E5CA7C862+31 +RelocPtr g_renderManager(0x061E0900); + +// FA43F2F87927D8F20B17E756782BC91BB6BD04C2+3B +RelocPtr g_renderTargetManager(0x0384FD30); + +// 4A4A200E8F9173F8CE99D39E27F4BDAF680DF52B+9C +RelocPtr g_shaderResourceManager(0x05C08F08); + +// 84E19996C30AD51CE0D4AEF3E6ED8FFFF4AE4BD7+18 +RelocPtr g_D3D11Device(0x0609BF88); + +// BFDAF477B684098E9F394A636CCDC1BBD06EDEBF+25 +RelocPtr g_D3D11DeviceContext(0x061DDC60); diff --git a/f4se/f4se/BSGraphics.h b/f4se/f4se/BSGraphics.h new file mode 100644 index 0000000..0dcc7ac --- /dev/null +++ b/f4se/f4se/BSGraphics.h @@ -0,0 +1,82 @@ +#pragma once + +#include "f4se_common/Relocation.h" +#include "f4se_common/Utilities.h" + +#include "f4se/BSGeometry.h" +#include "f4se/NiTextures.h" + +class BSGeometryData; + +// ?? +class BSRenderManager +{ +public: + UInt64 unk2588[0x2590 >> 3]; // 2588 + CRITICAL_SECTION m_textureLock; // 2590 + + MEMBER_FN_PREFIX(BSRenderManager); + DEFINE_MEMBER_FN(CreateBSGeometryData, BSGeometryData*, 0x01D0BD60, UInt32 * blockSize, UInt8 * vertexData, UInt64 vertexDesc, BSGeometryData::TriangleData * triData); // Creates a block with a vertex copy in the resource pool with a reference to the supplied triblock (partial deep copy) +}; +STATIC_ASSERT(offsetof(BSRenderManager, m_textureLock) == 0x2590); + +class BSShaderResourceManager +{ +public: + virtual ~BSShaderResourceManager(); + + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); + virtual void Unk_06(); + virtual void Unk_07(); + virtual void IncGeometryRef(BSGeometryData * geomData); + virtual void DefGeometryRef(BSGeometryData * geomData); // Will auto-destroy the block when it reaches zero + //... + + // Unk_21(BSRenderData * rendererData); // Release texture? +}; + +// ?? +class BSRenderTargetManager +{ +public: + struct SharedTargetInfo + { + UInt32 width; // 00 - 400 + UInt32 height; // 04 - 400 + UInt32 unk08; // 08 - 1C + UInt32 unk0C; // 0C - 10000 + UInt32 unk10; // 10 - FFFFFFFF + UInt32 unk14; // 14 - r13d + UInt32 unk18; // 18 - r12b + }; + + MEMBER_FN_PREFIX(BSRenderTargetManager); + // D16605905EE44603E286262CE17CFC8383EABDDC+84 + DEFINE_MEMBER_FN(LockTextureType, void, 0x01D329B0, UInt32 type); + // D16605905EE44603E286262CE17CFC8383EABDDC+32F + DEFINE_MEMBER_FN(ReleaseTextureType, void, 0x01D32A40, UInt32 type); + DEFINE_MEMBER_FN(GetRenderData, BSRenderData *, 0x01D32910, UInt32 type, UInt64 unk1, UInt64 unk2, UInt32 unk3); // type, 0, 1, 0 + // D16605905EE44603E286262CE17CFC8383EABDDC+1EF + DEFINE_MEMBER_FN(Unk_01, void, 0x01D32960, UInt32 type, BSRenderData *, UInt8 unk3); // type, rendererData, 1 +}; + +// 1B8 +class BSRenderer +{ +public: + +}; + +struct ID3D11DeviceContext; +struct ID3D11Device; + +extern RelocPtr g_renderer; +extern RelocPtr g_renderManager; +extern RelocPtr g_renderTargetManager; +extern RelocPtr g_shaderResourceManager; +extern RelocPtr g_D3D11Device; +extern RelocPtr g_D3D11DeviceContext; diff --git a/f4se/f4se/BSLight.cpp b/f4se/f4se/BSLight.cpp new file mode 100644 index 0000000..31a0339 --- /dev/null +++ b/f4se/f4se/BSLight.cpp @@ -0,0 +1 @@ +#include "f4se/BSLight.h" diff --git a/f4se/f4se/BSLight.h b/f4se/f4se/BSLight.h new file mode 100644 index 0000000..73566a0 --- /dev/null +++ b/f4se/f4se/BSLight.h @@ -0,0 +1,83 @@ +#pragma once + +#include "f4se/GameTypes.h" +#include "f4se/NiTypes.h" +#include "f4se/NiObjects.h" + +class BSLight; +class NiLight; +class BSMultiBoundNode; +class BSPortal; +class BSPortalGraph; +class BSCullingProcess; +class NiCamera; +class NiTexture; +class BSLensFlareRenderData; + +// 28 +class BSShaderPropertyLightData +{ +public: + UInt32 uiLightListFence; // 00 + UInt32 uiShadowAccumFlags; // 04 + UInt32 uiLightListChanged; // 08 + tArray lLightList; // 10 +}; + +// 190 +class BSLight : public NiRefObject +{ +public: + float fLODDimmer; // 10 + float fLuminance; // 14 + UInt32 usFrustumCull; // 18 + UInt32 unk1C; // 1C - padding + float kGoboProjection[4][4]; // 20 - aligned to 16 + NiMatrix43 kShapeRotation; // 60 + NiPoint3A bPointPosition; // 90 + union + { + float fCenterAndHalfExtents[6]; + float fRadius; + float fSpotParam[2]; + }; // A0 + NiPointer spLight; // B8 + void * unkC0; // C0 + void * unkC8; // C8 + void * lFadeNodeList; // D0 - FadeNodeListT > + void * kGeomListFence; // D8 + void * unk0E0; // E0 + void * unkE8; // E8 + tArray kMultiboundRooms; // F0 + tArray kPortals; // 108 + tArray kProcessedNodes; // 120 + BSPortalGraph * pPortalGraph; // 138 + BSCullingProcess * pCullingProcess; // 140 + NiPointer spGeometry; // 148 + NiPointer spCamera; // 150 + NiPointer spGoboTexture; // 158 + UInt32 uiAddFadeNodeLock; // 160 + UInt32 LensFlareLastFrame; // 164 + UInt32 LensFlareFrameCount; // 168 + UInt32 LensFlareIndex; // 16C + bool bPointLight; // 170 + bool bAmbientLight; // 171 + bool bDynamicLight; // 172 + bool bPortalStrict; // 173 + bool bShowDebugTexture; // 174 + bool bAffectLand; // 175 + bool bAffectWater; // 176 + bool bLODFade; // 177 + bool bSpecular; // 178 + bool bAttenuationOnly; // 179 + bool bIgnoreRoughness; // 17A + bool bIgnoreRim; // 17B + bool bOccluded; // 17C + bool bStencilVolume; // 17D + bool bTemporary; // 17E + UInt8 unk174; // 17F + UInt32 kShape; // 180 BSLight::eShape + UInt32 unk17C; // 184 + BSLensFlareRenderData * pLensFlare; // 188 +}; +STATIC_ASSERT(sizeof(BSLight) == 0x190); diff --git a/f4se/f4se/BSModelDB.cpp b/f4se/f4se/BSModelDB.cpp new file mode 100644 index 0000000..a78d80f --- /dev/null +++ b/f4se/f4se/BSModelDB.cpp @@ -0,0 +1,4 @@ +#include "f4se/BSModelDB.h" + +// 76A9720CA598893620233A95993057BD7FAF98DD+71 +RelocPtr g_TESProcessor(0x058D3370); diff --git a/f4se/f4se/BSModelDB.h b/f4se/f4se/BSModelDB.h new file mode 100644 index 0000000..7066650 --- /dev/null +++ b/f4se/f4se/BSModelDB.h @@ -0,0 +1,50 @@ +#pragma once + +#include "f4se_common/Utilities.h" + +class NiAVObject; + +class BSModelDB +{ +public: + struct ModelData + { + UInt64 unk00; // 00 + + enum + { + kFlag_Unk1 = (1 << 0), + kFlag_Dynamic = (1 << 1), + kFlag_PostProcess = (1 << 3), + kFlag_Unk2 = (1 << 4), + kFlag_Unk3 = (1 << 5) + }; + + UInt8 modelFlags; // 08 + // ... + }; + + class BSModelProcessor + { + public: + virtual ~BSModelProcessor() { }; + + virtual void Process(ModelData * modelData, const char * modelName, NiAVObject ** root, UInt32 * typeOut) { }; + }; + + class TESProcessor : public BSModelProcessor + { + public: + TESProcessor() { } + virtual ~TESProcessor() { CALL_MEMBER_FN(this, dtor)(); }; + + virtual void Process(ModelData * modelData, const char * modelName, NiAVObject ** root, UInt32 * typeOut) override { CALL_MEMBER_FN(this, Impl_Process)(modelData, modelName, root, typeOut); } + + MEMBER_FN_PREFIX(TESProcessor); + // ??_7BSModelProcessor@BSModelDB@@6B@ + DEFINE_MEMBER_FN(dtor, void, 0x02C57BC8); + DEFINE_MEMBER_FN(Impl_Process, void, 0x00137E50, ModelData * modelData, const char * modelName, NiAVObject ** root, UInt32 * typeOut); + }; +}; + +extern RelocPtr g_TESProcessor; diff --git a/f4se/f4se/BSParticleShaderEmitter.cpp b/f4se/f4se/BSParticleShaderEmitter.cpp new file mode 100644 index 0000000..c9568f6 --- /dev/null +++ b/f4se/f4se/BSParticleShaderEmitter.cpp @@ -0,0 +1 @@ +#include "f4se/BSParticleShaderEmitter.h" diff --git a/f4se/f4se/BSParticleShaderEmitter.h b/f4se/f4se/BSParticleShaderEmitter.h new file mode 100644 index 0000000..139dcbc --- /dev/null +++ b/f4se/f4se/BSParticleShaderEmitter.h @@ -0,0 +1,67 @@ +#pragma once + +#include "f4se/NiTypes.h" +#include "f4se/NiObjects.h" + +class BSParticleShaderProperty; + +// 60 +class BSParticleShaderEmitter : public NiRefObject +{ +protected: + BSParticleShaderProperty * pProperty; // 10 + UInt16 uiEmitterType; // 18 + UInt16 uiParticleCount; // 1A + float fAlpha; // 1C + float fGenerateRemainder; // 20 + float fMaxParticleRatio; // 24 + + // 8 + struct TextureAnimInfo + { + float fCurrentFrame; // 00 + UInt32 usTotalFrames; // 04 + }; + + TextureAnimInfo * pTextureAnims; // 28 + + // 30 + struct ParticleData + { + float fXPos; // 00 + float fYPos; // 04 + float fZPos; // 08 + float fAge; // 0C + float fXVel; // 10 + float fYVel; // 14 + float fZVel; // 18 + float fLifeAdjust; // 1C + float fRotationStart; // 20 + float fRotationSpeed; // 24 + float fSpeedMult; // 28 + char ucTexCoordU; // 2C + char ucTexCoordV; // 2D + char ucVertexX; // 2E + char ucVertexY; // 2F + }; + + ParticleData InstanceDataA[78]; // 30 +}; + +// 2B0 +class BSParticleShaderCubeEmitter : public BSParticleShaderEmitter +{ +public: + float kOcclusionWorldViewProj[4][4]; // 60 + NiPoint3 kCameraOffsetVector; // A0 + NiPoint3 kOffsetVector; // AC + NiPoint3 kCompositeOffsetVector; // B8 + NiPoint3 kFrameVelocityVector; // C4 + NiPoint3 kWindVelocity; // D0 + NiPoint3 kGravityVelocity; // DC + float fRotation; // C0 + float fRotationVelocity; // C4 + float fDensity; // C8 + float fEnvCubeSize; // CC + NiPoint3 pInstanceOffset[40]; // D0 +}; diff --git a/f4se/f4se/BSSkin.cpp b/f4se/f4se/BSSkin.cpp new file mode 100644 index 0000000..c5aa58e --- /dev/null +++ b/f4se/f4se/BSSkin.cpp @@ -0,0 +1 @@ +#include "f4se/BSSkin.h" diff --git a/f4se/f4se/BSSkin.h b/f4se/f4se/BSSkin.h new file mode 100644 index 0000000..dde6db8 --- /dev/null +++ b/f4se/f4se/BSSkin.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include "NiObjects.h" + +class BSSkin +{ +public: + // 28 + class BoneData : public NiObject + { + public: + struct BoneTransforms + { + NiPoint3 boundingSphere; // 00 + float radius; // 0C + NiTransform transform; // 10 + }; + + tArray transforms; // 10 + void * unk20; // 20 + }; + + // C0 + class Instance : public NiObject + { + public: + tArray bones; // 10 + tArray worldTransforms; // 28 - These are pointers to the corresponding bone's worldTransforms + BoneData * boneData; // 40 + NiAVObject * rootNode; // 48 + void * unk50; // 50 + UInt64 unk58; // 58 + __m128 unk60; // 60 + __m128 unk70; // 64 + __m128 unk80; // 68 + __m128 unk90; // 90 + void * unkA0; // A0 + void * unkA8; // A8 + UInt64 unkB0; // B0 + UInt32 unkB8; // B8 + SInt32 unkBC; // BC + }; +}; diff --git a/f4se/f4se/CustomMenu.cpp b/f4se/f4se/CustomMenu.cpp new file mode 100644 index 0000000..8c2ad3a --- /dev/null +++ b/f4se/f4se/CustomMenu.cpp @@ -0,0 +1,108 @@ +#include "f4se/CustomMenu.h" +#include "f4se/ScaleformLoader.h" + +BSReadWriteLock g_customMenuLock; +std::unordered_map g_customMenuData; + +CustomMenu::CustomMenu() : GameMenuBase() +{ + +} + +// Normally this code would happen in the constructor, but the menu name isn't set until after construction +void LoadCustomMenu_Hook(IMenu * menu) +{ + BSReadAndWriteLocker locker(&g_customMenuLock); + auto menuData = g_customMenuData.find(menu->menuName.c_str()); + if(menuData != g_customMenuData.end()) + { + BSFixedString menuPath = menuData->second.menuPath; + BSFixedString rootPath = menuData->second.rootPath; + UInt32 movieFlags = menuData->second.movieFlags; + UInt32 extFlags = menuData->second.extFlags; + menu->flags = menuData->second.menuFlags; + menu->depth = menuData->second.depth; + + if((menu->flags & IMenu::kFlag_ShowCursor) && (extFlags & CustomMenuData::kExtFlag_CheckForGamepad)) + { + if((*g_inputDeviceMgr)->IsGamepadEnabled()) + menu->flags &= ~IMenu::kFlag_ShowCursor; + } + + if (CALL_MEMBER_FN((*g_scaleformManager), LoadMovie)(menu, menu->movie, menuPath.c_str(), rootPath.c_str(), movieFlags)) + { + menu->stage.SetMember("menuFlags", &GFxValue(menu->flags)); + menu->stage.SetMember("movieFlags", &GFxValue(movieFlags)); + menu->stage.SetMember("extendedFlags", &GFxValue(extFlags)); + + GameMenuBase * gameMenu = static_cast(menu); + + CreateBaseShaderTarget(gameMenu->shaderTarget, menu->stage); + + if(extFlags & CustomMenuData::kExtFlag_InheritColors) + { + gameMenu->shaderTarget->SetFilterColor(false); + (*g_colorUpdateDispatcher)->eventDispatcher.AddEventSink(gameMenu->shaderTarget); + } + + if (menu->flags & IMenu::kFlag_ApplyDropDownFilter) + { + gameMenu->subcomponents.Push(gameMenu->shaderTarget); + } + } + } +} + +void CustomMenu::RegisterFunctions() +{ + RegisterNativeFunction("PlaySound", 0); + RegisterNativeFunction("OpenMenu", 1); + RegisterNativeFunction("CloseMenu", 2); +} + +void CustomMenu::Invoke(Args * args) +{ + switch (args->optionID) + { + case 0: + { + if(args->numArgs >= 1) + { + if(args->args[0].IsString()) + PlayUISound(args->args[0].GetString()); + } + } + break; + case 1: + { + if(args->numArgs >= 1) + { + if(args->args[0].IsString()) + { + BSFixedString menuName(args->args[0].GetString()); + CALL_MEMBER_FN(*g_uiMessageManager, SendUIMessage)(menuName, kMessage_Open); + } + } + } + break; + case 2: + { + if(args->numArgs >= 1) + { + if(args->args[0].IsString()) + { + BSFixedString menuName(args->args[0].GetString()); + CALL_MEMBER_FN(*g_uiMessageManager, SendUIMessage)(menuName, kMessage_Close); + } + } + } + break; + default: + break; + } +} + +IMenu * CreateCustomMenu() +{ + return new CustomMenu(); +} \ No newline at end of file diff --git a/f4se/f4se/CustomMenu.h b/f4se/f4se/CustomMenu.h new file mode 100644 index 0000000..009dd6f --- /dev/null +++ b/f4se/f4se/CustomMenu.h @@ -0,0 +1,36 @@ +#pragma once + +#include "f4se/GameMenus.h" +#include +#include + +struct CustomMenuData +{ + BSFixedString menuPath; + BSFixedString rootPath; + UInt32 menuFlags; + UInt32 movieFlags; + UInt32 extFlags; + UInt32 depth; + + enum ExtendedFlags + { + kExtFlag_InheritColors = 1, + kExtFlag_CheckForGamepad = 2 + }; +}; + +extern BSReadWriteLock g_customMenuLock; +extern std::unordered_map g_customMenuData; + +class CustomMenu : public GameMenuBase +{ +public: + CustomMenu(); + + virtual void RegisterFunctions() override; + virtual void Invoke(Args * args) override final; +}; + +void LoadCustomMenu_Hook(IMenu * menu); +IMenu * CreateCustomMenu(); \ No newline at end of file diff --git a/f4se/f4se/GameAPI.cpp b/f4se/f4se/GameAPI.cpp new file mode 100644 index 0000000..8ca65c8 --- /dev/null +++ b/f4se/f4se/GameAPI.cpp @@ -0,0 +1,41 @@ +#include "f4se/GameAPI.h" + +// B53CEF7AA7FC153E48CDE9DBD36CD8242577E27F+11D +RelocPtr g_mainHeap(0x038CC980); + +void * Heap_Allocate(size_t size) +{ + return CALL_MEMBER_FN(g_mainHeap, Allocate)(size, 0, false); +} + +void Heap_Free(void * ptr) +{ + CALL_MEMBER_FN(g_mainHeap, Free)(ptr, false); +} + +// CF40EA3DCB94FC3927A17CCA60198108D4742CA7+68 +RelocPtr g_console(0x058E0AE0); + +// 1C0F98B1DC3F82F9BD55E938765C22AD25B75571+15 +RelocAddr g_consoleHandle(0x05ADB4A8); + +void Console_Print(const char * fmt, ...) +{ + ConsoleManager * mgr = *g_console; + if(mgr) + { + va_list args; + va_start(args, fmt); + + CALL_MEMBER_FN(mgr, VPrint)(fmt, args); + + va_end(args); + } +} + +LONGLONG GetPerfCounter(void) +{ + LARGE_INTEGER li; + QueryPerformanceCounter(&li); + return li.QuadPart; +} diff --git a/f4se/f4se/GameAPI.h b/f4se/f4se/GameAPI.h new file mode 100644 index 0000000..5c21073 --- /dev/null +++ b/f4se/f4se/GameAPI.h @@ -0,0 +1,31 @@ +#pragma once + +#include "f4se_common/Utilities.h" + +class Heap +{ +public: + MEMBER_FN_PREFIX(Heap); + DEFINE_MEMBER_FN(Allocate, void *, 0x01B0EFD0, size_t size, size_t alignment, bool aligned); + DEFINE_MEMBER_FN(Free, void, 0x01B0F2E0, void * buf, bool aligned); +}; + +extern RelocPtr g_mainHeap; + +void * Heap_Allocate(size_t size); +void Heap_Free(void * ptr); + +class ConsoleManager +{ +public: + MEMBER_FN_PREFIX(ConsoleManager); + DEFINE_MEMBER_FN(VPrint, void, 0x01262EC0, const char * fmt, va_list args); + DEFINE_MEMBER_FN(Print, void, 0x01262F50, const char * str); +}; + +extern RelocPtr g_console; +extern RelocAddr g_consoleHandle; + +void Console_Print(const char * fmt, ...); + +LONGLONG GetPerfCounter(void); diff --git a/f4se/f4se/GameCamera.cpp b/f4se/f4se/GameCamera.cpp new file mode 100644 index 0000000..7f1fbb1 --- /dev/null +++ b/f4se/f4se/GameCamera.cpp @@ -0,0 +1,14 @@ +#include "f4se/GameCamera.h" + +// 05C2864F8B39388E329CE28479B6A5636B4D529C+11 +RelocPtr g_playerCamera(0x058CEB28); + +SInt32 PlayerCamera::GetCameraStateId(TESCameraState * state) +{ + for(int i = 0; i < kNumCameraStates; i++) { + if(state == cameraStates[i]) + return i; + } + + return -1; +} diff --git a/f4se/f4se/GameCamera.h b/f4se/f4se/GameCamera.h new file mode 100644 index 0000000..b9a85ce --- /dev/null +++ b/f4se/f4se/GameCamera.h @@ -0,0 +1,139 @@ +#pragma once + +#include "f4se_common/Relocation.h" +#include "f4se/GameEvents.h" +#include "f4se/GameInput.h" +#include "f4se/NiTypes.h" + +class NiNode; +class TESCameraState; + +// 24 +class TESCameraState : public BSIntrusiveRefCounted, public BSInputEventUser +{ +public: + virtual ~TESCameraState(); + + virtual void Unk_09(); + virtual void Unk_0A(); + virtual void Unk_0B(void * arg); + virtual void GetRotation(NiQuaternion * out); + virtual void GetPosition(NiPoint3 * out); + virtual void Unk_0E(); + virtual void Unk_0F(); + virtual void Unk_10(); + + void * unk18; // 18 + UInt32 stateID; // 20 + UInt32 pad24; // 24 +}; + +STATIC_ASSERT(sizeof(TESCameraState) == 0x28); + +// 138 +class ThirdPersonState : public TESCameraState +{ +public: + virtual ~ThirdPersonState(); + + virtual void UpdateMode(bool weaponDrawn); + virtual bool Unk_12(); + virtual void Unk_13(); + virtual void Unk_14(); + virtual void Unk_15(); +}; + +// 38 +class TESCamera +{ +public: + virtual ~TESCamera(); + + virtual void SetCameraNode(NiNode * node); + virtual void Unk_02(UInt8 unk1); // Sets 0x30 + virtual void Unk_03(); + + float unk08; // 08 + float unk0C; // 0C + float unk10; // 10 + float unk14; // 14 + float unk18; // 18 + UInt32 unk1C; // 1C + NiNode * cameraNode; // 20 + TESCameraState * cameraState; // 28 + UInt8 unk30; // 30 + UInt8 unk31; // 31 + UInt16 unk32; // 32 + UInt32 unk34; // 34 + + MEMBER_FN_PREFIX(TESCamera); + DEFINE_MEMBER_FN(SetCameraState, void, 0x0082E930, TESCameraState * cameraState); +}; + +class PlayerCamera : public TESCamera +{ +public: + virtual ~PlayerCamera(); + + enum + { + kCameraState_FirstPerson = 0, + kCameraState_AutoVanity, + kCameraState_VATS, + kCameraState_Free, + kCameraState_IronSights, + kCameraState_Transition, + kCameraState_TweenMenu, + kCameraState_ThirdPerson1, + kCameraState_ThirdPerson2, + kCameraState_Furniture, + kCameraState_Horse, + kCameraState_Bleedout, + kCameraState_Dialogue, + kNumCameraStates + }; + + BSInputEventReceiver inputEventReceiver; // 38 + BSTEventSink idleInputSink; // 48 + BSTEventSink userEventEnabledSink; // 50 + BSTEventSink otherEventEnabledSink; // 58 + + UInt32 unk60; // 60 + UInt32 unk64; // 64 - Handle + UInt32 unk68; // 68 + UInt32 unk6C; // 6C + + UInt64 unk70[(0xE0 - 0x70) >> 3]; + + TESCameraState * cameraStates[kNumCameraStates]; // E0 + UInt64 unk148; // 148 + UInt64 unk150; // 150 - hknpSphereShape + UInt64 unk158; // 158 - hknpBSWorld + UInt32 unk160; // 160 + UInt32 unk164; // 164 - Handle + float fDefaultWorldFov; // 168 - fDefaultWorldFOV:Display + float fDefault1stPersonFOV; // 16C - fDefault1stPersonFOV:Display + UInt64 unk170; // 170 + UInt64 unk178; // 178 + float unk180; // 180 + float unk184; // 184 + float unk188; // 188 + float unk18C; // 18C + float unk190; // 190 + float unk194; // 194 + float unk198; // 198 + float unk19C; // 19C + SInt32 unk1A0; // 1A0 + UInt8 unk1A4; // 1A4 + UInt8 unk1A5; // 1A5 + UInt8 unk1A6; // 1A6 + UInt8 unk1A7; // 1A7 + UInt8 unk1A8; // 1A8 + UInt8 unk1A9; // 1A9 + + SInt32 GetCameraStateId(TESCameraState * state); +}; +STATIC_ASSERT(offsetof(PlayerCamera, cameraStates) == 0xE0); +STATIC_ASSERT(offsetof(PlayerCamera, unk148) == 0x148); + +extern RelocPtr g_playerCamera; diff --git a/f4se/f4se/GameCustomization.cpp b/f4se/f4se/GameCustomization.cpp new file mode 100644 index 0000000..3a33b9f --- /dev/null +++ b/f4se/f4se/GameCustomization.cpp @@ -0,0 +1,170 @@ +#include "f4se/GameCustomization.h" +#include "f4se/GameAPI.h" +#include "f4se/GameObjects.h" + +// 6E6D6B9C5754133F46724CAD540B520D846299B9+B5 +RelocPtr g_characterCreation(0x05A67420); // array +// 6E6D6B9C5754133F46724CAD540B520D846299B9+AF +RelocPtr g_characterIndex(0x05A67440); + +// 89B16A159EF66C1743643F2F457380448C4803F0+18 +RelocPtr g_faceGenManager(0x058D0880); + +// E5618E306F15B8DF84D22F68B984045D0DD91165+29 +RelocPtr g_customizationDummy1(0x059DAD98); // Either Nora or Nate's dummy actors +// E5618E306F15B8DF84D22F68B984045D0DD91165+32 +RelocPtr g_customizationDummy2(0x059DAD60); // Either Nora or Nate's dummy actors + +RelocAddr<_CreateCharacterTintEntry> CreateCharacterTintEntry(0x002A5750); +RelocAddr<_CopyCharacterTints> CopyCharacterTints(0x002A4740); +RelocAddr<_ClearCharacterTints> ClearCharacterTints(0x002AA9C0); + +RelocAddr <_FillTintTemplates> FillTintTemplates(0x002A48E0); // For manipulating the tint lists, their signatures aren't quite right yet +RelocAddr <_MergeTintTextures> MergeTintTextures(0x00689BB0); +RelocAddr <_CreateMergeTintTextures> CreateMergeTintTextures(0x006899A0); + +// 8290027174FC425F1C5C8233B65132B99D1A37E1+F6 +RelocPtr > g_morphIntensityMap(0x03715370 - 0x08); + +// These are for creating new instances + +// ??_7Mask@Template@BGSCharacterTint@@6B@ +RelocAddr s_BGSCharacterTint_Template_MaskVtbl(0x02C6AD88); +// ??_7Palette@Template@BGSCharacterTint@@6B@ +RelocAddr s_BGSCharacterTint_Template_PaletteVtbl(0x02C6ADB8); +// ??_7TextureSet@Template@BGSCharacterTint@@6B@ +RelocAddr s_BGSCharacterTint_Template_TextureSetVtbl(0x02C6ADE8); + +bool BGSCharacterTint::Entry::IsEqual(Entry * rhs) +{ + return GetType() == rhs->GetType() && tintIndex == rhs->tintIndex && percent == rhs->percent; +} + +bool BGSCharacterTint::PaletteEntry::IsEqual(Entry * rhs) +{ + return __super::IsEqual(rhs) && color.bgra == ((PaletteEntry*)rhs)->color.bgra && colorID == ((PaletteEntry*)rhs)->colorID; +} + +void BGSCharacterTint::Entry::Copy(Entry * rhs) +{ + if(GetType() == rhs->GetType()) + { + percent = rhs->percent; + } +} + +void BGSCharacterTint::PaletteEntry::Copy(Entry * rhs) +{ + PaletteEntry * pRHS = (PaletteEntry*)rhs; + if(GetType() == pRHS->GetType()) + { + percent = pRHS->percent; + color = pRHS->color; + colorID = pRHS->colorID; + } +} + +BGSCharacterTint::Template::Entry * BGSCharacterTint::Template::Entry::Create(UInt32 size, UInt64 vtbl) +{ + void* memory = Heap_Allocate(size); + memset(memory, 0, size); + ((UInt64*)memory)[0] = vtbl; + BGSCharacterTint::Template::Entry* xData = (BGSCharacterTint::Template::Entry*)memory; + return xData; +} + +BGSCharacterTint::Template::Mask * BGSCharacterTint::Template::Mask::Create() +{ + return (BGSCharacterTint::Template::Mask *)BGSCharacterTint::Template::Entry::Create(sizeof(BGSCharacterTint::Template::Mask), s_BGSCharacterTint_Template_MaskVtbl.GetUIntPtr()); +} + +BGSCharacterTint::Template::Palette * BGSCharacterTint::Template::Palette::Create() +{ + return (BGSCharacterTint::Template::Palette *)BGSCharacterTint::Template::Entry::Create(sizeof(BGSCharacterTint::Template::Palette), s_BGSCharacterTint_Template_PaletteVtbl.GetUIntPtr()); +} + +BGSCharacterTint::Template::TextureSet * BGSCharacterTint::Template::TextureSet::Create() +{ + return (BGSCharacterTint::Template::TextureSet *)BGSCharacterTint::Template::Entry::Create(sizeof(BGSCharacterTint::Template::TextureSet), s_BGSCharacterTint_Template_TextureSetVtbl.GetUIntPtr()); +} + +BGSCharacterTint::Template::Palette::ColorData * BGSCharacterTint::Template::Palette::GetColorDataByID(UInt16 colorID) +{ + for(UInt32 i = 0; i < colors.count; i++) { + if(colors[i].colorID == colorID) { + return &colors[i]; + } + } + + return nullptr; +} + +BGSCharacterTint::Template::Entry * CharacterCreation::CharGenData::GetTemplateBySlot(UInt32 slotType) +{ + if(!tintData) + return nullptr; + + for(UInt32 i = 0; i < tintData->count; i++) + { + CharacterCreation::TintData * data; + tintData->GetNthItem(i, data); + + BGSCharacterTint::Template::Entry* entry = data->GetTemplateBySlot(slotType); + if(entry) + return entry; + } + + return nullptr; +} + +BGSCharacterTint::Template::Entry * CharacterCreation::CharGenData::GetTemplateByIndex(UInt16 index) +{ + if(!tintData) + return nullptr; + + for(UInt32 i = 0; i < tintData->count; i++) + { + CharacterCreation::TintData * data; + tintData->GetNthItem(i, data); + + BGSCharacterTint::Template::Entry* entry = data->GetTemplateByIndex(index); + if(entry) + return entry; + } + + return nullptr; +} + +BGSCharacterTint::Template::Entry * CharacterCreation::TintData::GetTemplateByIndex(UInt16 index) +{ + for(UInt32 k = 0; k < entry.count; k++) + { + BGSCharacterTint::Template::Entry* templateEntry; + entry.GetNthItem(k, templateEntry); + + if(templateEntry->templateIndex == index) + return templateEntry; + } + + return nullptr; +} + +BGSCharacterTint::Template::Entry * CharacterCreation::TintData::GetTemplateBySlot(UInt32 slotType) +{ + for(UInt32 k = 0; k < entry.count; k++) + { + BGSCharacterTint::Template::Entry* templateEntry; + entry.GetNthItem(k, templateEntry); + + if(templateEntry->slot == slotType) + return templateEntry; + } + + return nullptr; +} + +void CharacterCreation::MorphIntensity::Dump(void) +{ + _MESSAGE("\t\tFormID: %08X", npc->formID); + _MESSAGE("\t\tintensity: %f", morphIntensity); +} diff --git a/f4se/f4se/GameCustomization.h b/f4se/f4se/GameCustomization.h new file mode 100644 index 0000000..9bb5861 --- /dev/null +++ b/f4se/f4se/GameCustomization.h @@ -0,0 +1,476 @@ +#pragma once + +#include "f4se_common/Relocation.h" +#include "f4se_common/Utilities.h" + +#include "f4se/GameUtilities.h" +#include "f4se/GameTypes.h" + +class Actor; +class TESNPC; +class BGSHeadPart; +class BGSColorForm; +class BGSTextureSet; +class ActorValueInfo; +class Condition; +class NiAVObject; +class BSTriShape; + +class BGSCharacterTint +{ +public: + class Template + { + public: + // 20 + class Entry + { + public: + virtual ~Entry(); + + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Load(void * unk1) = 0; // Loads template from plugin stream + + enum + { + kSlotForeheadMask = 0, + kSlotEyesMask, + kSlotNoseMask, + kSlotEarsMask, + kSlotCheeksMask, + kSlotMouthMask, + kSlotNeckMask, + kSlotLipColor, + kSlotCheekColor, + kSlotEyeliner, + kSlotEyeSocketUpper, + kSlotEyeSocketLower, + kSlotSkinTone, + kSlotPaint, + kSlotLaughLines, + kSlotCheekColorLower, + kSlotNose, + kSlotChin, + kSlotNeck, + kSlotForehead, + kSlotDirt, + kSlotScar, + kSlotFaceDetail, + kSlotBrows, + kNumSlots + }; + + enum + { + kBlendOpDefault, + kBlendOpMultiply, + kBlendOpOverlay, + kBlendOpSoftLight, + kBlendOpHardLight, + }; + + enum + { + kFlagOnOff = (1 << 0), + kFlagChargenDetail = (1 << 1), + kFlagTakesSkinTone = (1 << 2) + }; + + BSFixedString name; // 08 + Condition * conditions; // 10 + UInt32 slot; // 18 + UInt16 templateIndex; // 1C + UInt8 flags; // 1E + UInt8 unk1F; // 1F + + static Entry * Create(UInt32 size, UInt64 vtbl); + }; + + // 30 + class Mask : public Entry + { + public: + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Load(void * unk1); + + BSFixedString texture; // 20 + UInt32 blendOp; // 28 + UInt32 unk2C; // 2C + + static Mask * Create(); + }; + + // 48 + class Palette : public Entry + { + public: + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Load(void * unk1); + + struct ColorData + { + BGSColorForm * colorForm; // 00 + float alpha; // 08 + UInt32 blendOp; // 0C + UInt16 colorID; // 10 + UInt16 unk12; // 12 + UInt32 unk14; // 14 + }; + + BSFixedString texture; // 20 + UInt32 unk28; // 28 + UInt32 unk2C; // 2C + tArray colors; // 30 + + static Palette * Create(); + ColorData * GetColorDataByID(UInt16 colorID); + }; + + // 40 + class TextureSet : public Entry + { + public: + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Load(void * unk1); + + BSFixedString diffuse; // 20 + BSFixedString normal; // 28 + BSFixedString specular; // 30 + UInt32 blendOp; // 38 + float defaultValue; // 3C + + static TextureSet * Create(); + }; + }; + + // 18 + class Entry + { + public: + virtual ~Entry(); + + virtual bool IsEqual(Entry * rhs); + virtual void Copy(Entry * rhs); + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual UInt32 GetType(void) = 0; + virtual void Unk_06(void) { }; + + enum + { + kTypeMask = 0, + kTypePalette, + kTypeTexture + }; + + Template::Entry * templateEntry; // 08 + UInt16 tintIndex; // 10 + UInt8 percent; // 12 divided by 100 + UInt8 pad13; // 13 + UInt32 pad14; // 14 + }; + + // 18 + class MaskEntry : public Entry + { + public: + virtual ~MaskEntry(); + + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual UInt32 GetType(void) { return kTypeMask; }; // 0 + virtual void Unk_06(void) { }; + }; + + // 20 + class PaletteEntry : public MaskEntry + { + public: + virtual ~PaletteEntry(); + + virtual bool IsEqual(Entry * rhs); + virtual void Copy(Entry * rhs); + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual UInt32 GetType(void) { return kTypePalette; }; // 1 + virtual void Unk_06(void); + + union Color + { + struct Channel + { + UInt8 red; + UInt8 green; + UInt8 blue; + UInt8 unused; + } channel; // 18 + UInt32 bgra; + } color; + + SInt16 colorID; // 1C - ID of the color from the template's list + UInt16 pad1E; // 1E + }; + + // 18 + class TextureSetEntry : public Entry + { + public: + virtual ~TextureSetEntry(); + + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual UInt32 GetType(void) { return kTypeTexture; }; // 2 + virtual void Unk_06(void) { }; + }; + +}; + +// 548 +class CharacterCreation +{ +public: + UInt64 unk00; // 00 + void * unk08; // 08 + UInt64 unk10; // 10 4 + UInt64 unk18; // 18 1 + UInt64 unk20; // 20 + UInt64 unk28; // 28 + UInt64 unk30; // 30 + UInt64 unk38; // 38 + UInt64 unk40; // 40 + UInt64 unk48; // 48 + UInt64 unk50; // 50 + Actor * actor; // 58 + TESNPC * npc; // 60 + TESNPC * npc2; // 68 + + struct TintData + { + BSFixedString category; // 00 + SInt32 unk08; // 08 + UInt32 type; // 0C + tArray entry; // 10 BGSCharacterTint::Template::Entry + + BGSCharacterTint::Template::Entry * GetTemplateByIndex(UInt16 index); + BGSCharacterTint::Template::Entry * GetTemplateBySlot(UInt32 slotType); + }; + + // 40 + struct MorphGroup + { + BSFixedString name; // 00 - MPGN + struct Preset + { + BSFixedString name; // 00 - MPPN + BSFixedString morph; // 08 - MPPM + BGSTextureSet * texture; // 10 - MPPT + UInt32 unk18; // 18 - MPPF + UInt32 index; // 1C - MPPI (morph index?) + }; + tArray presets; // 08 - MPPC (count) + tArray unk20; // 20 + UInt32 key; // 38 - MPPK (group key?) + }; + + // 90 + struct FaceMorphRegion + { + UInt64 unk00; // 00 + UInt64 unk08; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + BSFixedString name; // 28 - FMRN + UInt32 * unk30; // 30 + UInt32 unk30_capacity; // 38 + UInt32 unk30_size; // 3C + struct Data + { + void * key; // 00 - ?? + UInt64 unk08; // 08 + UInt64 unk10; // 10 + BSFixedString bone; // 18 + float * values; // 20 + + void Dump(void) + { + _MESSAGE("\t\tkey: %16lX bone: %s unk08: %16lX unk10: %16lX", key, bone.data ? bone.data->Get() : "", unk08, unk10); + if(values) + { + for(UInt32 i = 0; i < 4; i++) + _MESSAGE("\t\tdata: %f", values[i]); + } + } + }; + tHashSet dataSet; // 40 + UInt64 unk70; // 70 + UInt64 unk78; // 78 + UInt64 unk80; // 80 + UInt32 index; // 88 - FMRI + UInt32 unk8C; // 8C + }; + + // 10 + struct MorphIntensity + { + TESNPC * npc; // 00 + float morphIntensity; // 08 + + operator TESNPC*() const { return npc; } + + static inline UInt32 GetHash(TESNPC ** key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)*key, 0); + return hash; + } + + void Dump(void); + }; + + // 48+ + struct CharGenData + { + tArray * tintData; // 00 + tArray * textureSets; // 08 + BGSTextureSet * defaultTexture; // 10 + tArray * presets; // 18 + tArray * colors; // 20 + BGSColorForm * defaultColor; // 28 + tArray * headParts; // 30 + tArray * morphGroups; // 38 + tArray * faceMorphs; // 40 + + BGSCharacterTint::Template::Entry * GetTemplateByIndex(UInt16 index); + BGSCharacterTint::Template::Entry * GetTemplateBySlot(UInt32 slotType); + }; + + struct Data1 + { + tArray presets; // 00 + tArray headParts; // 18 + tArray unk30; // 30 + + struct Details + { + BSFixedString location; // 00 + struct Data + { + BSFixedString name; // 00 + BSFixedString type; // 08 + BGSTextureSet * textureSet; // 10 + UInt32 unk18; + float unk1C; + }; + tArray selection; // 08 + }; + tArray details; // 48 + tArray tintData; // 60 + tArray textures; // 78 + // ... + }; + Data1 * unk70; // 70 + + // Used by Scaleform callback to SetHairColor (17) + tArray hairColors; // 78 + + struct Data2 + { + tArray unk00; // 00 + tArray unk18; // 18 + }; + Data2 * unk90; // 90 + UInt64 unk98; // 98 + UInt64 unkA0; // A0 + UInt64 unkA8; // A8 + tArray faces; // B0 + tArray eyes; // C8 + tArray hairStyles; // E0 + tArray unkF8; // F8 + UInt64 unk110[(0x350-0x110)/8]; // 110 + + BGSCharacterTint::Template::Palette * skinTint; // 350 BGSCharacterTint::Template::Entry - Skin Tone? + + // Used by Scaleform callback to SetDetailColor (31) + tArray tints; // 358 + tArray*> details; // 370 + + UInt64 unk388[(0x510-0x388)/8]; // 388 + UInt32 presetIndex; // 510 + UInt16 unk514; // 514 + UInt8 unk516; // 516 - weight change? + UInt8 unk517; // 517 - face dirty? + UInt8 dirty; // 518 + UInt8 unk519[3]; // 519 + UInt64 unk520[(0x548-0x520)/8]; // 520 + + MEMBER_FN_PREFIX(CharacterCreation); + DEFINE_MEMBER_FN(LoadPreset, void, 0x00CACD40, UInt32 presetIndex); // Loads preset by index onto the actor +}; + +// ?? +class BSFaceGenManager +{ +public: + UInt8 unk00; // 00 + UInt8 bDisableCustomFaceGeneration; // 01 + UInt16 unk02; // 02 + UInt32 numActorsAllowedToMorph; // 04 numActorsAllowedToMorph + + UInt64 unk08[(0x31E8 - 0x08) >> 3]; // 08 + void * unk31E8; // 31E8 + UInt64 unk31F0[(0x3230 - 0x31F0) >> 3]; // 31F0 + void * unk3230; // 3230 + UInt64 unk3238[(0x3478 - 0x3238) >> 3]; // 3238 + void * unk3478; // 3478 + UInt64 unk3480[(0x34A8 - 0x3480) >> 3]; // 3238 + NiAVObject * camera; // 34A8 - NiCamera (WorldRoot Camera) + + MEMBER_FN_PREFIX(BSFaceGenManager); + DEFINE_MEMBER_FN(ApplyDynamicData, void, 0x00678E40, BSTriShape * trishape); +}; +STATIC_ASSERT(offsetof(BSFaceGenManager, unk3230) == 0x3230); +STATIC_ASSERT(offsetof(BSFaceGenManager, unk3478) == 0x3478); +STATIC_ASSERT(offsetof(BSFaceGenManager, camera) == 0x34A8); + +extern RelocPtr g_faceGenManager; +extern RelocPtr g_characterCreation; +extern RelocPtr g_characterIndex; + +extern RelocPtr g_customizationDummy1; +extern RelocPtr g_customizationDummy2; + +typedef BGSCharacterTint::Entry* (* _CreateCharacterTintEntry)(UInt32 id); +extern RelocAddr <_CreateCharacterTintEntry> CreateCharacterTintEntry; // ID is (templateIndex << 16 | type) + +typedef UInt64 (* _ClearCharacterTints)(tArray * src); +extern RelocAddr <_ClearCharacterTints> ClearCharacterTints; + +typedef UInt64 (* _CopyCharacterTints)(tArray * dst, tArray * src); +extern RelocAddr <_CopyCharacterTints> CopyCharacterTints; + +// Signature might not be correct +typedef void (* _FillTintTemplates)(tArray dst, CharacterCreation::CharGenData * src); +extern RelocAddr <_FillTintTemplates> FillTintTemplates; + +// Signature might not be correct +typedef bool (* _MergeTintTextures)(TESNPC * npc, tArray * dst, void * unk3478, void * unk3230, UInt64 unk1, UInt64 unk2); +extern RelocAddr <_MergeTintTextures> MergeTintTextures; + +// Signature might not be correct +typedef void (* _CreateMergeTintTextures)(TESNPC * npc, tArray * dst, void * unk1, UInt64 unk2); +extern RelocAddr <_CreateMergeTintTextures> CreateMergeTintTextures; + +extern RelocAddr s_BGSCharacterTint_Template_MaskVtbl; +extern RelocAddr s_BGSCharacterTint_Template_PaletteVtbl; +extern RelocAddr s_BGSCharacterTint_Template_TextureSetVtbl; + +extern RelocPtr > g_morphIntensityMap; diff --git a/f4se/f4se/GameData.cpp b/f4se/f4se/GameData.cpp new file mode 100644 index 0000000..c0e8c8a --- /dev/null +++ b/f4se/f4se/GameData.cpp @@ -0,0 +1,95 @@ +#include "f4se/GameData.h" + +// 856197F11173AF60E35EBF54A88E7BF43AFC3588+305 +RelocPtr g_dataHandler(0x058CF080); + +// 5ED90DCE1A1D1EDBCC888F3EA1234E23E307DD26+6 +RelocPtr g_isGameDataReady(0x05A58A14); + +// 637A4CF3B8D5BEB4F483234F10F54E7595CA465F+A49 +RelocPtr g_defaultObjectMap(0x058D36D0); + +// 637A4CF3B8D5BEB4F483234F10F54E7595CA465F+A3D +RelocPtr g_defaultObjectMapLock(0x058D4138); + +// C449692A90CCF8E972B00FE2979EF8ADEED6925A+3D +RelocPtr g_favoritesManager(0x05A5FC90); + +class LoadedModFinder +{ + const char * m_stringToFind; + +public: + LoadedModFinder(const char * str) : m_stringToFind(str) { } + + bool Accept(ModInfo* modInfo) + { + return _stricmp(modInfo->name, m_stringToFind) == 0; + } +}; + +const ModInfo * DataHandler::LookupModByName(const char * modName) +{ + return modList.modInfoList.Find(LoadedModFinder(modName)); +} + +UInt8 DataHandler::GetModIndex(const char* modName) +{ + return modList.modInfoList.GetIndexOf(LoadedModFinder(modName)); +} + +const ModInfo* DataHandler::LookupLoadedModByName(const char* modName) +{ + for(UInt32 i = 0; i < modList.loadedMods.count; i++) { + ModInfo * modInfo = modList.loadedMods[i]; + if(_stricmp(modInfo->name, modName) == 0) + return modInfo; + } + + return nullptr; +} + +const ModInfo* DataHandler::LookupLoadedLightModByName(const char* modName) +{ + for(UInt32 i = 0; i < modList.lightMods.count; i++) { + ModInfo * modInfo = modList.lightMods[i]; + if(_stricmp(modInfo->name, modName) == 0) + return modInfo; + } + + return nullptr; +} + +UInt8 DataHandler::GetLoadedModIndex(const char* modName) +{ + const ModInfo * modInfo = LookupLoadedModByName(modName); + if(modInfo) { + return modInfo->modIndex; + } + + return -1; +} + +UInt16 DataHandler::GetLoadedLightModIndex(const char* modName) +{ + for(UInt32 i = 0; i < modList.lightMods.count; i++) { + ModInfo * modInfo = modList.lightMods[i]; + if(_stricmp(modInfo->name, modName) == 0) + return i; + } + + return -1; +} + +BGSDefaultObject * DefaultObjectMap::GetDefaultObject(BSFixedString name) +{ + BSReadLocker locker(g_defaultObjectMapLock); + if(*g_defaultObjectMap) { + auto entry = (*g_defaultObjectMap)->Find(&name.data); + if(entry) { + return entry->defaultObject; + } + } + return nullptr; +} + diff --git a/f4se/f4se/GameData.h b/f4se/f4se/GameData.h new file mode 100644 index 0000000..41286c8 --- /dev/null +++ b/f4se/f4se/GameData.h @@ -0,0 +1,421 @@ +#pragma once + +#include "f4se_common/Utilities.h" +#include "f4se/GameTypes.h" +#include "f4se/GameForms.h" +#include "f4se/GameObjects.h" +#include "f4se/GameInput.h" + +class TESObjectWEAP; +class TESNPC; +class AlchemyItem; +class SpellItem; +class ScrollItem; +class IngredientItem; +class EffectSetting; +class TESObjectARMO; +class TESObjectARMA; +class TESObjectCONT; +class TESObjectCELL; +class TESRegionList; +class TESWorldSpace; +class BGSAddonNode; +class TESWaterForm; +class BGSPerk; +class TESLevCharacter; +class BGSDefaultObject; +class BGSConstructibleObject; + +struct ModInfo // referred to by game as TESFile +{ + ModInfo(); + ~ModInfo(); + + UInt64 unk00[10]; // 000 + BSFile* file; // 050 + UInt64 unk58; // 058 + UInt64 unk60; // 060 + void * unk68; // 068 + char name[MAX_PATH]; // 070 + char directory[MAX_PATH]; // 174 + UInt64 unk278[0xB8/8]; // 278 + struct Dependency + { + void * unk00; // 00 + const char * name; // 08 + Dependency * next; // 10 + }; + + enum RecordFlag + { + kRecordFlags_None = 0, + kRecordFlags_ESM = 1 << 0, + kRecordFlags_Active = 1 << 3, + kRecordFlags_Localized = 1 << 7, + kRecordFlags_ESL = 1 << 9 + }; + + UInt32 flags; // 330 + UInt32 recordFlags; // 334 + UInt64 unk338; // 338 + UInt64 unk340; // 340 + UInt64 unk348; // 348 + Dependency * depends; // 350 + UInt32 numRefMods; // 358 + UInt32 unk35C; // 35C + ModInfo ** refModInfo; // 360 + UInt64 unk368; // 360 + UInt8 modIndex; // 370 + UInt8 pad371; // 371 + UInt16 lightIndex; // 372 + UInt8 pad[4]; // 371 + BSString author; // 378 + BSString description; // 388 + // ... + + // Checks if a particular formID is part of the mod + bool IsFormInMod(UInt32 formID) const + { + if (!IsLight() && (formID >> 24) == modIndex) + return true; + if (IsLight() && (formID >> 24) == 0xFE && ((formID & 0x00FFF000) >> 12) == lightIndex) + return true; + return false; + } + + // Returns either a modIndex or a modIndex|lightIndex pair + UInt32 GetPartialIndex() const + { + return !IsLight() ? modIndex : (0xFE000 | lightIndex); + } + + // Converts the lower bits of a FormID to a full FormID depending on plugin type + UInt32 GetFormID(UInt32 formLower) const + { + return !IsLight() ? UInt32(modIndex) << 24 | (formLower & 0xFFFFFF) : 0xFE000000 | (UInt32(lightIndex) << 12) | (formLower & 0xFFF); + } + + bool IsActive() const { return modIndex != 0xFF; } + bool IsLight() const { return (recordFlags & kRecordFlags_ESL) == kRecordFlags_ESL; } +}; + +STATIC_ASSERT(offsetof(ModInfo, recordFlags) == 0x334); +STATIC_ASSERT(offsetof(ModInfo, author) == 0x378); + +struct ModList +{ + tList modInfoList; // 00 + tArray loadedMods; // 10 + tArray lightMods; // 28 + tArray formMapping; // 40 +}; + +// 17E8? +class DataHandler +{ +public: + UInt64 unk00; // 00 + UnkArray unk08; // 08 + UnkArray unk20; // 20 + UnkArray unk38; // 38 + UnkArray unk50; // 50 + + UnkFormArray arrNONE; // Form Type 0 + UnkFormArray arrTES4; // Form Type 1 + UnkFormArray arrGRUP; // Form Type 2 + UnkFormArray arrGMST; // Form Type 3 + tArray arrKYWD; // Form Type 4 + UnkFormArray arrLCRT; // Form Type 5 + tArray arrAACT; // Form Type 6 + tArray arrTRNS; // Form Type 7 + tArray arrCMPO; // Form Type 8 + UnkFormArray arrTXST; // Form Type 9 + UnkFormArray arrMICN; // Form Type 10 + UnkFormArray arrGLOB; // Form Type 11 + UnkFormArray arrDMGT; // Form Type 12 + UnkFormArray arrCLAS; // Form Type 13 + UnkFormArray arrFACT; // Form Type 14 + tArray arrHDPT; // Form Type 15 + UnkFormArray arrEYES; // Form Type 16 + tArray arrRACE; // Form Type 17 + UnkFormArray arrSOUN; // Form Type 18 + UnkFormArray arrASPC; // Form Type 19 + UnkFormArray arrSKIL; // Form Type 20 + tArray arrMGEF; // Form Type 21 + UnkFormArray arrSCPT; // Form Type 22 + UnkFormArray arrLTEX; // Form Type 23 + tArray arrENCH; // Form Type 24 + tArray arrSPEL; // Form Type 25 + tArray arrSCRL; // Form Type 26 + tArray arrACTI; // Form Type 27 + UnkFormArray arrTACT; // Form Type 28 + tArray arrARMO; // Form Type 29 + UnkFormArray arrBOOK; // Form Type 30 + tArray arrCONT; // Form Type 31 + UnkFormArray arrDOOR; // Form Type 32 + tArray arrINGR; // Form Type 33 + UnkFormArray arrLIGH; // Form Type 34 + tArray arrMISC; // Form Type 35 + UnkFormArray arrSTAT; // Form Type 36 + UnkFormArray arrSCOL; // Form Type 37 + UnkFormArray arrMSTT; // Form Type 38 + UnkFormArray arrGRAS; // Form Type 39 + UnkFormArray arrTREE; // Form Type 40 + UnkFormArray arrFLOR; // Form Type 41 + tArray arrFURN; // Form Type 42 + tArray arrWEAP; // Form Type 43 + UnkFormArray arrAMMO; // Form Type 44 + tArray arrNPC_; // Form Type 45 + tArray arrLVLN; // Form Type 46 + UnkFormArray arrKEYM; // Form Type 47 + tArray arrALCH; // Form Type 48 + UnkFormArray arrIDLM; // Form Type 49 + UnkFormArray arrNOTE; // Form Type 50 + UnkFormArray arrPROJ; // Form Type 51 + UnkFormArray arrHAZD; // Form Type 52 + UnkFormArray arrBNDS; // Form Type 53 + UnkFormArray arrSLGM; // Form Type 54 + tArray arrTERM; // Form Type 55 + UnkFormArray arrLVLI; // Form Type 56 + UnkFormArray arrWTHR; // Form Type 57 + UnkFormArray arrCLMT; // Form Type 58 + UnkFormArray arrSPGD; // Form Type 59 + UnkFormArray arrRFCT; // Form Type 60 + UnkFormArray arrREGN; // Form Type 61 + UnkFormArray arrNAVI; // Form Type 62 + UnkFormArray arrCELL; // Form Type 63 + UnkFormArray arrREFR; // Form Type 64 + UnkFormArray arrACHR; // Form Type 65 + UnkFormArray arrPMIS; // Form Type 66 + UnkFormArray arrPARW; // Form Type 67 + UnkFormArray arrPGRE; // Form Type 68 + UnkFormArray arrPBEA; // Form Type 69 + UnkFormArray arrPFLA; // Form Type 70 + UnkFormArray arrPCOM; // Form Type 71 + UnkFormArray arrPBAR; // Form Type 72 + UnkFormArray arrPHZD; // Form Type 73 + UnkFormArray arrWRLD; // Form Type 74 + UnkFormArray arrLAND; // Form Type 75 + UnkFormArray arrNAVM; // Form Type 76 + UnkFormArray arrTLOD; // Form Type 77 + UnkFormArray arrDIAL; // Form Type 78 + UnkFormArray arrINFO; // Form Type 79 + UnkFormArray arrQUST; // Form Type 80 + UnkFormArray arrIDLE; // Form Type 81 + UnkFormArray arrPACK; // Form Type 82 + UnkFormArray arrCSTY; // Form Type 83 + UnkFormArray arrLSCR; // Form Type 84 + UnkFormArray arrLVSP; // Form Type 85 + UnkFormArray arrANIO; // Form Type 86 + tArray arrWATR; // Form Type 87 + UnkFormArray arrEFSH; // Form Type 88 + UnkFormArray arrTOFT; // Form Type 89 + UnkFormArray arrEXPL; // Form Type 90 + UnkFormArray arrDEBR; // Form Type 91 + UnkFormArray arrIMGS; // Form Type 92 + UnkFormArray arrIMAD; // Form Type 93 + tArray arrFLST; // Form Type 94 + tArray arrPERK; // Form Type 95 + UnkFormArray arrBPTD; // Form Type 96 + UnkFormArray arrADDN; // Form Type 97 + UnkFormArray arrAVIF; // Form Type 98 + UnkFormArray arrCAMS; // Form Type 99 + UnkFormArray arrCPTH; // Form Type 100 + UnkFormArray arrVTYP; // Form Type 101 + UnkFormArray arrMATT; // Form Type 102 + UnkFormArray arrIPCT; // Form Type 103 + UnkFormArray arrIPDS; // Form Type 104 + tArray arrARMA; // Form Type 105 + tArray arrECZN; // Form Type 106 + tArray arrLCTN; // Form Type 107 + UnkFormArray arrMESG; // Form Type 108 + UnkFormArray arrRGDL; // Form Type 109 + UnkFormArray arrDOBJ; // Form Type 110 + tArray arrDFOB; // Form Type 111 + UnkFormArray arrLGTM; // Form Type 112 + UnkFormArray arrMUSC; // Form Type 113 + UnkFormArray arrFSTP; // Form Type 114 + UnkFormArray arrFSTS; // Form Type 115 + UnkFormArray arrSMBN; // Form Type 116 + UnkFormArray arrSMQN; // Form Type 117 + UnkFormArray arrSMEN; // Form Type 118 + UnkFormArray arrDLBR; // Form Type 119 + UnkFormArray arrMUST; // Form Type 120 + UnkFormArray arrDLVW; // Form Type 121 + UnkFormArray arrWOOP; // Form Type 122 + UnkFormArray arrSHOU; // Form Type 123 + UnkFormArray arrEQUP; // Form Type 124 + UnkFormArray arrRELA; // Form Type 125 + UnkFormArray arrSCEN; // Form Type 126 + UnkFormArray arrASTP; // Form Type 127 + UnkFormArray arrOTFT; // Form Type 128 + UnkFormArray arrARTO; // Form Type 129 + UnkFormArray arrMATO; // Form Type 130 + UnkFormArray arrMOVT; // Form Type 131 + UnkFormArray arrSNDR; // Form Type 132 + UnkFormArray arrDUAL; // Form Type 133 + UnkFormArray arrSNCT; // Form Type 134 + UnkFormArray arrSOPM; // Form Type 135 + UnkFormArray arrCOLL; // Form Type 136 + tArray arrCLFM; // Form Type 137 + UnkFormArray arrREVB; // Form Type 138 + UnkFormArray arrPKIN; // Form Type 139 + UnkFormArray arrRFGP; // Form Type 140 + UnkFormArray arrAMDL; // Form Type 141 + UnkFormArray arrLAYR; // Form Type 142 + tArray arrCOBJ; // Form Type 143 + tArray arrOMOD; // Form Type 144 + tArray arrMSWP; // Form Type 145 + UnkFormArray arrZOOM; // Form Type 146 + UnkFormArray arrINNR; // Form Type 147 + UnkFormArray arrKSSM; // Form Type 148 + UnkFormArray arrAECH; // Form Type 149 + UnkFormArray arrSCCO; // Form Type 150 + UnkFormArray arrAORU; // Form Type 151 + UnkFormArray arrSCSN; // Form Type 152 + UnkFormArray arrSTAG; // Form Type 153 + UnkFormArray arrNOCM; // Form Type 154 + UnkFormArray arrLENS; // Form Type 155 + UnkFormArray arrLSPR; // Form Type 156 + UnkFormArray arrGDRY; // Form Type 157 + UnkFormArray arrOVIS; // Form Type 158 + + TESRegionList * regionList; // F50 + NiTArray cellList; // F58 + NiTArray addonNodes; // F70 + + UInt64 unkF88; // F88 + UInt64 unkF90; // F90 + UInt64 unkF98; // F98 + UInt32 unkFA0; // FA0 - FormID? + UInt32 unkFA4; // FA4 + UInt64 unkFA8; // FA8 + + ModList modList; // FB0 + + UInt64 unk17C0[(0x17E8-0x17C0)/8]; // 17C0 + + const ModInfo* LookupModByName(const char* modName); + UInt8 GetModIndex(const char* modName); + + const ModInfo* LookupLoadedModByName(const char* modName); + UInt8 GetLoadedModIndex(const char* modName); + + const ModInfo* LookupLoadedLightModByName(const char* modName); + UInt16 GetLoadedLightModIndex(const char* modName); +}; + +extern RelocPtr g_dataHandler; +extern RelocPtr g_isGameDataReady; + +// 30 +class LocationData +{ +public: + LocationData(Actor * actor) + { + CALL_MEMBER_FN(this, ctor)(actor); + } + ~LocationData() { }; + + float unk00; // 00 + float unk04; // 04 + float unk08; // 08 + float unk0C; // 0C + float unk10; // 10 + float unk14; // 14 + void * unk18; // 18 + TESObjectCELL * cell; // 20 + TESWorldSpace * worldspace; // 28 + + MEMBER_FN_PREFIX(LocationData); + DEFINE_MEMBER_FN(ctor, LocationData*, 0x001F8830, Actor * refr); +}; + +struct DefaultObjectEntry +{ + BSFixedString editorId; + BGSDefaultObject * defaultObject; + + operator StringCache::Entry*() const { return editorId.data; } + + static inline UInt32 GetHash(StringCache::Entry** key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)*key, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tname: %s", editorId.data->Get()); + _MESSAGE("\t\tinstance: %08X", defaultObject->formID); + } +}; + +class DefaultObjectMap : public tHashSet +{ +public: + BGSDefaultObject * GetDefaultObject(BSFixedString name); +}; + +extern RelocPtr g_defaultObjectMap; +extern RelocPtr g_defaultObjectMapLock; + +class FavoritesManager : public BSIntrusiveRefCounted +{ +public: + virtual ~FavoritesManager(); + + BSInputEventUser inputEventUser; // 10 + BSTEventSink inventoryEventSink; // 20 + BSTEventSink favoriteChangedEventSink; // 28 + + UInt64 unk30; // 30 + UInt64 unk38; // 38 + tArray> favoritesSinks; // 40 + UInt64 unk58; // 58 + UInt64 unk60; // 60 + UInt64 unk68; // 68 + UInt64 unk70; // 70 + UInt64 unk78; // 78 + UInt64 unk80; // 80 + UInt64 unk88; // 88 + + enum Favorites + { + kNumFavorites = 12 + }; + + TESForm * favorites[kNumFavorites]; // 90 + void * queuedFile[kNumFavorites]; // F0 + tHashSet unk150; // 150 + + struct TaggedEntry + { + TESForm * form; // 00 + UInt32 unk08; // 08 + UInt32 unk0C; // 0C + + operator TESForm *() const { return form; } + + static inline UInt32 GetHash(TESForm ** key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)*key, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tForm: %08X", form ? form->formID : 0); + _MESSAGE("\t\tunk08: %08X", unk08); + } + }; + + tHashSet taggedForms; // 180 + // ... +}; +STATIC_ASSERT(offsetof(FavoritesManager, taggedForms) == 0x180); + +extern RelocPtr g_favoritesManager; diff --git a/f4se/f4se/GameEvents.cpp b/f4se/f4se/GameEvents.cpp new file mode 100644 index 0000000..f519b74 --- /dev/null +++ b/f4se/f4se/GameEvents.cpp @@ -0,0 +1,7 @@ +#include "f4se/GameEvents.h" + +// 7D79D7BCBD040E10630ADDBE12B957FD115304A2+D +RelocPtr *> g_colorUpdateDispatcher(0x06577DF0); + +// 61A1F6B0E46CB298CD32C55D9A653D702CCD1B67+63 +RelocPtr g_globalEvents(0x058CEC18); \ No newline at end of file diff --git a/f4se/f4se/GameEvents.h b/f4se/f4se/GameEvents.h new file mode 100644 index 0000000..d2f4fe0 --- /dev/null +++ b/f4se/f4se/GameEvents.h @@ -0,0 +1,241 @@ +#pragma once + +#include "f4se/GameTypes.h" + +class Actor; +class TESObjectREFR; + +enum EventResult +{ + kEvent_Continue = 0, + kEvent_Abort +}; + +// 08 +template +class BSTEventSink +{ +public: + virtual ~BSTEventSink() { }; + virtual EventResult ReceiveEvent(T * evn, void * dispatcher) { return kEvent_Continue; }; // pure +// void ** _vtbl; // 00 +}; + +struct BGSInventoryListEvent +{ + struct Event + { + + }; +}; + +struct MenuOpenCloseEvent +{ + BSFixedString menuName; + bool isOpen; +}; + +struct MenuModeChangeEvent +{ + +}; + +struct UserEventEnabledEvent +{ + +}; + +struct RequestHUDModesEvent +{ + +}; + +struct TESHitEvent +{ + +}; + +struct PerkEntryUpdatedEvent +{ + struct PerkValueEvents + { + + }; +}; + +struct ApplyColorUpdateEvent +{ + +}; + +struct BSMovementDataChangedEvent +{ + +}; + +struct BSTransformDeltaEvent +{ + +}; + +struct BSSubGraphActivationUpdate +{ + +}; + +struct bhkCharacterMoveFinishEvent +{ + +}; + +struct bhkNonSupportContactEvent +{ + +}; + +struct bhkCharacterStateChangeEvent +{ + +}; + +struct ChargenCharacterUpdateEvent +{ + +}; + +struct QuickContainerStateEvent +{ + +}; + +struct TESCombatEvent +{ + TESObjectREFR * source; // 00 + TESObjectREFR * target; // 04 + UInt32 state; // 08 +}; + +struct TESDeathEvent +{ + TESObjectREFR * source; // 00 +}; + +struct TESObjectLoadedEvent +{ + UInt32 formId; + UInt8 loaded; // 01 - loaded, 00 - unloaded +}; + +struct TESLoadGameEvent +{ + +}; + +struct TESFurnitureEvent +{ + Actor * actor; + TESObjectREFR * furniture; + bool isGettingUp; +}; + +struct TESInitScriptEvent +{ + TESObjectREFR * reference; +}; + +// 08 +template +class BSTEventDispatcher +{ +public: + typedef BSTEventSink SinkT; + + bool AddEventSink(SinkT * sink) + { + SimpleLocker locker(&lock); + + // Check for duplicate first + for (int i = 0; i < eventSinks.count; i++) + { + if(eventSinks[i] == sink) + return false; + } + + eventSinks.Insert(0, sink); + return true; + } + + void RemoveEventSink(SinkT * sink) + { + SimpleLocker locker(&lock); + + for (int i = 0; i < eventSinks.count; i++) + { + if(eventSinks[i] == sink) { + eventSinks.Remove(i); + break; + } + } + } + + SimpleLock lock; // 000 + tArray eventSinks; // 008 + tArray addBuffer; // 020 + tArray removeBuffer; // 038 + bool stateFlag; // 050 + char pad[3]; +}; + +class BSTGlobalEvent +{ +public: + virtual ~BSTGlobalEvent(); + + template + class EventSource + { + public: + virtual ~EventSource(); + + // void ** _vtbl; // 00 + UInt64 unk08; // 08 + BSTEventDispatcher eventDispatcher; // 10 + }; + + // void ** _vtbl; // 00 + UInt64 unk08; // 08 + UInt64 unk10; // 10 + tArray*> eventSources; // 18 +}; + +extern RelocPtr g_globalEvents; +extern RelocPtr *> g_colorUpdateDispatcher; + +template +BSTEventDispatcher * GetEventDispatcher() { }; + +#define DECLARE_EVENT_DISPATCHER(Event, address) \ +template<> inline BSTEventDispatcher * GetEventDispatcher() \ +{ \ + typedef BSTEventDispatcher * (*_GetEventDispatcher)(); \ + RelocAddr<_GetEventDispatcher> GetDispatcher(address); \ + return GetDispatcher(); \ +} + +// A548D71D41C7C2E9D21B25E06730FB911FC31F47+B4 (struct+A0) +DECLARE_EVENT_DISPATCHER(TESCombatEvent, 0x004420F0) + +// A548D71D41C7C2E9D21B25E06730FB911FC31F47+118 (struct+C8) +DECLARE_EVENT_DISPATCHER(TESDeathEvent, 0x00442550) + +// A548D71D41C7C2E9D21B25E06730FB911FC31F47+1A4 (struct+108) +DECLARE_EVENT_DISPATCHER(TESFurnitureEvent, 0x00442C30) + +// A548D71D41C7C2E9D21B25E06730FB911FC31F47+1F4 (struct+128) +DECLARE_EVENT_DISPATCHER(TESLoadGameEvent, 0x00442EB0) + +// A548D71D41C7C2E9D21B25E06730FB911FC31F47+238 (struct+140) +DECLARE_EVENT_DISPATCHER(TESObjectLoadedEvent, 0x004431D0) + +// 22274238010A92A75A0E77127FF6D54FDBC6F943+821 (inside call) +DECLARE_EVENT_DISPATCHER(TESInitScriptEvent, 0x00442D70) diff --git a/f4se/f4se/GameExtraData.cpp b/f4se/f4se/GameExtraData.cpp new file mode 100644 index 0000000..27bfe6f --- /dev/null +++ b/f4se/f4se/GameExtraData.cpp @@ -0,0 +1,160 @@ +#include "f4se/GameExtraData.h" +#include "f4se/GameObjects.h" + +// ??_7ExtraPowerLinks@@6B@ +RelocAddr s_ExtraPowerLinksVtbl(0x02C52388); + +// ??_7ExtraInstanceData@@6B@ +RelocAddr s_ExtraInstanceDataVtbl(0x02C53C10); + +// ??_7ExtraHealth@@6B@ +RelocAddr s_ExtraHealthVtbl(0x02C521E8); + +// ??_7ExtraMaterialSwap@@6B@ +RelocAddr s_ExtraMaterialSwapVtbl(0x02C528E8); + +bool ExtraDataList::PresenceBitfield::HasType(UInt32 type) const +{ + UInt32 index = (type >> 3); + UInt8 bitMask = 1 << (type % 8); + return (bits[index] & bitMask) != 0; +} + +bool ExtraDataList::HasType(UInt32 type) +{ + BSReadLocker locker(&m_lock); + return (m_presence) ? m_presence->HasType(type) : false; +} + +void ExtraDataList::MarkType(UInt32 type, bool bCleared) +{ + if (!m_presence) { + m_presence = (PresenceBitfield*)Heap_Allocate(sizeof(PresenceBitfield)); + memset(m_presence, 0, sizeof(PresenceBitfield)); + } + + UInt32 index = (type >> 3); + UInt8 bitMask = 1 << (type % 8); + UInt8& flag = m_presence->bits[index]; + if (bCleared) { + flag &= ~bitMask; + } else { + flag |= bitMask; + } +} + +bool ExtraDataList::Remove(UInt8 type, BSExtraData* toRemove) +{ + if (!toRemove) return false; + + BSReadAndWriteLocker locker(&m_lock); + if (HasType(type)) { + bool bRemoved = false; + if (m_data == toRemove) { + m_data = m_data->next; + bRemoved = true; + } + + for (BSExtraData* traverse = m_data; traverse; traverse = traverse->next) { + if (traverse->next == toRemove) { + traverse->next = toRemove->next; + bRemoved = true; + break; + } + } + if (bRemoved) { + MarkType(type, true); + } + return true; + } + + return false; +} + +bool ExtraDataList::Add(UInt8 type, BSExtraData* toAdd) +{ + if (!toAdd || HasType(type)) return false; + + BSReadAndWriteLocker locker(&m_lock); + BSExtraData* next = m_data; + m_data = toAdd; + toAdd->next = next; + MarkType(type, false); + return true; +} + +BSExtraData* ExtraDataList::GetByType(UInt32 type) +{ + if (!HasType(type)) + return NULL; + + BSReadLocker locker(&m_lock); + for(BSExtraData * traverse = m_data; traverse; traverse = traverse->next) { + if(traverse->type == type) + return traverse; + } + return NULL; +} + +#ifdef _DEBUG +#include "f4se_common\Utilities.h" + +void ExtraDataList::Dump() +{ + for(BSExtraData * traverse = m_data; traverse; traverse = traverse->next) + { + gLog.Indent(); + _MESSAGE("%016I64X %s", traverse, GetObjectClassName(traverse)); + gLog.Outdent(); + } +} +#endif + +BSExtraData* BSExtraData::Create(UInt32 size, UInt64 vtbl) +{ + void* memory = Heap_Allocate(size); + memset(memory, 0, size); + ((UInt64*)memory)[0] = vtbl; + BSExtraData* xData = (BSExtraData*)memory; + xData->unk10 = 0; + xData->unk13 = 0; + xData->unk14 = 0; + xData->next = NULL; + return xData; +} + +ExtraPowerLinks* ExtraPowerLinks::Create() +{ + ExtraPowerLinks* pPowerLinks = (ExtraPowerLinks*)BSExtraData::Create(sizeof(ExtraPowerLinks), s_ExtraPowerLinksVtbl.GetUIntPtr()); + pPowerLinks->type = kExtraData_PowerLinks; + pPowerLinks->connections.entries = NULL; + pPowerLinks->connections.count = 0; + pPowerLinks->connections.capacity = 0; + return pPowerLinks; +} + +ExtraInstanceData* ExtraInstanceData::Create(TESForm * baseForm, TBO_InstanceData * instanceData) +{ + ExtraInstanceData* pInstanceData = (ExtraInstanceData*)BSExtraData::Create(sizeof(ExtraInstanceData), s_ExtraInstanceDataVtbl.GetUIntPtr()); + pInstanceData->type = kExtraData_InstanceData; + pInstanceData->baseForm = baseForm; + pInstanceData->instanceData = instanceData; + InterlockedIncrement(&instanceData->m_refCount); + return pInstanceData; +} + +ExtraHealth* ExtraHealth::Create(float value) +{ + ExtraHealth* pHeath = (ExtraHealth*)BSExtraData::Create(sizeof(ExtraHealth), s_ExtraHealthVtbl.GetUIntPtr()); + pHeath->type = kExtraData_Health; + pHeath->health = value; + return pHeath; +} + +ExtraMaterialSwap* ExtraMaterialSwap::Create(BGSMaterialSwap * matSwap) +{ + ExtraMaterialSwap* pMatSwap = (ExtraMaterialSwap*)BSExtraData::Create(sizeof(ExtraMaterialSwap), s_ExtraMaterialSwapVtbl.GetUIntPtr()); + pMatSwap->type = kExtraData_MaterialSwap; + pMatSwap->materialSwap = matSwap; + return pMatSwap; +} \ No newline at end of file diff --git a/f4se/f4se/GameExtraData.h b/f4se/f4se/GameExtraData.h new file mode 100644 index 0000000..3cfd57d --- /dev/null +++ b/f4se/f4se/GameExtraData.h @@ -0,0 +1,416 @@ +#pragma once + +#include "f4se/GameTypes.h" + +class BGSMaterialSwap; +class BGSMessage; +class TESQuest; +class TESWaterForm; +class TBO_InstanceData; + +enum ExtraDataType +{ + kExtraData_Havok = 0x1, + kExtraData_Cell3D = 0x2, + kExtraData_CellWaterType = 0x3, + kExtraData_RegionList = 0x4, + kExtraData_SeenData = 0x5, + kExtraData_EditorID = 0x6, + kExtraData_CellMusicType = 0x7, + kExtraData_CellSkyRegion = 0x8, + kExtraData_ProcessMiddleLow = 0x9, + kExtraData_DetachTime = 0xA, + kExtraData_PersistentCell = 0xB, // Confirmed + kExtraData_Keywords = 0xC, // Confirmed + kExtraData_Action = 0xD, // Confirmed + kExtraData_StartingPosition = 0xE, + // ?? = 0xF, // D.N.E + kExtraData_AnimGraphManager = 0x10, // Confirmed + kExtraData_Biped = 0x11, // Confirmed + kExtraData_UsedMarkers = 0x12, + kExtraData_DistantData = 0x13, // Confirmed + kExtraData_RagDollData = 0x14, + kExtraData_PreVisRefs = 0x15, // Confirmed + kExtraData_InitActions = 0x16, // Confirmed + kExtraData_EssentialProtected = 0x17, // Confirmed + kExtraData_PackageStartLocation = 0x18, // Confirmed + kExtraData_Package = 0x19, // Confirmed + kExtraData_TresPassPackage = 0x1A, + kExtraData_RunOncePacks = 0x1B, + kExtraData_ReferenceHandle = 0x1C, // Confirmed + kExtraData_Follower = 0x1D, // Confirmed + kExtraData_LevCreaModifier = 0x1E, // Confirmed + kExtraData_Ghost = 0x1F, + kExtraData_OriginalReference = 0x20, // Confirmed + kExtraData_Ownership = 0x21, + kExtraData_Global = 0x22, + kExtraData_Rank = 0x23, + kExtraData_Count = 0x24, + kExtraData_Health = 0x25, + kExtraData_RangedDistOverride = 0x26, // Confirmed + kExtraData_TimeLeft = 0x27, + kExtraData_Charge = 0x28, + kExtraData_Light = 0x29, + kExtraData_Lock = 0x2A, // Confirmed + kExtraData_Teleport = 0x2B, // Confirmed + kExtraData_MapMarker = 0x2C, // Confirmed + kExtraData_LeveledCreature = 0x2D, // Confirmed + kExtraData_LeveledItem = 0x2E, + kExtraData_Scale = 0x2F, + kExtraData_Seed = 0x30, // Confirmed + kExtraData_MagicCaster = 0x31, // Confirmed + // ?? = 0x32, // D.N.E + kExtraData_MasterFileCell = 0x33, // Confirmed + kExtraData_PlayerCrimeList = 0x34, + kExtraData_ObjectInstance = 0x35, // Confirmed + kExtraData_EnableStateParent = 0x36, + kExtraData_EnableStateChildren = 0x37, + kExtraData_ItemDropper = 0x38, // Confirmed + kExtraData_DroppedItemList = 0x39, // Confirmed + kExtraData_RandomTeleportMarker = 0x3A, // Confirmed + //?? = 0x3B, // D.N.E + kExtraData_SavedHavokData = 0x3C, // Confirmed + kExtraData_CannotWear = 0x3D, // Confirmed + kExtraData_Poison = 0x3E, + //?? = 0x3F, // D.N.E + kExtraData_LastFinishedSequence = 0x40, // Confirmed + kExtraData_SavedAnimation = 0x41, // Confirmed + kExtraData_NorthRotation = 0x42, + kExtraData_SpawnContainer = 0x43, // Confirmed + kExtraData_FriendHits = 0x44, // Confirmed + kExtraData_HeadingTarget = 0x45, + //?? = 0x46, // D.N.E + kExtraData_RefractionProperty = 0x47, + kExtraData_StartingWorldOrCell = 0x48, + kExtraData_Hotkey = 0x49, + kExtraData_EditorRef3DData = 0x4A, + kExtraData_EditiorRefMoveData = 0x4B, // Confirmed + kExtraData_InfoGeneralTopic = 0x4C, + kExtraData_HasNoRumors = 0x4D, + kExtraData_Sound = 0x4E, // Confirmed + kExtraData_TerminalState = 0x4F, + kExtraData_LinkedRef = 0x50, + kExtraData_LinkedRefChildren = 0x51, + kExtraData_ActivateRef = 0x52, // Confirmed + kExtraData_ActivateRefChildren =0x53, // Confirmed + kExtraData_CanTalkToPlayer = 0x54, // Confirmed + kExtraData_ObjectHealth = 0x55, + kExtraData_CellImageSpace = 0x56, + kExtraData_NavMeshPortal = 0x57, + kExtraData_ModelSwap = 0x58, // Confirmed + kExtraData_Radius = 0x59, + //?? = 0x5A, // D.N.E + kExtraData_FactionChanges = 0x5B, // Confirmed + kExtraData_DismemberedLimbs = 0x5C, // Confirmed + kExtraData_ActorCause = 0x5D, // Confirmed + kExtraData_MultiBound = 0x5E, + kExtraData_MultiBoundData = 0x5F, + kExtraData_MultiBoundRef = 0x60, + kExtraData_ReflectedRefs = 0x61, + kExtraData_ReflectorRefs = 0x62, + kExtraData_EmittanceSource = 0x63, + kExtraData_RadioData = 0x64, // Confirmed + kExtraData_CombatStyle = 0x65, + //?? = 0x66, // D.N.E + kExtraData_Primitive = 0x67, + kExtraData_OpenCloseActivateRef = 0x68, + kExtraData_AnimNoteReceiver = 0x69, + kExtraData_Ammo = 0x6A, + kExtraData_PatrolRefData = 0x6B, + kExtraData_PackageData = 0x6C, + kExtraData_OcclusionShape = 0x6D, // Confirmed + kExtraData_CollisionData = 0x6E, // Confirmed + kExtraData_SayTopicInfoOnceADay = 0x6F, + kExtraData_EncounterZone = 0x70, + kExtraData_SayTopicInfo = 0x71, + kExtraData_OcclusionPlaneRefData = 0x72, + kExtraData_PortalRefData = 0x73, + kExtraData_Portal = 0x74, + kExtraData_Room = 0x75, + kExtraData_HealthPerc = 0x76, + kExtraData_RoomRefData = 0x77, + kExtraData_GuardedRefData = 0x78, + kExtraData_CreatureAwakeSound = 0x79, + //?? = 0x7A, // D.N.E + kExtraData_Horse = 0x7B, + kExtraData_IgnoredBySandbox = 0x7C, + kExtraData_CellAcousticSpace = 0x7D, + kExtraData_ReservedMarkers = 0x7E, // Confirmed + kExtraData_TransitionCellCount = 0x7F, // Confirmed + kExtraData_WaterLightRefs = 0x80, + kExtraData_LitWaterRefs = 0x81, + kExtraData_RadioRepeater = 0x82, // Confirmed + kExtraData_ActivateLoopSound = 0x83, + kExtraData_PatrolRefInUseData = 0x84, + kExtraData_AshPileRef = 0x85, + kExtraData_CreatureMovementSound = 0x86, // Confirmed + kExtraData_FollowerSwimBreadcrumbs = 0x87, + kExtraData_AliasInstanceArray = 0x88, + kExtraData_Location = 0x89, // Confirmed + kExtraData_MasterLocation = 0x8A, // Confirmed + kExtraData_LocationRefType = 0x8B, + kExtraData_PromotedRef = 0x8C, + kExtraData_AnimationSequencer = 0x8D, // Confirmed + kExtraData_OutfitItem = 0x8E, // Confirmed + //?? = 0x8F, // D.N.E + kExtraData_LeveledItemBase = 0x90, // Confirmed + kExtraData_LightData = 0x91, + kExtraData_SceneData = 0x92, + kExtraData_BadPosition = 0x93, // Confirmed + kExtraData_HeadTrackingWeight = 0x94, + kExtraData_FromAlias = 0x95, + kExtraData_ShouldWear = 0x96, // Confirmed + kExtraData_FavorCost = 0x97, + kExtraData_AttachedArrows3D = 0x98, + kExtraData_TextDisplayData = 0x99, + kExtraData_AlphaCutoff = 0x9A, + kExtraData_Enchantment = 0x9B, + kExtraData_Soul = 0x9C, + kExtraData_ForcedTarget = 0x9D, // Confirmed + kExtraData_SoundOutputOverride = 0x9E, // Confirmed + kExtraData_UniqueID = 0x9F, // Confirmed + kExtraData_Flags = 0xA0, // Confirmed + kExtraData_RefrPath = 0xA1, + kExtraData_DecalGroup = 0xA2, + kExtraData_LockList = 0xA3, + kExtraData_ForcedLandingMarker = 0xA4, + kExtraData_LargeRefOwnerCells = 0xA5, + kExtraData_CellWaterEnvMap = 0xA6, // Confirmed + kExtraData_CellGrassData = 0xA7, + kExtraData_TeleportName = 0xA8, + kExtraData_Interaction = 0xA9, // Confirmed + kExtraData_WaterData = 0xAA, + kExtraData_WaterCurrentZoneData = 0xAB, // Confirmed + kExtraData_AttachRef = 0xAC, // Confirmed + kExtraData_AttachRefChildren = 0xAD, // Confirmed + kExtraData_GroupConstraint = 0xAE, // Confirmed + kExtraData_ScriptedAnimDependence = 0xAF, // Confirmed + kExtraData_CachedScale = 0xB0, // Confirmed + kExtraData_RaceData = 0xB1, + kExtraData_GIDBuffer = 0xB2, // Confirmed + kExtraData_MissingRefIDs = 0xB3, // Confirmed + kExtraData_BendableSplineParams = 0xB4, // Confirmed + //?? = 0xB5, // D.N.E + //?? = 0xB6, // D.N.E + //?? = 0xB7, // D.N.E + //?? = 0xB8, // D.N.E + kExtraData_MaterialSwap = 0xB9, // Confirmed + kExtraData_InstanceData = 0xBA, // Confirmed + kExtraData_PowerArmor = 0xBB, // Confirmed + //?? = 0xBC, // D.N.E + kExtraData_InputEnableLayer = 0xBD, // Confirmed + kExtraData_ProjectedDecalData = 0xBE, // Confirmed + kExtraData_WorkshopExtraData = 0xBF, + kExtraData_RadioReceiver = 0xC0, // Confirmed + kExtraData_CulledBone = 0xC1, // Confirmed + kExtraData_ActorValueStorage = 0xC2, // Confirmed + kExtraData_DirectAtTarget = 0xC3, // Confirmed + kExtraData_ActivateText = 0xC4, // Confirmed + kExtraData_CombinedRefs = 0xC5, // Confirmed + kExtraData_ObjectBreakable = 0xC6, // Confirmed + kExtraData_SavedDynamicIdles = 0xC7, // Confirmed + kExtraData_IgnoredAttractKeywords = 0xC8, // Confirmed + kExtraData_ModRank = 0xC9, // Confirmed + kExtraData_InteriorLODWorldspace = 0xCA, // Confirmed + kExtraData_BoneScaleMap = 0xCB, // Confirmed + kExtraData_FXPickNodes = 0xCC, // Confirmed + kExtraData_PowerArmorPreload = 0xCD, // Confirmed + kExtraData_AnimGraphPreload = 0xCE, // Confirmed + kExtraData_AnimSounds = 0xCF, // Confirmed + kExtraData_PowerLinks = 0xD0, // Confirmed + kExtraData_ObjectSavedUnrecoverableSubgraph = 0xD1, // Confirmed + kExtraData_RefWeaponSounds = 0xD2, // Confirmed + kExtraData_InvestedGold = 0xD3, // Confirmed + kExtraData_FurnitureEntryData = 0xD4, // Confirmed + kExtraData_VoiceType = 0xD5, // Confirmed +}; + +// 18 +class BSExtraData +{ +public: + virtual ~BSExtraData(); + + virtual void Unk_01(); + virtual void Unk_02(); + + BSExtraData * next; // 08 + UInt16 unk10; // 10 + UInt8 type; // 12 + UInt8 unk13; // 13 + UInt32 unk14; // 14 + + static BSExtraData* Create(UInt32 size, UInt64 vtbl); +}; + +// 20? +class ExtraDataList : public BSIntrusiveRefCounted +{ +public: + struct PresenceBitfield + { + bool HasType(UInt32 type) const; + + UInt8 bits[0x1B]; + }; + + bool HasType(UInt32 type); + void MarkType(UInt32 type, bool bCleared); + bool Remove(UInt8 type, BSExtraData* toRemove); + bool Add(UInt8 type, BSExtraData* toAdd); + BSExtraData* GetByType(UInt32 type); + +#ifdef _DEBUG + void Dump(); +#endif + + BSExtraData * m_data; // 08 + void * unk10; // 10 + PresenceBitfield * m_presence; // 18 + BSReadWriteLock m_lock; // 20 +}; +STATIC_ASSERT(sizeof(ExtraDataList) == 0x28); + +// 28 +class BGSObjectInstanceExtra : public BSExtraData +{ +public: + struct Data + { + struct Form + { + UInt32 formId; // 00 + UInt32 unk04; // 04 + }; + + Form * forms; // 00 + UInt32 blockSize; // 04 - blockSize/sizeof(Form) yields length + }; + + Data * data; // 18 + SInt16 unk20; // 20 + UInt16 unk22; // 22 + UInt32 unk24; // 24 +}; + +// 30 +class ExtraPowerLinks : public BSExtraData +{ +public: + tArray connections; // 18 - The Reference formids that this is power-linked to (item A wired to item B, the wire receives formids to A and B, and A and B receive formids to the wire) + + static ExtraPowerLinks* Create(); +}; + +// 30 +class ExtraBendableSplineParams : public BSExtraData +{ +public: + float unk18; // 18 + float thickness; // 1C + float xOffset; // 20 + float yOffset; // 24 + float zOffset; // 28 + float unk2C; // 2C +}; + +// 58 +class ExtraWorkshopData +{ +public: + UInt64 unk18[(0x58 - 0x18) >> 3]; +}; + +// 20 +class ExtraMaterialSwap : public BSExtraData +{ +public: + BGSMaterialSwap * materialSwap; + + static ExtraMaterialSwap* Create(BGSMaterialSwap * matSwap); +}; + + +// 48 +class ExtraTextDisplayData : public BSExtraData { +public: + ExtraTextDisplayData(); + virtual ~ExtraTextDisplayData(); + + // 10 + struct TextReplaceEntries + { + BSFixedString token; + TESForm* value; + }; + + BSFixedString name; // 18 + BGSMessage* message; // 20 + TESQuest* quest; // 28 + UInt32 type; // 30 - 1 for refs with ref alias set name, -2 for names set by user + UInt32 unk34; // 34 - some terminals 1, actors 0. terminal sometimes also 0.. maybe HasTextReplaceData? + tArray * textReplaceData; // 38 + UInt16 nameLength; // 40 + UInt16 unk42; // 42 + UInt32 unk44; // 44 + + MEMBER_FN_PREFIX(ExtraTextDisplayData); + DEFINE_MEMBER_FN(GetReferenceName, BSFixedString *, 0x000C0A00, TESForm * baseForm); +}; + +// 20 +class ExtraUniqueID : public BSExtraData +{ +public: + UInt16 uniqueId; // 18 + UInt16 unk1A; // 1A + UInt16 formOwner; // 1C +}; + +// 20 +class ExtraCellWaterType : public BSExtraData +{ +public: + TESWaterForm * waterForm; // 18 +}; + +// 28 +class ExtraInstanceData : public BSExtraData +{ +public: + TESForm * baseForm; // 18 + TBO_InstanceData * instanceData; // 20 + + static ExtraInstanceData * Create(TESForm * baseForm, TBO_InstanceData * instanceData); +}; + +// 30 +class ExtraPromotedRef : public BSExtraData +{ +public: + tArray promoters; // 18 - a list of forms that are keeping this reference persistent +}; +STATIC_ASSERT(sizeof(ExtraPromotedRef) == 0x30); + +// 20 +class ExtraFlags : public BSExtraData +{ +public: + enum { + kFlag_PlayerHasTaken = 1 << 5, + }; + UInt32 flags; // 18 + UInt32 pad1C; // 1C +}; +STATIC_ASSERT(sizeof(ExtraFlags) == 0x20); + +// 20 +class ExtraHealth : public BSExtraData +{ +public: + float health; // 18 - value is a percentage from 0-1. + UInt32 pad1C; // 1C + + static ExtraHealth* Create(float value); +}; +STATIC_ASSERT(sizeof(ExtraHealth) == 0x20); diff --git a/f4se/f4se/GameFormComponents.cpp b/f4se/f4se/GameFormComponents.cpp new file mode 100644 index 0000000..51a8dc3 --- /dev/null +++ b/f4se/f4se/GameFormComponents.cpp @@ -0,0 +1,30 @@ +#include "f4se/GameFormComponents.h" +#include "f4se/GameForms.h" + +RelocAddr <_EvaluationConditions> EvaluationConditions(0x0072AF80); + +#ifdef _DEBUG +#include "f4se/GameExtraData.h" + +void BGSInventoryItem::Dump() +{ + _MESSAGE("%016I64X %s", form->formID, GetObjectClassName(form)); + gLog.Indent(); + stack->Dump(); + gLog.Outdent(); +} + +void BGSInventoryItem::Stack::Dump() +{ + _MESSAGE("Count: %d", count); + if(extraData) + extraData->Dump(); + + if(next) { + gLog.Indent(); + next->Dump(); + gLog.Outdent(); + } + +} +#endif diff --git a/f4se/f4se/GameFormComponents.h b/f4se/f4se/GameFormComponents.h new file mode 100644 index 0000000..4f6a6bc --- /dev/null +++ b/f4se/f4se/GameFormComponents.h @@ -0,0 +1,1543 @@ +#pragma once + +#include "f4se/GameTypes.h" +#include "f4se/NiObjects.h" +#include "f4se/GameEvents.h" + +class BGSEquipSlot; +class EnchantmentItem; +class BGSKeyword; +class BGSSoundDescriptorForm; +class BGSVoiceType; +class TESForm; +class TESObjectARMO; +class TESRace; +class BGSInstanceNamingRules; +class BGSImpactDataSet; +class BGSMaterialType; +class TESLevItem; +class TESAmmo; +class BGSAimModel; +class BGSZoomData; +class BGSBodyPartData; +class BGSTextureSet; +class BGSMaterialSwap; +class BGSTerminal; +class BGSObjectInstanceExtra; +class TESObjectARMA; +class IAnimationGraphManagerHolder; +class ExtraDataList; +class ActorValueInfo; +class Condition; +class TESObjectREFR; +class BGSDamageType; +class BGSRefAlias; + +typedef bool (* _EvaluationConditions)(Condition ** condition, TESObjectREFR * ref1, TESObjectREFR * ref2); +extern RelocAddr <_EvaluationConditions> EvaluationConditions; // Evaluates whole condition LinkedList + +// 10 +class TBO_InstanceData : public BSIntrusiveRefCounted +{ +public: + virtual ~TBO_InstanceData(); + + virtual void Unk_01(void); + virtual void Unk_02(void); + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual void Unk_05(void); + virtual void Unk_06(void); + virtual void Unk_07(void); + virtual void Unk_08(void); + virtual void Unk_09(void); + virtual void Unk_0A(void); + virtual void Unk_0B(void); + virtual void Unk_0C(void); + virtual void Unk_0D(void); + virtual void Unk_0E(void); + virtual void Unk_0F(void); + virtual void Unk_10(void); + virtual void Unk_11(void); + virtual void Unk_12(void); + +// void ** _vtbl; // 00 +// BSIntrusiveRefCounted refCount; // 08 + + struct DamageTypes + { + BGSDamageType * damageType; // 00 + UInt32 value; // 08 + }; + + struct ValueModifier + { + ActorValueInfo * avInfo; // 00 + UInt32 unk08; // 08 + }; +}; + +// 08 +class BaseFormComponent +{ +public: + BaseFormComponent(); + virtual ~BaseFormComponent(); + + virtual void Unk_01(void); + virtual void Unk_02(void); + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual void Unk_05(void); + virtual void Unk_06(void); + +// void ** _vtbl; // 00 +}; + +// 08 +class IAnimationGraphManagerHolder +{ +public: + virtual ~IAnimationGraphManagerHolder(); + + virtual void Unk_01(void); + virtual void Unk_02(void); + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual void Unk_05(void); + virtual void Unk_06(void); + virtual void Unk_07(void); + virtual void Unk_08(void); + virtual void Unk_09(void); + virtual void Unk_0A(void); + virtual void Unk_0B(void); + virtual void Unk_0C(void); + virtual void Unk_0D(void); + virtual void Unk_0E(void); + virtual void Unk_0F(void); + virtual void Unk_10(void); + virtual void Unk_11(void); + virtual void Unk_12(void); + virtual void Unk_13(void); + virtual void Unk_14(void); + virtual void Unk_15(void); + virtual void Unk_16(void); + virtual void Unk_17(void); + virtual void Unk_18(void); + +// void ** _vtbl; +}; + +// 08 +class IKeywordFormBase +{ +public: + virtual ~IKeywordFormBase(); + + virtual void Unk_01(void); + virtual void Unk_02(void); + +// void ** _vtbl; +}; + +// 08 +class ActorValueOwner +{ +public: + virtual ~ActorValueOwner(); + + virtual float GetValue(ActorValueInfo * actorValueInfo); + virtual float GetMaximum(ActorValueInfo * actorValueInfo); + virtual float GetBase(ActorValueInfo * actorValueInfo); + virtual void SetBase(ActorValueInfo * actorValueInfo, float value); + virtual void ModBase(ActorValueInfo * actorValueInfo, float value); + virtual void Mod(UInt32 unk1, ActorValueInfo * actorValueInfo, float value); // unk1=0 for regular ModAV + virtual float GetMod(UInt32 unk1, ActorValueInfo * actorValueInfo); + virtual void Unk_08(ActorValueInfo * actorValueInfo, float value); // Calls Mod with unk1=2 + virtual void Unk_09(ActorValueInfo * actorValueInfo, float value); // Just calls SetBase + virtual void Unk_0A(void); + +// void ** _vtbl; // 00 +}; + +// 10 +class TESFullName : public BaseFormComponent +{ +public: + virtual ~TESFullName(); + + virtual void Unk_07(void); + virtual void Unk_08(void); + + BSFixedString name; // 08 +}; + +// 10 +class BGSPreviewTransform : public BaseFormComponent +{ +public: + virtual ~BGSPreviewTransform(); + + void * unk08; // 08 +}; + +// 08 +class BGSSoundTagComponent : public BaseFormComponent +{ +public: + virtual ~BGSSoundTagComponent(); +}; + + +// 68 +class TESActorBaseData : public BaseFormComponent +{ +public: + virtual ~TESActorBaseData(); + + virtual void Unk_07(void); + virtual void Unk_08(void); + virtual void Unk_09(void); + + enum + { + kFlagFemale = 0x01, + kFlagEssential = 0x02, + kFlagIsPreset = 0x04, + kFlagRespawn = 0x08, + kFlagAutoCalcStats = 0x10, + kFlagUnique = 0x20, + kFlagDoesntAffectStealthMeter = 0x40, + kFlagPCLevelMult = 0x80, + kFlagProtected = 0x800 + }; + + UInt64 flags; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + BGSVoiceType * voiceType; // 28 + UInt64 unk30[7]; // 30 + + MEMBER_FN_PREFIX(TESActorBaseData); + DEFINE_MEMBER_FN(SetSex, void, 0x00149720, UInt32 unk1, bool isFemale, UInt32 unk2); // unk1 = 1, unk2 = 1 + DEFINE_MEMBER_FN(GetLevel, UInt16, 0x001497F0); +}; +STATIC_ASSERT(sizeof(TESActorBaseData) == 0x68); + +// 18 +class TESContainer : public BaseFormComponent +{ +public: + struct Entry + { + struct Data + { + UInt32 unk00; + UInt32 unk04; + float unk08; + }; + + UInt32 count; // 00 + UInt32 pad04; // 04 + TESForm * form; // 08 + Data * data; // 10 - extra data? + }; + + Entry ** entries; // 08 + UInt32 numEntries; // 10 + UInt32 pad14; // 14 +}; + +// 28 +class TESAIForm : public BaseFormComponent +{ +public: + UInt64 unk08[(0x28 - 0x08) / 8]; // 08 +}; + +// 10 +class BGSSkinForm : public BaseFormComponent +{ +public: + TESObjectARMO * skin; // 08 +}; + +// 10 +class TESSpellList : public BaseFormComponent +{ +public: + void * unk08; // 08 +}; + +// 30 +class TESModel : public BaseFormComponent +{ +public: + virtual const char * GetModelName(void); + virtual void SetModelName(const char * name); + virtual UInt32 Unk_07(void); + + BSFixedString name; // 08 StringCache::Ref + + UInt32 unk0C; // 0C + UInt32 unk10; // 10 + UInt16 unk14; // 14 + UInt8 unk16; // 16 + UInt8 unk17; // 17 + UInt32 unk18; // 18 + void * unk20; // 20 + UInt32 unk28; // 28 + + enum + { + kFlag_Dynamic = (1 << 0), + kFlag_Unknown = (1 << 1) + }; + + UInt8 flags; // 2C + UInt8 unk2D; // 2D + UInt16 unk2E; // 2E +}; + +// 30 +class BGSBehaviorGraphModel : public TESModel +{ +public: + virtual ~BGSBehaviorGraphModel(); +}; + +// 30 +class BGSTextureModel : public TESModel +{ +public: + virtual ~BGSTextureModel(); +}; + +// 30 +class TESModelTri : public TESModel +{ +public: + virtual ~TESModelTri(); +}; + +// 40 +class BGSModelMaterialSwap : public TESModel +{ +public: + virtual void Unk_08(void); + virtual void Unk_09(void); + + BGSMaterialSwap * materialSwap; // 30 + float unk38; // 34 + UInt32 pad3C; // 38 +}; +STATIC_ASSERT(sizeof(BGSModelMaterialSwap) == 0x40); + + +// 10 +class TESTexture : public BaseFormComponent +{ +public: + virtual UInt32 Unk_07(void); + virtual void Unk_08(void); //virtual void GetNormalMapName(BSString * out); // might have const char * retn type + virtual void Unk_09(void); //virtual const char * GetSearchDir(void); + + BSFixedString str; // 08 StringCache::Ref +}; + +// 10 +class TESIcon : public TESTexture +{ +public: + virtual ~TESIcon(); +}; + +// 08 +class BGSPreloadable : public BaseFormComponent +{ +public: + virtual void Unk_07(void); +}; + +// 18 +class BGSMessageIcon : public BaseFormComponent +{ +public: + TESIcon unk08; // 08 +}; +STATIC_ASSERT(sizeof(BGSMessageIcon) == 0x18); + +// 18 +class BGSBlockBashData : public BaseFormComponent +{ +public: + BGSImpactDataSet * impactDataSet; // 08 BGSImpactDataSet* + BGSMaterialType * materialType; // 10 BGSMaterialType * +}; + +// 20 +class BGSKeywordForm : public BaseFormComponent +{ +public: + virtual bool HasKeyword(void* keyword); //BGSKeyword + virtual UInt32 GetDefaultKeyword(void); + + IKeywordFormBase keywordBase; // 08 + BGSKeyword ** keywords; // 10 BGSKeyword + UInt32 numKeywords; // 18 + UInt32 unk14; // 1C +}; + +// 18 +class BGSPickupPutdownSounds : public BaseFormComponent +{ +public: + BGSSoundDescriptorForm * pickUp; // 08 BGSSoundDescriptorForm + BGSSoundDescriptorForm * putDown; // 10 BGSSoundDescriptorForm +}; + +// 18 +class TESEnchantableForm : public BaseFormComponent +{ +public: + virtual UInt16 Unk_07(void); // return unk10 + + EnchantmentItem * enchantment; // 08 EnchantmentItem + UInt16 unk10; // 10 + UInt16 maxCharge; // 12 + UInt16 pad14[2]; // 14 +}; + +// 10 +class BGSDestructibleObjectForm : public BaseFormComponent +{ +public: + void * data; // 08 +}; + +// 10 +class BGSEquipType : public BaseFormComponent +{ +public: + virtual BGSEquipSlot * GetEquipSlot(void); + virtual void SetEquipSlot(BGSEquipSlot * type); + + BGSEquipSlot * equipSlot; // 08 +}; + +// 18 +class TESDescription : public BaseFormComponent +{ +public: + void * unk08; // 08 + void * unk10; // 10 + + MEMBER_FN_PREFIX(TESDescription); + DEFINE_MEMBER_FN(Get, void, 0x0014FAF0, BSString * out, TESForm * parent); +}; + +// 10 +class BGSInstanceNamingRulesForm : public BaseFormComponent +{ +public: + BGSInstanceNamingRules * rules; // 08 +}; + +// 10 +class BGSTypedKeywordValueArray +{ +public: + void * unk00; // 00 + void * unk08; // 08 +}; + +// 18 +class BGSAttachParentArray : public BaseFormComponent +{ +public: + BGSTypedKeywordValueArray unk08; +}; + +// 10 +class BGSAttackDataForm : public BaseFormComponent +{ +public: + void * unk08; // 08 +}; + +// 18 +class BGSPerkRankArray : public BaseFormComponent +{ +public: + void * unk08; // 08 + UInt32 unk10; // 10 + UInt32 pad14; // 14 +}; + + +// 10 +class BGSPropertySheet : public BaseFormComponent +{ +public: + + struct AVIFProperty + { + ActorValueInfo * actorValue; // 00 + float value; // 08 + UInt32 pad0C; // 0C + }; + + tArray * sheet; // 08 +}; + +// 10 +class TESRaceForm : public BaseFormComponent +{ +public: + TESRace * race; // 08 +}; + +// 38 +class BGSOverridePackCollection : public BaseFormComponent +{ +public: + UInt64 unk08; // 08 + UInt64 unk10; // 08 + UInt64 unk18; // 08 + UInt64 unk20; // 08 + UInt64 unk28; // 08 + UInt64 unk30; // 08 +}; + +// 10 +class BGSForcedLocRefType : public BaseFormComponent +{ +public: + void * unk08; // 08 +}; + +// 10 +class BGSNativeTerminalForm : public BaseFormComponent +{ +public: + BGSTerminal * terminal; // 08 +}; + +// 10 +class BGSBipedObjectForm : public BaseFormComponent +{ +public: + // applicable to DefaultRace + enum + { + kPart_Head = 1 << 0, + kPart_Hair = 1 << 1, + kPart_Body = 1 << 2, + kPart_Hands = 1 << 3, + kPart_Forearms = 1 << 4, + kPart_Amulet = 1 << 5, + kPart_Ring = 1 << 6, + kPart_Feet = 1 << 7, + kPart_Calves = 1 << 8, + kPart_Shield = 1 << 9, + kPart_Unnamed10 = 1 << 10, + kPart_LongHair = 1 << 11, + kPart_Circlet = 1 << 12, + kPart_Ears = 1 << 13, + kPart_Unnamed14 = 1 << 14, + kPart_Unnamed15 = 1 << 15, + kPart_Unnamed16 = 1 << 16, + kPart_Unnamed17 = 1 << 17, + kPart_Unnamed18 = 1 << 18, + kPart_Unnamed19 = 1 << 19, + kPart_Unnamed20 = 1 << 20, + kPart_Unnamed21 = 1 << 21, + kPart_Unnamed22 = 1 << 22, + kPart_Unnamed23 = 1 << 23, + kPart_Unnamed24 = 1 << 24, + kPart_Unnamed25 = 1 << 25, + kPart_Unnamed26 = 1 << 26, + kPart_Unnamed27 = 1 << 27, + kPart_Unnamed28 = 1 << 28, + kPart_Unnamed29 = 1 << 29, + kPart_Unnamed30 = 1 << 30, + kPart_FX01 = 1 << 31, + }; + + enum + { + kWeight_Light = 0, + kWeight_Heavy, + kWeight_None, + }; + + struct Data + { + UInt32 parts; // 00 - init'd to 0 + UInt32 weightClass; // 04 - init'd to 2 (none) + }; + + Data data; // 08 + + UInt32 GetSlotMask() const { return data.parts; } + void SetSlotMask(UInt32 mask) { data.parts = mask; } + + UInt32 AddSlotToMask(UInt32 slot) + { + data.parts |= slot; + return data.parts; + } + + UInt32 RemoveSlotFromMask(UInt32 slot) + { + data.parts &= ~slot; + return data.parts; + } +}; + +// 08 +class BSISoundDescriptor +{ +public: + virtual ~BSISoundDescriptor(); + + virtual void Unk_01(void) = 0; + virtual void Unk_02(void) = 0; + virtual void Unk_03(void) = 0; + virtual void Unk_04(void) = 0; + + // void ** _vtbl; // 00 +}; + +// 08 +class BGSSoundDescriptor : public BSISoundDescriptor +{ +public: + virtual void Unk_01(void); + virtual void Unk_02(void); + virtual void Unk_03(void); + virtual void Unk_04(void); + + // void ** _vtbl; // 00 +}; + +// 10 +class TESWeightForm : public BaseFormComponent +{ +public: + float weight; // 08 + UInt32 pad0C; // 0C +}; + +// 10 +class BGSCraftingUseSound : public BaseFormComponent +{ +public: + UInt64 unk08; // 08 +}; + +// 10 +class BGSMenuDisplayObject : public BaseFormComponent +{ +public: + UInt64 unk08; // 08 +}; + +// 10 +class TESValueForm : public BaseFormComponent +{ +public: + UInt32 value; // 08 + UInt32 unk0C; // 0C +}; + +// 108 +class TESBipedModelForm : public BaseFormComponent +{ +public: + UInt64 unk[(0x108-0x8)/8]; +}; +STATIC_ASSERT(sizeof(TESBipedModelForm) == 0x108); + +// 10 +class BGSFeaturedItemMessage : public BaseFormComponent +{ +public: + UInt64 unk08; // 08 +}; + +// 30 +class TESLeveledList : public BaseFormComponent +{ +public: + virtual void Unk_07(void); + virtual void Unk_08(void); + virtual void Unk_09(void); + virtual void Unk_0A(void); + + struct Entry + { + TESForm * form; // 00 + void * unk08; // 08 + UInt16 count; // 04 + UInt16 level; // 06 + UInt32 unk8; // 08 + }; + + enum + { + kFlagCalculateFromAllLevelsLTPCLevel = 1 << 0, + kFlagCalculateForEachItemInCount = 1 << 1 + }; + + UInt64 unk08; // 08 + tArray * filterKeywords; // 10 + Entry * entries; // 18 + Entry ** scriptAddedEntries; // 20 + UInt8 scriptAdded; // 28 + UInt8 length; // 29 + UInt8 unk2A; // 2A + UInt8 flags; // 2B + UInt32 unk2C; // 2C +}; +STATIC_ASSERT(sizeof(TESLeveledList) == 0x30); + +// 18 +class MagicTarget +{ +public: + virtual ~MagicTarget(); + + virtual void Unk_01(void); + virtual void Unk_02(void); + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual void Unk_05(void); + virtual void Unk_06(void); + virtual void Unk_07(void); + virtual void Unk_08(void); + virtual void Unk_09(void); + virtual void Unk_0A(void); + virtual void Unk_0B(void); + virtual void Unk_0C(void); + + void * unk08; // 08 + void * unk10; // 10 +}; + +// 08 +struct IMovementInterface +{ +public: + virtual ~IMovementInterface(); +}; + +// 08 +class IMovementState : public IMovementInterface +{ +public: + virtual void Unk_01() = 0; + virtual void Unk_02() = 0; + virtual void Unk_03() = 0; + virtual void Unk_04() = 0; + virtual void Unk_05() = 0; + virtual void Unk_06() = 0; + virtual void Unk_07() = 0; + virtual void Unk_08() = 0; + virtual void Unk_09() = 0; + virtual void Unk_0A() = 0; + virtual void Unk_0B() = 0; + virtual void Unk_0C() = 0; + virtual void Unk_0D() = 0; + virtual void Unk_0E() = 0; + virtual void Unk_0F() = 0; + virtual void Unk_10() = 0; + virtual void Unk_11() = 0; + virtual void Unk_12() = 0; + virtual void Unk_13() = 0; + virtual void Unk_14() = 0; + virtual void Unk_15() = 0; + virtual void Unk_16() = 0; + virtual void Unk_17() = 0; + virtual void Unk_18() = 0; + virtual void Unk_19() = 0; + virtual void Unk_1A() = 0; + virtual void Unk_1B() = 0; + virtual void Unk_1C() = 0; + virtual void Unk_1D() = 0; + virtual void Unk_1E() = 0; + virtual void Unk_1F() = 0; + virtual void Unk_20() = 0; +}; + +// 10 +class ActorState : public IMovementState +{ +public: + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); + virtual void Unk_06(); + virtual void Unk_07(); + virtual void Unk_08(); + virtual void Unk_09(); + virtual void Unk_0A(); + virtual void Unk_0B(); + virtual void Unk_0C(); + virtual void Unk_0D(); + virtual void Unk_0E(); + virtual void Unk_0F(); + virtual void Unk_10(); + virtual void Unk_11(); + virtual void Unk_12(); + virtual void Unk_13(); + virtual void Unk_14(); + virtual void Unk_15(); + virtual void Unk_16(); + virtual void Unk_17(); + virtual void Unk_18(); + virtual void Unk_19(); + virtual void Unk_1A(); + virtual void Unk_1B(); + virtual void Unk_1C(); + virtual void Unk_1D(); + virtual void Unk_1E(); + virtual void Unk_1F(); + virtual void Unk_20(); + + enum Flags + { + kUnk1 = 0x80000, + kUnk2 = 0x40000, + + kWeaponStateShift = 1, + kWeaponStateMask = 0x07, + + kWeaponState_Drawn = 0x03, + }; + + UInt32 unk08; // 08 + UInt32 flags; // 0C + + UInt32 GetWeaponState() { return (flags >> kWeaponStateShift) & kWeaponStateMask; } + bool IsWeaponDrawn() { return GetWeaponState() >= kWeaponState_Drawn; } +}; + +// 08 +class IPostAnimationChannelUpdateFunctor +{ + virtual ~IPostAnimationChannelUpdateFunctor(); + + virtual void Unk_01(); +}; + +// 38 +class Condition +{ +public: + enum CompareFlags { + kCompareFlag_And = 0x00, + kCompareFlag_Or = 0x01, + kCompareFlag_Equal = 0x00, + kCompareFlag_UseAliases = 0x02, + kCompareFlag_Global = 0x04, + kCompareFlag_UsePackData = 0x08, + kCompareFlag_SwapTarget = 0x10, + }; + + enum CompareOperators { + kCompareOp_Equal = 0, + kCompareOp_NotEqual, + kCompareOp_Greater, + kCompareOp_GreaterEqual, + kCompareOp_Less, + kCompareOp_LessEqual + }; + + enum Functions { + kFunction_GetWantBlocking = 0, + kFunction_GetDistance, + kFunction_GetLocked = 5, + kFunction_GetPos, + kFunction_GetAngle = 8, + kFunction_GetStartingPos = 10, + kFunction_GetStartingAngle, + kFunction_GetSecondsPassed, + kFunction_GetValue = 14, + kFunction_GetCurrentTime = 18, + kFunction_GetScale = 24, + kFunction_IsMoving, + kFunction_IsTurning, + kFunction_GetLineOfSight, + kFunction_GetInSameCell = 32, + kFunction_GetDisabled = 35, + kFunction_MenuMode, + kFunction_GetDisease = 39, + kFunction_GetClothingValue = 41, + kFunction_SameFaction, + kFunction_SameRace, + kFunction_SameSex, + kFunction_GetDetected, + kFunction_GetDead, + kFunction_GetItemCount, + kFunction_GetGold, + kFunction_GetSleeping, + kFunction_GetTalkedToPC, + kFunction_GetQuestRunning = 56, + kFunction_GetStage = 58, + kFunction_GetStageDone, + kFunction_GetFactionRankDifference, + kFunction_GetAlarmed, + kFunction_IsRaining, + kFunction_GetAttacked, + kFunction_GetIsCreature, + kFunction_GetLockLevel, + kFunction_GetShouldAttack, + kFunction_GetInCell, + kFunction_GetIsClass, + kFunction_GetIsRace, + kFunction_GetIsSex, + kFunction_GetInFaction, + kFunction_GetIsID, + kFunction_GetFactionRank, + kFunction_GetGlobalValue, + kFunction_IsSnowing, + kFunction_GetRandomPercent = 77, + kFunction_WouldBeStealing = 79, + kFunction_GetLevel, + kFunction_IsRotating, + kFunction_GetDeadCount = 84, + kFunction_GetIsAlerted = 91, + kFunction_GetPlayerControlsDisabled = 98, + kFunction_GetHeadingAngle, + kFunction_IsWeaponMagicOut = 101, + kFunction_IsTorchOut, + kFunction_IsShieldOut, + kFunction_IsFacingUp = 106, + kFunction_GetKnockedState, + kFunction_GetWeaponAnimType, + kFunction_IsWeaponSkillType, + kFunction_GetCurrentAIPackage, + kFunction_IsWaiting, + kFunction_IsIdlePlaying, + kFunction_IsIntimidatedbyPlayer = 116, + kFunction_IsPlayerInRegion, + kFunction_GetActorAggroRadiusViolated, + kFunction_GetCrime = 122, + kFunction_IsGreetingPlayer, + kFunction_IsGuard = 125, + kFunction_HasBeenEaten = 127, + kFunction_GetStaminaPercentage, + kFunction_HasBeenRead, + kFunction_GetDying, + kFunction_GetSceneActionPercent, + kFunction_WouldRefuseCommand, + kFunction_SameFactionAsPC, + kFunction_SameRaceAsPC, + kFunction_SameSexAsPC, + kFunction_GetIsReference, + kFunction_IsTalking = 141, + kFunction_GetComponentCount, + kFunction_GetCurrentAIProcedure, + kFunction_GetTrespassWarningLevel, + kFunction_IsTrespassing, + kFunction_IsInMyOwnedCell, + kFunction_GetWindSpeed, + kFunction_GetCurrentWeatherPercent, + kFunction_GetIsCurrentWeather, + kFunction_IsContinuingPackagePCNear, + kFunction_GetIsCrimeFaction = 152, + kFunction_CanHaveFlames, + kFunction_HasFlames, + kFunction_GetOpenState = 157, + kFunction_GetSitting = 159, + kFunction_GetIsCurrentPackage = 161, + kFunction_IsCurrentFurnitureRef, + kFunction_IsCurrentFurnitureObj, + kFunction_GetDayOfWeek = 170, + kFunction_GetTalkedToPCParam = 172, + kFunction_IsPCSleeping = 175, + kFunction_IsPCAMurderer, + kFunction_HasSameEditorLocationAsRef = 180, + kFunction_HasSameEditorLocationAsRefAlias, + kFunction_GetEquipped, + kFunction_IsSwimming = 185, + kFunction_GetAmountSoldStolen = 190, + kFunction_GetIgnoreCrime = 192, + kFunction_GetPCExpelled, + kFunction_GetPCFactionMurder = 195, + kFunction_GetPCEnemyofFaction = 197, + kFunction_GetPCFactionAttack = 199, + kFunction_GetDestroyed = 203, + kFunction_HasMagicEffect = 214, + kFunction_GetDefaultOpen, + kFunction_IsSpellTarget = 223, + kFunction_GetVATSMode, + kFunction_GetPersuasionNumber, + kFunction_GetVampireFeed, + kFunction_GetCannibal, + kFunction_GetIsClassDefault, + kFunction_GetClassDefaultMatch, + kFunction_GetInCellParam, + kFunction_GetPlayerDialogueInput, + kFunction_GetVatsTargetHeight = 235, + kFunction_GetIsGhost = 237, + kFunction_GetUnconscious = 242, + kFunction_GetRestrained = 244, + kFunction_GetIsUsedItem = 246, + kFunction_GetIsUsedItemType, + kFunction_IsScenePlaying, + kFunction_IsInDialogueWithPlayer, + kFunction_GetLocationCleared, + kFunction_GetIsPlayableRace = 254, + kFunction_GetOffersServicesNow, + kFunction_HasAssociationType = 258, + kFunction_HasFamilyRelationship, + kFunction_HasParentRelationship = 261, + kFunction_IsWarningAbout, + kFunction_IsWeaponOut, + kFunction_HasSpell, + kFunction_IsTimePassing, + kFunction_IsPleasant, + kFunction_IsCloudy, + kFunction_IsSmallBump = 274, + kFunction_GetBaseValue = 277, + kFunction_IsOwner, + kFunction_IsCellOwner = 280, + kFunction_IsHorseStolen = 282, + kFunction_IsLeftUp = 285, + kFunction_IsSneaking, + kFunction_IsRunning, + kFunction_GetFriendHit, + kFunction_IsInCombat, + kFunction_IsInInterior = 300, + kFunction_IsWaterObject = 304, + kFunction_GetPlayerAction, + kFunction_IsActorUsingATorch, + kFunction_IsXBox = 309, + kFunction_GetInWorldspace, + kFunction_GetPCMiscStat = 312, + kFunction_GetPairedAnimation, + kFunction_IsActorAVictim, + kFunction_GetTotalPersuasionNumber, + kFunction_GetIdleDoneOnce = 318, + kFunction_GetNoRumors = 320, + kFunction_GetCombatState = 323, + kFunction_GetWithinPackageLocation = 325, + kFunction_IsRidingMount = 327, + kFunction_IsFleeing = 329, + kFunction_IsInDangerousWater = 332, + kFunction_GetIgnoreFriendlyHits = 338, + kFunction_IsPlayersLastRiddenMount, + kFunction_IsActor = 353, + kFunction_IsEssential, + kFunction_IsPlayerMovingIntoNewSpace = 358, + kFunction_GetInCurrentLocation, + kFunction_GetInCurrentLocationAlias, + kFunction_GetTimeDead, + kFunction_HasLinkedRef, + kFunction_IsChild = 365, + kFunction_GetStolenItemValueNoCrime, + kFunction_GetLastPlayerAction, + kFunction_IsPlayerActionActive, + kFunction_IsTalkingActivatorActor = 370, + kFunction_IsInList = 372, + kFunction_GetStolenItemValue, + kFunction_GetCrimeGoldViolent = 375, + kFunction_GetCrimeGoldNonviolent, + kFunction_IsOwnedBy = 378, + kFunction_GetCommandDistance = 380, + kFunction_GetCommandLocationDistance, + kFunction_GetHitLocation = 390, + kFunction_IsPC1stPerson, + kFunction_GetCauseofDeath = 396, + kFunction_IsLimbGone, + kFunction_IsWeaponInList, + kFunction_IsBribedbyPlayer = 402, + kFunction_GetRelationshipRank, + kFunction_GetVATSValue = 407, + kFunction_IsKiller, + kFunction_IsKillerObject, + kFunction_GetFactionCombatReaction, + kFunction_Exists = 414, + kFunction_GetGroupMemberCount, + kFunction_GetGroupTargetCount, + kFunction_GetIsVoiceType = 426, + kFunction_GetPlantedExplosive, + kFunction_IsScenePackageRunning = 429, + kFunction_GetHealthPercentage, + kFunction_GetIsObjectType = 432, + kFunction_PlayerVisualDetection = 434, + kFunction_PlayerAudioDetection, + kFunction_GetIsCreatureType = 437, + kFunction_HasKey, + kFunction_IsFurnitureEntryType, + kFunction_GetInCurrentLocationFormList = 444, + kFunction_GetInZone, + kFunction_GetVelocity, + kFunction_GetGraphVariableFloat, + kFunction_HasPerk, + kFunction_GetFactionRelation, + kFunction_IsLastIdlePlayed, + kFunction_GetPlayerTeammate = 453, + kFunction_GetPlayerTeammateCount, + kFunction_GetActorCrimePlayerEnemy = 458, + kFunction_GetCrimeGold, + kFunction_IsPlayerGrabbedRef = 463, + kFunction_GetKeywordItemCount = 465, + kFunction_GetDestructionStage = 470, + kFunction_GetIsAlignment = 473, + kFunction_IsProtected = 476, + kFunction_GetThreatRatio, + kFunction_GetIsUsedItemEquipType = 479, + kFunction_GetPlayerActivated = 483, + kFunction_GetFullyEnabledActorsInHigh = 485, + kFunction_IsCarryable = 487, + kFunction_GetConcussed, + kFunction_GetMapMarkerVisible = 491, + kFunction_PlayerKnows = 493, + kFunction_GetPermanentValue, + kFunction_GetKillingBlowLimb, + kFunction_CanPayCrimeGold = 497, + kFunction_GetDaysInJail = 499, + kFunction_EPAlchemyGetMakingPoison, + kFunction_EPAlchemyEffectHasKeyword, + kFunction_GetAllowWorldInteractions = 503, + kFunction_DialogueGetAv = 506, + kFunction_DialogueHasPerk, + kFunction_GetLastHitCritical, + kFunction_DialogueGetItemCount = 510, + kFunction_LastCrippledCondition, + kFunction_HasSharedPowerGrid, + kFunction_IsCombatTarget, + kFunction_GetVATSRightAreaFree = 515, + kFunction_GetVATSLeftAreaFree, + kFunction_GetVATSBackAreaFree, + kFunction_GetVATSFrontAreaFree, + kFunction_GetIsLockBroken, + kFunction_IsPS3, + kFunction_IsWindowsPC, + kFunction_GetVATSRightTargetVisible, + kFunction_GetVATSLeftTargetVisible, + kFunction_GetVATSBackTargetVisible, + kFunction_GetVATSFrontTargetVisible, + kFunction_IsInCriticalStage = 528, + kFunction_GetXPForNextLevel = 530, + kFunction_GetInfamy = 533, + kFunction_GetInfamyViolent, + kFunction_GetInfamyNonViolent, + kFunction_GetTypeCommandPerforming, + kFunction_GetQuestCompleted = 543, + kFunction_GetSpeechChallengeSuccessLevel, + kFunction_IsGoreDisabled = 547, + kFunction_IsSceneActionComplete = 550, + kFunction_GetSpellUsageNum = 552, + kFunction_GetActorsInHigh = 554, + kFunction_HasLoaded3D, + kFunction_HasKeyword = 560, + kFunction_HasRefType, + kFunction_LocationHasKeyword, + kFunction_LocationHasRefType, + kFunction_GetIsEditorLocation = 565, + kFunction_GetIsAliasRef, + kFunction_GetIsEditorLocationAlias, + kFunction_IsSprinting, + kFunction_IsBlocking, + kFunction_HasEquippedSpell, + kFunction_GetCurrentCastingType, + kFunction_GetCurrentDeliveryType, + kFunction_GetAttackState = 574, + kFunction_GetEventData = 576, + kFunction_IsCloserToAThanB, + kFunction_LevelMinusPCLevel, + kFunction_IsBleedingOut = 580, + kFunction_GetRelativeAngle = 584, + kFunction_GetMovementDirection = 589, + kFunction_IsInScene, + kFunction_GetRefTypeDeadCount, + kFunction_GetRefTypeAliveCount, + kFunction_GetIsFlying = 594, + kFunction_IsCurrentSpell, + kFunction_SpellHasKeyword, + kFunction_GetEquippedItemType, + kFunction_GetLocationAliasCleared, + kFunction_GetLocationAliasRefTypeDeadCount = 600, + kFunction_GetLocationAliasRefTypeAliveCount, + kFunction_IsWardState, + kFunction_IsInSameCurrentLocationAsRef, + kFunction_IsInSameCurrentLocationAsRefAlias, + kFunction_LocationAliasIsLocation, + kFunction_GetKeywordDataForLocation, + kFunction_GetKeywordDataForAlias = 608, + kFunction_LocationAliasHasKeyword = 610, + kFunction_IsNullPackageData, + kFunction_GetNumericPackageData, + kFunction_IsPlayerRadioOn, + kFunction_GetPlayerRadioFrequency, + kFunction_GetHighestRelationshipRank, + kFunction_GetLowestRelationshipRank, + kFunction_HasAssociationTypeAny, + kFunction_HasFamilyRelationshipAny, + kFunction_GetPathingTargetOffset, + kFunction_GetPathingTargetAngleOffset, + kFunction_GetPathingTargetSpeed, + kFunction_GetPathingTargetSpeedAngle, + kFunction_GetMovementSpeed, + kFunction_GetInContainer, + kFunction_IsLocationLoaded, + kFunction_IsLocationAliasLoaded, + kFunction_IsDualCasting, + kFunction_GetVMQuestVariable = 629, + kFunction_GetCombatAudioDetection, + kFunction_GetCombatVisualDetection, + kFunction_IsCasting, + kFunction_GetFlyingState, + kFunction_IsInFavorState = 635, + kFunction_HasTwoHandedWeaponEquipped, + kFunction_IsFurnitureExitType, + kFunction_IsInFriendStatewithPlayer, + kFunction_GetWithinDistance, + kFunction_GetValuePercent, + kFunction_IsUnique, + kFunction_GetLastBumpDirection, + kFunction_GetInfoChallangeSuccess = 644, + kFunction_GetIsInjured, + kFunction_GetIsCrashLandRequest, + kFunction_GetIsHastyLandRequest, + kFunction_IsLinkedTo = 650, + kFunction_GetKeywordDataForCurrentLocation, + kFunction_GetInSharedCrimeFaction, + kFunction_GetBribeSuccess = 654, + kFunction_GetIntimidateSuccess, + kFunction_GetArrestedState, + kFunction_GetArrestingActor, + kFunction_HasVMScript = 659, + kFunction_GetVMScriptVariable, + kFunction_GetWorkshopResourceDamage, + kFunction_HasValidRumorTopic = 664, + kFunction_IsAttacking = 672, + kFunction_IsPowerAttacking, + kFunction_IsLastHostileActor, + kFunction_GetGraphVariableInt, + kFunction_ShouldAttackKill = 678, + kFunction_GetActivationHeight = 680, + kFunction_WornHasKeyword = 682, + kFunction_GetPathingCurrentSpeed, + kFunction_GetPathingCurrentSpeedAngle, + kFunction_GetWorkshopObjectCount = 691, + kFunction_EPMagic_SpellHasKeyword = 693, + kFunction_GetNoBleedoutRecovery, + kFunction_EPMagic_SpellHasSkill = 696, + kFunction_IsAttackType, + kFunction_IsAllowedToFly, + kFunction_HasMagicEffectKeyword, + kFunction_IsCommandedActor, + kFunction_IsStaggered, + kFunction_IsRecoiling, + kFunction_HasScopeWeaponEquipped, + kFunction_IsPathing, + kFunction_GetShouldHelp, + kFunction_HasBoundWeaponEquipped, + kFunction_GetCombatTargetHasKeyword, + kFunction_GetCombatGroupMemberCount = 709, + kFunction_IsIgnoringCombat, + kFunction_GetLightLevel, + kFunction_SpellHasCastingPerk = 713, + kFunction_IsBeingRidden, + kFunction_IsUndead, + kFunction_GetRealHoursPassed, + kFunction_IsUnlockedDoor = 718, + kFunction_IsHostileToActor, + kFunction_GetTargetHeight, + kFunction_IsPoison, + kFunction_WornApparelHasKeywordCount, + kFunction_GetItemHealthPercent, + kFunction_EffectWasDualCast, + kFunction_GetKnockStateEnum, + kFunction_DoesNotExist, + kFunction_GetPlayerWalkAwayFromDialogueScene = 728, + kFunction_GetActorStance, + kFunction_CanProduceForWorkshop = 734, + kFunction_CanFlyHere, + kFunction_EPIsDamageType, + kFunction_GetActorGunState = 738, + kFunction_GetVoiceLineLength, + kFunction_ObjectTemplateItem_HasKeyword = 741, + kFunction_ObjectTemplateItem_HasUniqueKeyword, + kFunction_ObjectTemplateItem_GetLevel, + kFunction_MovementIdleMatches, + kFunction_GetActionData, + kFunction_GetActionDataShort, + kFunction_GetActionDataByte, + kFunction_GetActionDataFlag, + kFunction_ModdedItemHasKeyword, + kFunction_GetAngryWithPlayer, + kFunction_IsCameraUnderWater, + kFunction_IsActorRefOwner = 753, + kFunction_HasActorRefOwner, + kFunction_GetLoadedAmmoCount = 756, + kFunction_IsTimeSpanSunrise, + kFunction_IsTimeSpanMorning, + kFunction_IsTimeSpanAfternoon, + kFunction_IsTimeSpanEvening, + kFunction_IsTimeSpanSunset, + kFunction_IsTimeSpanNight, + kFunction_IsTimeSpanMidnight, + kFunction_IsTimeSpanAnyDay, + kFunction_IsTimeSpanAnyNight, + kFunction_CurrentFurnitureHasKeyword, + kFunction_GetWeaponEquipIndex, + kFunction_IsOverEncumbered = 769, + kFunction_IsPackageRequestingBlockedIdles, + kFunction_GetActionDataInt, + kFunction_GetVATSRightMinusLeftAreaFree, + kFunction_GetInIronSights, + kFunction_GetActorStaggerDirection, + kFunction_GetActorStaggerMagnitude, + kFunction_WornCoversBipedSlot, + kFunction_GetInventoryValue, + kFunction_IsPlayerInConversation, + kFunction_IsInDialogueCamera, + kFunction_IsMyDialogueTargetPlayer, + kFunction_IsMyDialogueTargetActor, + kFunction_GetMyDialogueTargetDistance, + kFunction_IsSeatOccupied, + kFunction_IsPlayerRiding, + kFunction_IsTryingEventCamera, + kFunction_UseLeftSideCamera, + kFunction_GetNoteType, + kFunction_LocationHasPlayerOwnedWorkshop, + kFunction_IsStartingAction, + kFunction_IsMidAction, + kFunction_IsWeaponChargeAttack, + kFunction_IsInWorkshopMode, + kFunction_IsWeaponChargingHoldAttack, + kFunction_IsEncounterAbovePlayerLevel, + kFunction_IsMeleeAttacking, + kFunction_GetVATSQueuedTargetsUnique, + kFunction_GetCurrentLocationCleared, + kFunction_IsPowered, + kFunction_GetTransmitterDistance, + kFunction_GetCameraPlaybackTime, + kFunction_IsInWater, + kFunction_GetWithinActivateDistance, + kFunction_IsUnderWater, + kFunction_IsInSameSpace, + kFunction_LocationAllowsReset, + kFunction_GetVATSBackRightAreaFree, + kFunction_GetVATSBackLeftAreaFree, + kFunction_GetVATSBackRightTargetVisible, + kFunction_GetVATSBackLeftTargetVisible, + kFunction_GetVATSTargetLimbVisible, + kFunction_IsPlayerListening, + kFunction_GetPathingRequestedQuickTurn, + kFunction_EPIsCalculatingBaseDamage, + kFunction_GetReanimating, + kFunction_IsInRobotWorkbench = 817 + }; + + enum ReferenceTypes { + kReferenceType_Subject = 0, + kReferenceType_Target, + kReferenceType_Reference, + kReferenceType_CombatTarget, + kReferenceType_LinkedRef, + kReferenceType_Alias, + kReferenceType_PackageData, + kReferenceType_EventData + }; + + union Param + { + float f32; + UInt32 u32; + SInt32 s32; + TESForm * form; + }; + + Condition * next; // 00 + float compareValue; // 08 + UInt32 unk0C; // 0C + UInt32 refHandle; // 10 + UInt32 unk14; // 14 - FFFFFFFF + UInt16 functionId; // 18 + UInt8 unk1A; // 1A + UInt8 unk1B; // 1B + UInt32 unk1C; // 1C + Param param1; // 20 + Param param2; // 28 + + struct CompareType + { + unsigned char flags : 5; + unsigned char op : 3; + }; + + CompareType comparisonType; // 30 + UInt8 referenceType; // 31 + UInt8 unk32[6]; // 32 + + // 78 + class Evaluator + { + public: + Evaluator(TESForm * a1, TESForm * a2) + { + CALL_MEMBER_FN(this, ctor)(a1, a2, 0); + } + + TESForm * a1; // 00 + TESForm * a2; // 08 + UInt64 unk10[(0x78 - 0x08) >> 3]; + + MEMBER_FN_PREFIX(Evaluator); + DEFINE_MEMBER_FN(ctor, void, 0x0072A770, TESForm * a1, TESForm * a2, UInt64 unk1); // a1 might be player or subject, not sure yet + }; + + MEMBER_FN_PREFIX(Condition); + // DB2ACB0447C58B663FE4E9C862B96256D0C2716D+15 + DEFINE_MEMBER_FN(Evaluate, bool, 0x0072C530, Evaluator * eval); // Evaluates only a single condition +}; +STATIC_ASSERT(offsetof(Condition, referenceType) == 0x31); +STATIC_ASSERT(sizeof(Condition) == 0x38); + +// ?? +struct IMovementPlayerControlsFilter : public IMovementInterface +{ +public: + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); + virtual void Unk_06(); + virtual void Unk_07(); + virtual void Unk_08(); + virtual void Unk_09(); +}; + +// ?? +class ActorEquipData +{ +public: + UInt64 unk00; // 00 + NiNode * flattenedBoneTree; // 08 + + enum + { + kMaxSlots = 44 + }; + + // 58 + struct SlotData + { + TESForm * item; // 00 + TBO_InstanceData * instanceData; // 08 + BGSObjectInstanceExtra * extraData; // 10 + TESForm * model; // 18 - ARMA for ARMO and WEAP for WEAP + BGSModelMaterialSwap * modelMatSwap; // 20 + BGSTextureSet * textureSet; // 28 + NiAVObject * node; // 30 + void * unk38; // 38 + IAnimationGraphManagerHolder * unk40; // 40 + UInt64 unk48; // 48 + UInt32 unk50; // 50 + UInt32 unk54; // 54 + }; + + SlotData slots[kMaxSlots]; +}; + +// 08 +class EquippedItemData : public NiRefObject +{ +public: + virtual ~EquippedItemData(); +}; + +// 38 +class EquippedWeaponData : public EquippedItemData +{ +public: + virtual ~EquippedWeaponData(); + + TESAmmo * ammo; // 10 + UInt64 unk18; // 18 + void * unk20; // 20 + UInt64 unk28; // 28 + NiAVObject * object; // 30 +}; + +// 10 +struct BGSInventoryItem +{ + TESForm * form; // 00 + + // 28 + class Stack : public BSIntrusiveRefCounted + { + public: + virtual ~Stack(); + + Stack * next; // 10 + ExtraDataList * extraData; // 18 + SInt32 count; // 20 + + enum + { + kFlagEquipped = 7 + }; + + UInt16 flags; // 24 + +#ifdef _DEBUG + void Dump(); +#endif + template + bool Visit(T & f) + { + if(f(this) && next) + return next->Visit(f); + return true; // Continue + } + }; + +#ifdef _DEBUG + void Dump(); +#endif + + Stack * stack; // 08 +}; + +// ?? +class BGSInventoryList +{ +public: + UInt64 unk00; // 00 + tArray> eventSinks; // 08 + UInt64 unk20[(0x58-0x20)/8]; // 20 + tArray items; // 58 + float inventoryWeight; // 70 - is (-1) when not calculated + UInt32 unk74; // 74 + BSReadWriteLock inventoryLock; // 78 +}; + +class IAliasFunctor +{ +public: + virtual ~IAliasFunctor() { } + + virtual UInt32 Visit(BGSRefAlias * alias) = 0; +}; + +// 08 +class BGSPerkEntry +{ +public: + virtual ~BGSPerkEntry(); + + void * unk08; // 08 +}; diff --git a/f4se/f4se/GameForms.cpp b/f4se/f4se/GameForms.cpp new file mode 100644 index 0000000..93ba5e7 --- /dev/null +++ b/f4se/f4se/GameForms.cpp @@ -0,0 +1,10 @@ +#include "f4se/GameForms.h" +#include "f4se/GameUtilities.h" + +RelocAddr <_LookupFormByID> LookupFormByID(0x00152C90); + +// 19AD05F2961D07B65E7987D210D6A47199FC0EFA+21 +RelocPtr g_formFactoryList(0x058D3BF0); + +// 7156C2CD12E2BEAE5271E10ECFC738D8D24050B8+F1 +RelocPtr > g_modAttachmentMap(0x036E2148-0x08); diff --git a/f4se/f4se/GameForms.h b/f4se/f4se/GameForms.h new file mode 100644 index 0000000..e4ad133 --- /dev/null +++ b/f4se/f4se/GameForms.h @@ -0,0 +1,1921 @@ +#pragma once + +#include "f4se_common/Relocation.h" +#include "f4se/GameFormComponents.h" +#include "f4se/GameCustomization.h" +#include "f4se/GameUtilities.h" + +#include + +struct ModInfo; +class TESForm; +class TESNPC; +class BGSColorForm; +class BGSHeadPart; +class BSFile; +class TESGlobal; +class ActorValueInfo; +class TESEffectShader; +class BGSProjectile; +class BGSPerk; +class BGSExplosion; +class BGSArtObject; +class TESImageSpaceModifier; +class TESObjectLIGH; +class BGSEncounterZone; +class BGSMusicType; +class IFormFactory; +class TESObjectMISC; +class TESImageSpace; +class SpellItem; +class TESObjectLAND; +class TESWorldSpace; +class bhkWorld; +class BGSComponent; + +typedef TESForm* (* _LookupFormByID)(UInt32 id); +extern RelocAddr <_LookupFormByID> LookupFormByID; +extern RelocPtr g_formFactoryList; + +/**** form types *************************************************************** +* +* +* Code id name + NONE 0 + TES4 1 + GRUP 2 + GMST 3 + KYWD 4 BGSKeyword + LCRT 5 BGSLocationRefType + AACT 6 BGSAction + TRNS 7 + CMPO 8 + TXST 9 BGSTextureSet + MICN 10 BGSMenuIcon + GLOB 11 TESGlobal + DMGT 12 + CLAS 13 TESClass + FACT 14 TESFaction + HDPT 15 BGSHeadPart + EYES 16 TESEyes + RACE 17 TESRace + SOUN 18 TESSound + ASPC 19 BGSAcousticSpace + SKIL 20 + MGEF 21 EffectSetting + SCPT 22 Script + LTEX 23 TESLandTexture + ENCH 24 EnchantmentItem + SPEL 25 SpellItem + SCRL 26 ScrollItem + ACTI 27 TESObjectACTI + TACT 28 BGSTalkingActivator + ARMO 29 TESObjectARMO + BOOK 30 TESObjectBOOK + CONT 31 TESObjectCONT + DOOR 32 TESObjectDOOR + INGR 33 IngredientItem + LIGH 34 TESObjectLIGH + MISC 35 TESObjectMISC + STAT 36 TESObjectSTAT + SCOL 37 BGSStaticCollection + MSTT 38 BGSMovableStatic + GRAS 39 TESGrass + TREE 40 TESObjectTREE + FLOR 41 TESFlora + FURN 42 TESFurniture + WEAP 43 TESObjectWEAP + AMMO 44 TESAmmo + NPC_ 45 TESNPC + LVLN 46 TESLevCharacter + KEYM 47 TESKey + ALCH 48 AlchemyItem + IDLM 49 BGSIdleMarker + NOTE 50 BGSNote + PROJ 51 BGSProjectile + HAZD 52 BGSHazard + BNDS 53 + SLGM 54 TESSoulGem + TERM 55 BGSTerminal + LVLI 56 TESLevItem + WTHR 57 TESWeather + CLMT 58 TESClimate + SPGD 59 BGSShaderParticleGeometryData + RFCT 60 BGSReferenceEffect + REGN 61 TESRegion + NAVI 62 NavMeshInfoMap + CELL 63 TESObjectCELL + REFR 64 TESObjectREFR / Actor + ACHR 65 Character / PlayerCharacter + PMIS 66 MissileProjectile + PARW 67 ArrowProjectile + PGRE 68 GrenadeProjectile + PBEA 69 BeamProjectile + PFLA 70 FlameProjectile + PCON 71 ConeProjectile + PBAR 72 BarrierProjectile + PHZD 73 Hazard + WRLD 74 TESWorldSpace + LAND 75 TESObjectLAND + NAVM 76 NavMesh + TLOD 77 + DIAL 78 TESTopic + INFO 79 TESTopicInfo + QUST 80 TESQuest + IDLE 81 TESIdleForm + PACK 82 TESPackage + CSTY 83 TESCombatStyle + LSCR 84 TESLoadScreen + LVSP 85 TESLevSpell + ANIO 86 TESObjectANIO + WATR 87 TESWaterForm + EFSH 88 TESEffectShader + TOFT 89 + EXPL 90 BGSExplosion + DEBR 91 BGSDebris + IMGS 92 TESImageSpace + IMAD 93 TESImageSpaceModifier + FLST 94 BGSListForm + PERK 95 BGSPerk + BPTD 96 BGSBodyPartData + ADDN 97 BGSAddonNode + AVIF 98 ActorValueInfo + CAMS 99 BGSCameraShot + CPTH 100 BGSCameraPath + VTYP 101 BGSVoiceType + MATT 102 BGSMaterialType + IPCT 103 BGSImpactData + IPDS 104 BGSImpactDataSet + ARMA 105 TESObjectARMA + ECZN 106 BGSEncounterZone + LCTN 107 BGSLocation + MESG 108 BGSMessage + RGDL 109 BGSRagdoll + DOBJ 110 + DFOB 111 + LGTM 112 BGSLightingTemplate + MUSC 113 BGSMusicType + FSTP 114 BGSFootstep + FSTS 115 BGSFootstepSet + SMBN 116 BGSStoryManagerBranchNode + SMQN 117 BGSStoryManagerQuestNode + SMEN 118 BGSStoryManagerEventNode + DLBR 119 BGSDialogueBranch + MUST 120 BGSMusicTrackFormWrapper + DLVW 121 + WOOP 122 TESWordOfPower + SHOU 123 TESShout + EQUP 124 BGSEquipSlot + RELA 125 BGSRelationship + SCEN 126 BGSScene + ASTP 127 BGSAssociationType + OTFT 128 BGSOutfit + ARTO 129 BGSArtObject + MATO 130 BGSMaterialObject + MOVT 131 BGSMovementType + SNDR 132 BGSSoundDescriptorForm + DUAL 133 BGSDualCastData + SNCT 134 BGSSoundCategory + SOPM 135 BGSSoundOutput + COLL 136 BGSCollisionLayer + CLFM 137 BGSColorForm + REVB 138 BGSReverbParameters + PKIN 139 + RFGP 140 + AMDL 141 + LAYR 142 + COBJ 143 + OMOD 144 BGSMod::Attachment::Mod + MSWP 145 BGSMaterialSwap + ZOOM 146 + INNR 147 + KSSM 148 + AECH 149 + SCCO 150 + AORU 151 + SCSN 152 + STAG 153 + NOCM 154 + LENS 155 + LSPR 156 + GDRY 157 + OVIS 158 + + // Special types + 160 + 161 + 162 + 163 + 164 + 165 +*/ + +enum FormType +{ + kFormType_NONE = 0, + kFormType_TES4, + kFormType_GRUP, + kFormType_GMST, + kFormType_KYWD, + kFormType_LCRT, + kFormType_AACT, + kFormType_TRNS, + kFormType_CMPO, + kFormType_TXST, + kFormType_MICN, + kFormType_GLOB, + kFormType_DMGT, + kFormType_CLAS, + kFormType_FACT, + kFormType_HDPT, + kFormType_EYES, + kFormType_RACE, + kFormType_SOUN, + kFormType_ASPC, + kFormType_SKIL, + kFormType_MGEF, + kFormType_SCPT, + kFormType_LTEX, + kFormType_ENCH, + kFormType_SPEL, + kFormType_SCRL, + kFormType_ACTI, + kFormType_TACT, + kFormType_ARMO, + kFormType_BOOK, + kFormType_CONT, + kFormType_DOOR, + kFormType_INGR, + kFormType_LIGH, + kFormType_MISC, + kFormType_STAT, + kFormType_SCOL, + kFormType_MSTT, + kFormType_GRAS, + kFormType_TREE, + kFormType_FLOR, + kFormType_FURN, + kFormType_WEAP, + kFormType_AMMO, + kFormType_NPC_, + kFormType_LVLN, + kFormType_KEYM, + kFormType_ALCH, + kFormType_IDLM, + kFormType_NOTE, + kFormType_PROJ, + kFormType_HAZD, + kFormType_BNDS, + kFormType_SLGM, + kFormType_TERM, + kFormType_LVLI, + kFormType_WTHR, + kFormType_CLMT, + kFormType_SPGD, + kFormType_RFCT, + kFormType_REGN, + kFormType_NAVI, + kFormType_CELL, + kFormType_REFR, + kFormType_ACHR, + kFormType_PMIS, + kFormType_PARW, + kFormType_PGRE, + kFormType_PBEA, + kFormType_PFLA, + kFormType_PCON, + kFormType_PBAR, + kFormType_PHZD, + kFormType_WRLD, + kFormType_LAND, + kFormType_NAVM, + kFormType_TLOD, + kFormType_DIAL, + kFormType_INFO, + kFormType_QUST, + kFormType_IDLE, + kFormType_PACK, + kFormType_CSTY, + kFormType_LSCR, + kFormType_LVSP, + kFormType_ANIO, + kFormType_WATR, + kFormType_EFSH, + kFormType_TOFT, + kFormType_EXPL, + kFormType_DEBR, + kFormType_IMGS, + kFormType_IMAD, + kFormType_FLST, + kFormType_PERK, + kFormType_BPTD, + kFormType_ADDN, + kFormType_AVIF, + kFormType_CAMS, + kFormType_CPTH, + kFormType_VTYP, + kFormType_MATT, + kFormType_IPCT, + kFormType_IPDS, + kFormType_ARMA, + kFormType_ECZN, + kFormType_LCTN, + kFormType_MESG, + kFormType_RGDL, + kFormType_DOBJ, + kFormType_DFOB, + kFormType_LGTM, + kFormType_MUSC, + kFormType_FSTP, + kFormType_FSTS, + kFormType_SMBN, + kFormType_SMQN, + kFormType_SMEN, + kFormType_DLBR, + kFormType_MUST, + kFormType_DLVW, + kFormType_WOOP, + kFormType_SHOU, + kFormType_EQUP, + kFormType_RELA, + kFormType_SCEN, + kFormType_ASTP, + kFormType_OTFT, + kFormType_ARTO, + kFormType_MATO, + kFormType_MOVT, + kFormType_SNDR, + kFormType_DUAL, + kFormType_SNCT, + kFormType_SOPM, + kFormType_COLL, + kFormType_CLFM, + kFormType_REVB, + kFormType_PKIN, + kFormType_RFGP, + kFormType_AMDL, + kFormType_LAYR, + kFormType_COBJ, + kFormType_OMOD, + kFormType_MSWP, + kFormType_ZOOM, + kFormType_INNR, + kFormType_KSSM, + kFormType_AECH, + kFormType_SCCO, + kFormType_AORU, + kFormType_SCSN, + kFormType_STAG, + kFormType_NOCM, + kFormType_LENS, + kFormType_LSPR, + kFormType_GDRY, + kFormType_OVIS, + kFormType_Max, + + // These are special types for Papyrus + kFormType_Alias, + kFormType_ReferenceAlias, + kFormType_LocationAlias, + kFormType_ActiveMagicEffect, + kFormType_InputEnableLayer, + kFormType_RefCollectionAlias, + kFormType_ScriptObject = -1, +}; + +// 20 +class TESForm : public BaseFormComponent +{ +public: + enum { kTypeID = 0 }; // special-case + + virtual void Unk_07(); + virtual void Unk_08(); + virtual void Unk_09(); + virtual void Unk_0A(); + virtual void Unk_0B(); + virtual void Unk_0C(); + virtual bool MarkChanged(UInt64 changed); + virtual void Unk_0E(); + virtual void Unk_0F(); + virtual void Unk_10(); + virtual void Unk_11(); // Serialize + virtual void Unk_12(); + virtual void Unk_13(); + virtual void Unk_14(); + virtual void Unk_15(); + virtual void Unk_16(); + virtual ModInfo* GetLastModifiedMod(); // 17 - Returns the ModInfo* of the mod that last modified the form. + virtual ModInfo* GetLastModifiedMod_2(); // 18 - Returns the ModInfo* of the mod that last modified the form. Calls a helper function to do so. + virtual UInt8 GetFormType(); // 19 + virtual void Unk_1A(); // 1A - GetDebugName(TESForm * form, char * destBuffer, unsigned int bufferSize); + virtual bool GetPlayerKnows(); // 1B - Gets flag bit 6. + virtual void Unk_1C(); + virtual void Unk_1D(); + virtual void Unk_1E(); + virtual void Unk_1F(); + virtual void Unk_20(); + virtual void Unk_21(); + virtual void Unk_22(); + virtual void Unk_23(); + virtual void Unk_24(); + virtual void Unk_25(); + virtual void Unk_26(); + virtual void Unk_27(); + virtual void Unk_28(); + virtual void Unk_29(); + virtual void Unk_2A(); + virtual void Unk_2B(); + virtual void Unk_2C(); + virtual void Unk_2D(); + virtual void Unk_2E(); + virtual void Unk_2F(); + virtual void Unk_30(); + virtual void Unk_31(); + virtual void Unk_32(); + virtual void Unk_33(); + virtual void Unk_34(); + virtual const char * GetFullName(); // 35 + virtual void Unk_36(); + virtual void Unk_37(); + virtual void Unk_38(); + virtual void Unk_39(); + virtual const char * GetEditorID(); // Only returns string for things that actually store the EDID ingame + virtual void Unk_3B(); + virtual void Unk_3C(); + virtual void Unk_3D(); + virtual void Unk_3E(); + virtual void Unk_3F(); + virtual void Unk_40(); + virtual void Unk_41(); + virtual void Unk_42(); + virtual void Unk_43(); + virtual void Unk_44(); + virtual void Unk_45(); + virtual void Unk_46(); + virtual void Unk_47(); + + enum { + kFlag_IsDeleted = 1 << 5, + kFlag_PlayerKnows = 1 << 6, + kFlag_Persistent = 1 << 10, + kFlag_IsDisabled = 1 << 11, + kFlag_NoHavok = 1 << 29, + }; + + struct Data + { + ModInfo ** entries; // array of ModInfo* - mods that change this form. + UInt64 size; + }; + + Data * unk08; // 08 + UInt32 flags; // 10 + UInt32 formID; // 14 + UInt16 unk18; // 18 + UInt8 formType; // 1A + UInt8 unk1B; // 1B + UInt32 pad1C; // 1C +}; + +// 38 +class BGSDefaultObject : public TESForm +{ +public: + enum { kTypeID = kFormType_DFOB }; + + TESForm * form; // 20 + UInt64 unk28; // 28 + BSFixedString editorId; // 30 +}; + +// 118 +class BGSInstanceNamingRules : public TESForm +{ +public: + // 30 + struct KeywordData + { + void * unk00; // 00 + BGSKeywordForm keywordForm; // 08 + UInt64 unk28; // 28 + }; + UInt64 unk20; // 20 + tArray unk28; // 28 + tArray unk40; // 40 + tArray unk58; // 58 + tArray unk70; // 70 + tArray unk88; // 88 + UInt64 unkA0[(0x118 - 0xA0) / 8]; // A0 +}; +STATIC_ASSERT(offsetof(BGSInstanceNamingRules, unk88) == 0x88); +STATIC_ASSERT(sizeof(BGSInstanceNamingRules) == 0x118); + +class BGSKeyword : public TESForm +{ +public: + enum { kTypeID = kFormType_KYWD }; + + BSFixedString keyword; // 020 +}; + +// 30 +class BGSSoundDescriptorForm : public TESForm +{ +public: + enum { kTypeID = kFormType_SNDR }; + + BGSSoundDescriptor soundDescriptor; // 20 + UInt64 unk28; // 28 +}; + +class BGSAction : public BGSKeyword +{ +public: + enum { kTypeID = kFormType_AACT }; + + UInt64 unk28; // maybe index or id? +//Actions dump 1.1.30: +// unk28 Keyword +// ----- -------- +// 0 ActionTrick +// 1 ActionFireEmpty +// 2 ActionWeaponHotkey +// 3 ActionFlinchStart +// 4 ActionPerkSandman +// 5 ActionPerkCannibal +// 6 ActionGunAlert +// 7 ActionHide +// 8 ActionCameraBToCameraA +// 9 ActionCameraAToCameraB +// 10 ActionSwimStop +// 11 ActionSwimStart +// 12 ActionAvailableCondition1Heal +// 13 ActionRightArmHeal +// 14 ActionLeftArmHeal +// 15 ActionInstantAttackReset +// 16 ActionJetpackStop +// 17 ActionJetpackStart +// 18 ActionUnconsciousExit +// 19 ActionUnconsciousEnter +// 20 ActionFireChargeHold +// 21 ActionCustomLaughing +// 22 ActionPropellersOn +// 23 ActionPropellersOff +// 24 ActionCustomBooing +// 25 ActionCustomCheering +// 26 ActionGunReady +// 27 ActionDeferredKill +// 28 ActionNonSupportContact +// 29 ActionBark +// 30 ActionEnterDialogueCameraState +// 31 ActionGunChargeStart +// 32 ActionIdlePlayful +// 33 ActionChangeAnimFlavor +// 34 ActionChangeAnimArchetype +// 35 ActionBoltCharge +// 36 ActionAttackMissed +// 37 ActionTunnel +// 38 ActionInteractionExitAlt +// 39 ActionLeadingArrival +// 40 ActionLeadingArrivalEmote +// 41 ActionLeadingDoneEmote +// 42 ActionLeadingDeparture +// 43 ActionEscortWait +// 44 ActionIdleFlavor +// 45 ActionInstantInitializeGraphToBaseState +// 46 ActionLegsHeal +// 47 ActionPanic +// 48 ActionCower +// 49 ActionAOEAttack +// 50 ActionPipboyInspect +// 51 ActionShuffle +// 52 ActionDodge +// 53 ActionEvade +// 54 ActionStumbleStart +// 55 ActionGunRelaxed +// 56 ActionTurnStop +// 57 ActionTurnLeft +// 58 ActionTurnRight +// 59 ActionMoveStop +// 60 ActionMoveStart +// 61 ActionLand +// 62 ActionFall +// 63 ActionLargeMovementDelta +// 64 ActionDeathWait +// 65 ActionDualRelease +// 66 ActionDualAttack +// 67 ActionStopEffect +// 68 ActionListen +// 69 ActionTalking +// 70 ActionDeath +// 71 ActionSummonedStart +// 72 ActionBumpedInto +// 73 ActionHoverStop +// 74 ActionHoverStart +// 75 ActionFlyStop +// 76 ActionFlyStart +// 77 ActionSprintStart +// 78 ActionPathEnd +// 79 ActionPathStart +// 80 ActionForceEquip +// 81 ActionBlockAnticipate +// 82 ActionIdleStop +// 83 ActionBleedoutStop +// 84 ActionBleedoutStart +// 85 ActionRecoilLarge +// 86 ActionRecoil +// 87 ActionBlockHit +// 88 ActionStaggerStart +// 89 ActionVoiceInterrupt +// 90 ActionVoiceReady +// 91 ActionVoiceRelease +// 92 ActionRightInterrupt +// 93 ActionRightReady +// 94 ActionRightRelease +// 95 ActionLeftInterrupt +// 96 ActionLeftReady +// 97 ActionLeftRelease +// 98 ActionRightPowerAttack +// 99 ActionDraw +//100 ActionSprintStop +//101 ActionLook +//102 ActionActivate +//103 ActionVoice +//104 ActionSneak +//105 ActionJump +//106 ActionRightAttack +//107 ActionLeftAttack +//108 ActionSwimStateChange +//109 ActionIdle +//110 ActionWardHit +//111 ActionPipboyTab +//112 ActionPipboyMap +//113 ActionPipboyInventory +//114 ActionPipboyData +//115 ActionPipboyStats +//116 ActionPipboyZoom +//117 ActionExitCover +//118 ActionEnterCover +//119 ActionPipboyOpen +//120 ActionThrow +//121 ActionFireAuto +//122 ActionFireCharge +//123 ActionFireSingle +//124 ActionMelee +//125 ActionSightedRelease +//126 ActionSighted +//127 ActionReload +//128 ActionRagdollInstant +//129 ActionIdleStopInstant +//130 ActionSheath +//131 ActionGetUp +//132 ActionKnockDown +//133 ActionShieldChange +//134 ActionMoveRight +//135 ActionMoveLeft +//136 ActionMoveBackward +//137 ActionMoveForward +//138 ActionDualPowerAttack +//139 ActionLeftPowerAttack +//140 ActionIdleWarn +//141 ActionGunDown +//142 ActionListenNeutral +//143 ActionListenQuestion +//144 ActionListenNegative +//145 ActionListenPositive +//146 ActionIntimidate +//147 ActionInteractionEnter +//148 ActionInteractionEnterInstant +//149 ActionInteractionExit +//150 ActionInteractionExitQuick +//151 ActionPipboyClose +//152 ActionPipboyRadioOff +//153 ActionPipboyRadioOn +//154 ActionPipboySelect +//155 ActionPipboyTabPrevious +//156 ActionCoverSprintStart +//157 ActionActivateLoopingEnd +//158 ActionMantle +//159 ActionLeftSyncAttack +//160 ActionRightSyncAttack +//161 ActionFlipThrow +//162 ActionDialogueExit +//163 ActionDialogueEnter +//164 ActionPipboyLoadHolotape +//165 ActionLimbCritical +//166 ActionCombatExit +//167 ActionCombatEnter +//168 ActionActivateLoopingBegin +//169 ActionFurnitureFull +//170 ActionInitializeGraphToBaseState +//171 ActionFurnitureNoLongerFull +//172 ActionLegsCritical +}; + +// 190 +class ActorValueInfo : public TESForm +{ +public: + enum { kTypeID = kFormType_AVIF }; + + TESFullName fullName; // 20 + TESDescription description; // 30 + +#if _MSC_VER == 1700 + std::function calcFunction; // 48 +#else + void * func_vtable; // 48 - vtable of the lambda function + void * func; // 50 + UInt64 unk58; // 58 + void * func_ptr; // 60 - address of offset 48 +#endif + + const char * avName; // 68 + UInt64 unk70; // 70 + ActorValueInfo * dependentAVs[0xF]; // 78 + +#if _MSC_VER == 1700 + std::function derivedFunction; // F0 +#else + void * derived_func_vtable; // F0 - vtable of the lambda function + void * derived_func; // F8 + UInt64 unk100; // 100 + void * derived_func_ptr; // 108 - address of offset F0 +#endif + + UInt32 unk110[(0x16C - 0x110) / 4]; // 110 + + UInt32 avFlags; // 16C + enum AVFlags + { + kFlag_DefaultBase_0 = (1 << 10), // 10 | Default Base: 0 + kFlag_DefaultBase_1 = (1 << 11), // 11 | Default Base: 1 + kFlag_DefaultBase_100 = (1 << 12), // 12 | Default Base: 100 + kFlag_DefaultBase_UserDefined = (1 << 13), // 13 | Default Base: User Defined + kFlag_DefaultBase_Derived = (1 << 15), // 15 | Default Base: Derived (bits 10-13 must not be set) + kFlag_DamageIsPositive = (1 << 26), // 26 | Damage is Positive + kFlag_GodModeImmune = (1 << 27), // 27 | God Mode Immune + kFlag_Hardcoded = (1 << 31) // 31 | Hardcoded + }; + + UInt32 unk170; // 170 + UInt32 numDependentAVs; // 174 + UInt32 unk178; // 178 + UInt32 unk17C; // 17C + UInt32 unk180; // 180 + float defaultBase; // 184 + UInt32 unk188; // 188 + UInt32 unk18C; // 18C +}; +STATIC_ASSERT(offsetof(ActorValueInfo, avName) == 0x68); + +// 80 +class BGSMaterialType : public TESForm +{ +public: + enum { kTypeID = kFormType_MATT }; + + UInt64 unk20[(0x80-0x20)/8]; // 20 +}; + +// 38 +class BGSColorForm : public TESForm +{ +public: + enum { kTypeID = kFormType_CLFM }; + + TESFullName fullName; // 20 + union + { + struct + { + UInt8 r; + UInt8 g; + UInt8 b; + UInt8 unknown; + } channels; + UInt32 rgb; + float remappingIndex; + } color; // 30 + UInt32 unk34; // 34 + + struct Data + { + Data * next; // 00 + float unk08; // 08 + float unk0C; // 0C + float unk10; // 10 + SInt32 unk14; // 14 - 0xFFFFFFFF + UInt32 unk18; // 18 + UInt32 unk1C; // 1C + }; + + Data * unk38; // 38 + enum Flags + { + kFlags_Playable = 1, + kFlags_RemappableIndex = 2, + kFlags_ExtendedLUT = 4 + }; + UInt32 flags; // 40 + UInt32 unk44; // 44 +}; + +// 6C8 +class TESRace : public TESForm +{ +public: + enum { kTypeID = kFormType_RACE }; + + TESFullName fullName; // 20 + TESDescription description; // 30 + TESSpellList spellList; // 48 + BGSSkinForm skinForm; // 58 + BGSBipedObjectForm bipedObjects; // 68 + BGSKeywordForm keywordForm; // 78 + BGSAttackDataForm attackDataForm; // 98 + BGSPropertySheet propertySheet; // A8 + BGSPreloadable preloadable; // B8 + + TESModel models[2]; // C0 + TESModel unk120[2]; // 120 + UInt64 unk170[(0x288-0x180)/8]; // 180 + BGSTextureModel textureModel[2]; // 288 + BGSBehaviorGraphModel behaviorGraph[2]; // 2E8 + BSFixedString unk348[2]; // 348 + BSFixedString unk358[2]; // 358 + BGSVoiceType * voiceType[2]; // 368 + BGSBodyPartData * bodyPartData; // 378 + BGSSoundTagComponent unk380; // 380 + BGSAttachParentArray unk388; // 388 + BSFixedString editorId; // 3A0 + BGSMaterialType * materialType; // 3A8 + void * unk3B0; // 3B0 + BGSTextureSet * textureSets[2]; // 3B8 + BGSSoundDescriptorForm * soundDescriptors[2]; // 3C8 + BSFixedString bipedObjectNames[0x20]; // 3D8 + + // 10 + template + struct BoneScale // (BSMB/BSMS) pair + { + BSFixedString bone; // 00 + float * value; // 08 + + bool operator==(const BSFixedString a_key) const { return bone.data == a_key.data; } + static inline UInt32 GetHash(const BSFixedString * key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)key->data->Get(), 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tkey: %s", bone.data ? bone.data->Get() : ""); + for(UInt32 i = 0; i < T; i++) + _MESSAGE("\t\tdata: %f", value[i]); + } + }; + + struct MorphSlider + { + UInt32 key; // 00 + UInt32 pad04; // 04 + + struct Morphs + { + BSFixedString lower; // 00 + BSFixedString upper; // 08 + }; + Morphs * morphs; // 08 + + bool operator==(const UInt32 a_key) const { return key == a_key; } + static inline UInt32 GetHash(const UInt32 * key) + { + UInt32 hash; + CalculateCRC32_32(&hash, *key, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tkey: %08X", key); + if(morphs) + { + _MESSAGE("\t\tLower: %s", morphs->lower.data->Get()); + _MESSAGE("\t\tUpper: %s", morphs->upper.data->Get()); + } + } + }; + + struct BoneScaleMap + { + tHashSet, BSFixedString> weightMap1; // value array length 8 + tHashSet, BSFixedString> weightMap2; // value array length 4 + }; + + UInt64 unk4C0[(0x648-0x4D8)/8]; // 4D8 - 654, 670, 660 table? + tHashSet morphSliders; // 648 + UInt64 unk678[(0x698-0x678)/8]; // 678 + CharacterCreation::CharGenData * chargenData[2]; // 698 + BoneScaleMap * boneScaleMap[2]; // 6A8 + TESTexture hairColorLUT; // 6B8 +}; + +STATIC_ASSERT(offsetof(TESRace, chargenData) == 0x698); +STATIC_ASSERT(sizeof(TESRace) == 0x6C8); + +// 48 +class BGSListForm : public TESForm +{ +public: + enum { kTypeID = kFormType_FLST }; + + tArray forms; // 20 + tArray* tempForms; // 38 + UInt32 scriptAdded; // 40 - Amount on the end of the tArray that is script-added + UInt32 unk44; // 44 +}; +STATIC_ASSERT(sizeof(BGSListForm) == 0x48); + +// 178 +class BGSHeadPart : public TESForm +{ +public: + enum { kTypeID = kFormType_HDPT }; + + TESFullName fullName; // 20 + BGSModelMaterialSwap materialSwap; // 30 + + enum + { + kFlagPlayable = 1 << 0, + kFlagMale = 1 << 1, + kFlagFemale = 1 << 2, + kFlagExtraPart = 1 << 3, + kFlagSolidTint = 1 << 4, + kFlagUnk1 = 1 << 5 // only appears on Head Rear types + }; + UInt32 partFlags; // 70 + + enum { + kTypeMisc = 0, + kTypeFace, + kTypeEyes, + kTypeHair, + kTypeFacialHair, + kTypeScar, + kTypeBrows, + kType7, + kType8, + kTypeHeadRear, + kNumTypes + }; + UInt32 type; // 74 + + tArray extraParts; // 78 + BGSTextureSet * textureSet; // 90 + TESModel model; // 98 + TESModelTri morphs[3]; // C8 + UInt64 unk158; // 158 + BGSListForm * validRaces; // 160 + Condition * conditions; // 168 - Condition most likely + BSFixedString partName; // 170 + + bool IsExtraPart() { return (partFlags & kFlagExtraPart) == kFlagExtraPart; } +}; + +// 1B0 +class EffectSetting : public TESForm +{ +public: + enum { kTypeID = kFormType_MGEF }; + + TESFullName fullName; // 20 + BGSMenuDisplayObject menuObject; // 30 + BGSKeywordForm keywordForm; // 40 + UInt64 unk060[2]; // 60 + UInt32 unk070; // 70 + float unk074; + TESForm* unk078; // primary object? (SpellItem, TESObjectLIGH, BGSDamageType, BGSHazard) + UInt64 unk080; + ActorValueInfo* actorValInfo88; // 088 + UInt8 unk090; + UInt8 pad092[3]; + TESObjectLIGH* light98; + float unkA0; + UInt32 padA4; + TESEffectShader* effectShaderA8; + UInt64 unkB0; + UInt64 unkB8; + float unkC0; + float unkC4; + float unkC8; + float unkCC; + UInt32 unk0D0; + UInt32 pad0D4; + ActorValueInfo* actorValInfoD8; + BGSProjectile* projectileE0; + BGSExplosion* explosionE8; + UInt32 unkF0; // init to 3 cast type? + UInt32 unkF4; // init to 5 delivery type? + ActorValueInfo* actorValInfoF8; + UInt64 unk100; + BGSArtObject* art108; + BGSImpactDataSet* impact110; + UInt32 unk118; + UInt32 pad11C; + UInt64 unk120; + float unk128; // initialized to 3F800000 dual casting scale? + UInt32 pad12C; + UInt64 unk130[4]; + TESImageSpaceModifier* spaceModifier150; // 150 + BGSPerk* perk158; + UInt64 unk160; // initialized to 1 + UInt64 unk168[3]; + UInt64 unk180; + UInt32 unk188; + UInt32 unk18C; + UInt64 unk190; + void* unk198; // pointer to something + UInt64 unk1A0; + void* unk1A8; +}; +STATIC_ASSERT(offsetof(EffectSetting, unk160) == 0x160); +STATIC_ASSERT(sizeof(EffectSetting) == 0x1B0); + +// 50 +// MSWP +class BGSMaterialSwap : public TESForm +{ +public: + enum { kTypeID = kFormType_MSWP }; + + struct MaterialSwap + { + BSFixedString source; // 00 + BSFixedString target; // 08 + float colorRemapIndex; // 10 + UInt32 unk14; // 14 + + operator BSFixedString() const { return source; } + bool operator==(const BSFixedString a_name) const { return source == a_name; } + static inline UInt32 GetHash(BSFixedString * key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)key->data, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tname: %s", source.c_str()); + _MESSAGE("\t\ttarget: %s", target.c_str()); + _MESSAGE("\t\tremapIndex: %f", colorRemapIndex); + } + }; + + tHashSet materialSwaps; // 20 +}; +STATIC_ASSERT(sizeof(BGSMaterialSwap) == 0x50); + +class BGSMod +{ +public: + class Container + { + public: + + // Used by Papyrus code to verify internal type + // i.e. can't assign an Armor property to a Weapon OMOD + enum + { + kWeaponTarget_Offset = 0x000, + kArmorTarget_Offset = 0x100, + kActorTarget_Offset = 0x200 + }; + + // Used by Papyrus to specify the particular operator + enum + { + kOperator_Set = 0, + kOperator_Add = 1, + kOperator_Mult_Add = 2, + kOperator_And = 3, + kOperator_Or = 4, + kOperator_Rem = 5 + }; + + enum WeaponFormProperty + { + kWeaponTarget_bAlternateRumble = 22, + kWeaponTarget_bAutomatic = 25, + kWeaponTarget_bBoltAction = 81, + kWeaponTarget_bCantDrop = 26, + kWeaponTarget_bChargingAttack = 93, + kWeaponTarget_bChargingReload = 18, + kWeaponTarget_bDisableShells = 92, + kWeaponTarget_bEffectOnDeath = 21, + kWeaponTarget_bFixedRange = 20, + kWeaponTarget_bHasScope = 48, + kWeaponTarget_bHoldInputToPower = 85, + kWeaponTarget_bIgnoreResist = 24, + kWeaponTarget_bMinorCrime = 19, + kWeaponTarget_bNonHostile = 23, + kWeaponTarget_bNonPlayable = 27, + kWeaponTarget_bNPCsUseAmmo = 17, + kWeaponTarget_bPlayerOnly = 16, + kWeaponTarget_bRepeatableSingleFire = 86, + kWeaponTarget_ehHitBehavior = 9, + kWeaponTarget_elSoundLevel = 59, + kWeaponTarget_eoZoomDataOverlay = 68, + kWeaponTarget_esStaggerValue = 82, + kWeaponTarget_ewType = 15, + kWeaponTarget_cAccuracyBonus = 78, + kWeaponTarget_fAimModelBaseStability = 66, + kWeaponTarget_fAimModelConeDecreasePerSec = 36, + kWeaponTarget_fAimModelConeIncreasePerShot = 35, + kWeaponTarget_fAimModelConeIronSightsMultiplier = 47, + kWeaponTarget_fAimModelConeSneakMultiplier = 38, + kWeaponTarget_fAimModelMaxConeDegrees = 34, + kWeaponTarget_fAimModelMinConeDegrees = 33, + kWeaponTarget_fAimModelRecoilArgDeg = 45, + kWeaponTarget_fAimModelRecoilArgRotateDeg = 46, + kWeaponTarget_fAimModelRecoilDiminishSightsMult = 40, + kWeaponTarget_fAimModelRecoilDiminishSpringForce = 39, + kWeaponTarget_fAimModelRecoilHipMult = 43, + kWeaponTarget_fAimModelRecoilMaxDegPerShot = 41, + kWeaponTarget_fAimModelRecoilMinDegPerShot = 42, + kWeaponTarget_fAttackActionPointCost = 79, + kWeaponTarget_fAttackDelaySec = 4, + kWeaponTarget_fColorRemappingIndex = 88, + kWeaponTarget_fCriticalChargeBonus = 8, + kWeaponTarget_fCriticalDamageMult = 90, + kWeaponTarget_fFireSeconds = 50, + kWeaponTarget_fFullPowerSeconds = 84, + kWeaponTarget_fMaxRange = 3, + kWeaponTarget_fMinPowerPerShot = 87, + kWeaponTarget_fMinRange = 2, + kWeaponTarget_fOutOfRangeDamageMult = 6, + kWeaponTarget_fReach = 1, + kWeaponTarget_fReloadSpeed = 76, + kWeaponTarget_fSecondaryDamage = 7, + kWeaponTarget_fSightedTransitionSeconds = 83, + kWeaponTarget_fSoundLevelMult = 74, + kWeaponTarget_fSpeed = 0, + kWeaponTarget_fUnused = 5, + kWeaponTarget_fWeight = 30, + kWeaponTarget_fZoomDataCameraOffsetX = 70, + kWeaponTarget_fZoomDataCameraOffsetY = 71, + kWeaponTarget_fZoomDataCameraOffsetZ = 72, + kWeaponTarget_iAmmoCapacity = 12, + kWeaponTarget_iAttackDamage = 28, + kWeaponTarget_iRank = 10, + kWeaponTarget_iValue = 29, + kWeaponTarget_paAimModel = 32, + kWeaponTarget_peEnchantments = 65, + kWeaponTarget_pgZoomDataImageSpace = 69, + kWeaponTarget_piBashImpactDataSet = 63, + kWeaponTarget_piImpactDataSet = 60, + kWeaponTarget_pkKeywords = 31, + kWeaponTarget_plCritEffect = 62, + kWeaponTarget_pmBlockMaterial = 64, + kWeaponTarget_pnNPCAmmoList = 75, + kWeaponTarget_poAmmo = 61, + kWeaponTarget_ppOverrideProjectile = 80, + kWeaponTarget_psAttackFailSound = 55, + kWeaponTarget_psAttackLoop = 54, + kWeaponTarget_psAttackSound = 52, + kWeaponTarget_psAttackSound2D = 53, + kWeaponTarget_psEquipSound = 57, + kWeaponTarget_psFastEquipSound = 91, + kWeaponTarget_psIdleSound = 56, + kWeaponTarget_psUnEquipSound = 58, + kWeaponTarget_ptEquipSlot = 73, + kWeaponTarget_pwMaterialSwaps = 89, + kWeaponTarget_pzZoomData = 67, + kWeaponTarget_uAimModelConeDecreaseDelayMs = 37, + kWeaponTarget_uAimModelRecoilShotsForRunaway = 44, + kWeaponTarget_uNumProjectiles = 51, + kWeaponTarget_vaActorValues = 94, + kWeaponTarget_vdDamageTypeValues = 77 + }; + + enum ArmorFormProperty + { + kArmorTarget_ebBodyPart = 8, + kArmorTarget_fColorRemappingIndex = 12, + kArmorTarget_fWeight = 4, + kArmorTarget_iAddonIndex = 7, + kArmorTarget_iHealth = 11, + kArmorTarget_iRating = 6, + kArmorTarget_iValue = 5, + kArmorTarget_peEnchantments = 0, + kArmorTarget_piBashImpactDataSet = 1, + kArmorTarget_pkKeywords = 3, + kArmorTarget_pmBlockMaterial = 2, + kArmorTarget_pwMaterialSwaps = 13, + kArmorTarget_vaActorValues = 10, + kArmorTarget_vdDamageTypeValues = 9 + }; + + enum NPCFormProperty + { + kActorTarget_fColorRemappingIndex = 4, + kActorTarget_iXPOffset = 2, + kActorTarget_peEnchantments = 3, + kActorTarget_piForcedInventory = 1, + kActorTarget_pkKeywords = 0, + kActorTarget_pwMaterialSwaps = 5 + }; + + // 10 + struct Data + { + union + { + struct + { + float v1; + float v2; + } f; + + struct + { + UInt32 v1; + UInt32 v2; + } i; + + struct + { + UInt32 formId; + float v2; + } ff; + + struct + { + UInt32 formId; + UInt32 v2; + } fi; + + TESForm * form; + } value; // 00 + + UInt8 target; // 08 - ArmorFormProperty/NPCFormProperty/WeaponFormProperty + + enum + { + kOpFlag_Mod = 0x02, + kOpFlag_Mul = 0x08, + kOpFlag_Add = 0x10, + kOpFlag_Set = 0x20, + kOpFlag_Dual = 0x40, + kOpFlag_Form = 0x80, + + kOpFlag_Set_Bool = 0, + kOpFlag_Or_Bool = (kOpFlag_Add | kOpFlag_Dual), + kOpFlag_And_Bool = (kOpFlag_Mul | kOpFlag_Dual), + + kOpFlag_Set_Enum = (kOpFlag_Set | kOpFlag_Form), + + kOpFlag_Set_Int = kOpFlag_Dual, + kOpFlag_Add_Int = kOpFlag_Add, + kOpFlag_Mul_Add_Int = (kOpFlag_Mul | kOpFlag_Set), // Same as Float version + + kOpFlag_Set_Form = kOpFlag_Form, + kOpFlag_Add_Form = (kOpFlag_Add | kOpFlag_Form), + kOpFlag_Rem_Form = (kOpFlag_Mul | kOpFlag_Form), + + kOpFlag_Set_Float = kOpFlag_Set, + kOpFlag_Add_Float = (kOpFlag_Add | kOpFlag_Set), + kOpFlag_Mul_Add_Float = (kOpFlag_Mul | kOpFlag_Set), + + kOpFlag_Set_Form_Float = (kOpFlag_Dual | kOpFlag_Form), + kOpFlag_Add_Form_Float = (kOpFlag_Add | kOpFlag_Dual | kOpFlag_Form), + kOpFlag_Mul_Add_Form_Float = (kOpFlag_Mul | kOpFlag_Dual | kOpFlag_Form) + }; + + UInt8 op; // 09 + UInt16 unk0A; // 0A + UInt32 unk0C; // 0C + }; + Data * data; // 00 + UInt32 dataSize; // 08 - dataSize/sizeof(Data) = count + }; + + class Template + { + public: + // 20 + class Items : public BaseFormComponent + { + public: + virtual ~Items(); + + virtual void Unk_07(void); + virtual void Unk_08(void); + + void * unk08; // 08 + void * unk10; // 10 + void * unk18; // 18 + }; + + // 38 + class Item : public TESFullName + { + public: + virtual ~Item(); + + virtual void Unk_07(void); + virtual void Unk_08(void); + + void * unk08; // 08 + void * unk10; // 10 + UInt32 unk18; // 18 + UInt32 unk1C; // 1C + void * unk20; // 20 + UInt64 unk28; // 28 + UInt32 unk30; // 30 + UInt8 unk34[4]; // 34 + }; + }; + + class Attachment { + public: + // C8 + class Mod : public TESForm + { + public: + enum { kTypeID = kFormType_OMOD }; + + TESFullName fullName; // 20 + TESDescription description; // 30 + BGSModelMaterialSwap materialSwap; // 48 + BGSMod::Container modContainer; // 88 + BGSAttachParentArray unk98; // 98 + UInt64 unkB0; // B0 + UInt64 unkB8; // B8 + UInt16 unkC0; // C0 + + enum + { + kTargetType_None = 0, + kTargetType_Armor = 0x1D, + kTargetType_Furniture = 0x2A, + kTargetType_Weapon = 0x2B, + kTargetType_Actor = 0x2D + }; + + UInt8 targetType; // C2 + UInt8 maxRank; // C3 + UInt8 scaledOffset; // C4 + UInt8 priority; // C5 + UInt8 unkC6; // C6 + UInt8 unkC7; // C7 + }; + }; +}; +STATIC_ASSERT(offsetof(BGSMod::Attachment::Mod, modContainer) == 0x88); +STATIC_ASSERT(sizeof(BGSMod::Attachment::Mod) == 0xC8); + +// 48 +class BGSTransform : public TESForm +{ +public: + enum { kTypeID = kFormType_TRNS }; + + float matrix[9]; + UInt32 pad44; +}; +STATIC_ASSERT(offsetof(BGSTransform, pad44) == 0x44); +STATIC_ASSERT(sizeof(BGSTransform) == 0x48); + +class BGSOpenCloseForm +{ +public: + // void ** _vtbl; // 00 +}; + +class TESMagicTargetForm +{ +public: +}; + +class TESMagicCasterForm +{ +public: +}; + + +class BGSLocationRefType : public BGSKeyword +{ +public: + enum { kTypeID = kFormType_LCRT }; +}; + + +// 140 +class BGSLocation : public TESForm +{ +public: + enum { kTypeID = kFormType_LCTN }; + + TESFullName fullName; // 020 + BGSKeywordForm keywordForm; // 030 + + BGSLocation* parent; // 050 + UInt64 unk58; // 058 + BGSMusicType* musicType; // 060 + BGSEncounterZone* encounterZone; // 068 + UInt32 unk70; // 070 + float worldRadius; // 074 + float unk78; // init to 1.0 + UInt32 unk7C; + + struct CellStaticReference { + BGSLocationRefType* refType; // 00 + UInt32 markerForm; // 08 + UInt32 locationForm; // 0C + SInt16 gridY; // 10 + SInt16 gridX; // 12 + UInt32 pad10; // 14 + }; + tArray staticReferences; + UnkArray arr98; + UInt64 unkB0; + UInt64 unkB8; + UInt32 unkC0; + UInt32 unkC4; + UInt32 unkC8; + UInt32 unkCC; + UInt64 unkD0; + UInt32 unkD8; + float unkDC; + UInt32 unkE0; + UInt32 unkE4; + UInt64 unkE8; + UInt64 unkF0; + UInt32 unkF8; + float unkFC; + UInt32 unk100; + UInt32 unk104; + UInt32 unk108; + float unk10C; + UInt64 unk110; + UInt32 unk118; + float unk11C; + UInt32 unk120; + UInt32 unk124; + UInt32 unk128; + UInt32 unk12C; + UInt32 unk130; + UInt32 unk134; + UInt32 unk138; + UInt32 unk13C; +}; +STATIC_ASSERT(offsetof(BGSLocation, unkFC) == 0xFC); +STATIC_ASSERT(sizeof(BGSLocation) == 0x140); + +// F0 +class TESObjectCELL : public TESForm +{ +public: + enum { kTypeID = kFormType_CELL }; + + // parents + TESFullName fullName; // 20 + + UInt64 unk30; // 30 + UInt64 unk38; // 38 + + enum + { + kFlag_IsInterior = 1, + kFlag_HasWater = 2 // Water is implied for Exteriors, this bit is set when it has an override + }; + + UInt16 flags; // 40 + UInt16 unk42; // 42 + UInt32 unk44; // 44 + ExtraDataList * extraDataList; // 48 + + struct MaxHeightData + { + UInt32 unk00; // 00 + UInt32 unk04; // 04 + UInt8 * maxHeightBlock; // 08 + }; + + MaxHeightData * unk50; // 50 + TESObjectLAND * land; // 58 + SInt32 unk60; // 60 + float unk64; // 64 + tArray * navMeshes; // 68 + tArray objectList; // 70 + UInt64 unk88; // 88 + tHashSet unk90; // 90 + void * unkC0; // C0 + TESWorldSpace * worldSpace; // C8 + UInt64 unkD0; // D0 + UInt64 unkD8; // D8 + UInt64 unkE0; // E0 + UInt32 preVisCell; // E8 - FormID + UInt32 unkEC; // EC + + MEMBER_FN_PREFIX(TESObjectCELL); + DEFINE_MEMBER_FN(GetHavokWorld, bhkWorld*, 0x003B49A0); +}; +STATIC_ASSERT(offsetof(TESObjectCELL, objectList) == 0x70); +STATIC_ASSERT(offsetof(TESObjectCELL, worldSpace) == 0xC8); +STATIC_ASSERT(sizeof(TESObjectCELL) == 0xF0); + +// 48 +class BGSEncounterZone : public TESForm +{ +public: + enum { kTypeID = kFormType_ECZN }; + + enum + { + kFlag_NeverResets = 1, + kFlag_Workshop = 8 + }; + + UInt64 unk20; // 20 + BGSLocation* location; // 28 + SInt8 rank; // 30 + UInt8 minLevel; // 30 + UInt8 flags; // 32 + UInt8 maxLevel; // 33 + UInt32 unk34; // 34 + UInt64 unk38; // 38 + UInt64 unk40; // 40 +}; + +// 38 +class BGSOutfit : public TESForm +{ +public: + enum { kTypeID = kFormType_OTFT }; + + tArray forms; // 20 +}; + +// 30 +class BGSDamageType : public TESForm +{ +public: + enum { kTypeID = kFormType_DMGT }; + + UInt64 unk20; + UInt64 unk28; +}; + +// 48 +class BGSEquipSlot : public TESForm +{ +public: + enum { kTypeID = kFormType_EQUP }; + + tArray parentSlots; // 20 + UInt64 unk38; + UInt64 unk40; +}; + +// 228 +class TESWaterForm : public TESForm +{ +public: + enum { kTypeID = kFormType_WATR }; + + // parents + TESFullName fullName; // 20 + + UInt64 unk30[(0x68 - 0x30) >> 3]; // 30 + + TESTexture layer1Noise; // 68 + TESTexture layer2Noise; // 78 + TESTexture layer3Noise; // 88 + + UInt32 opacity; // 98 + UInt32 unk9C; // 9C + BGSMaterialType * material; // A0 + BGSSoundDescriptorForm * openSound; // A8 + + // C8 + struct VisualData + { + float depthAmount; // 00 + union Color + { + UInt32 bgr; + struct + { + UInt8 red; + UInt8 green; + UInt8 blue; + UInt8 unused; + } color; + }; + Color shallowColor; // 04 + Color deepColor; // 08 + float shallowColorRange; // 0C + float deepColorRange; // 10 + float shallowAlpha; // 14 + float deepAlpha; // 18 + float alphaShallowRange; // 1C + float alphaDeepRange; // 20 + Color underwaterColor; // 24 + float underwaterFogAmount; // 28 + float underwaterNearFog; // 2C + float underwaterFarFog; // 30 + float normalMagnitude; // 34 + float shadowNormalFalloff; // 38 + float deepNormalFalloff; // 3C + float reflectivityAmount; // 40 + float fresnelAmount; // 44 + float surfaceEffectFalloff; // 48 + float displacementForce; // 4C + float displacementVelocity; // 50 + float displacementFalloff; // 54 + float displacementDampener; // 58 + float displacementStartingSize; // 5C + Color reflectionColor; // 60 + float sunSpecularPower; // 64 + float sunSpecularMagnitude; // 68 + float sunSparklePower; // 6C + float sunSparkleMagnitude; // 70 + float interiorSpecularRadius; // 74 + float interiorSpecularBrightness; // 78 + float interiorSpecularPower; // 7C + float layer1WindDirection; // 80 + float layer2WindDirection; // 84 + float layer3WindDirection; // 88 + float layer1WindSpeed; // 8C + float layer2WindSpeed; // 90 + float layer3WindSpeed; // 94 + float layer1AmplitudeScale; // 98 + float layer2AmplitudeScale; // 9C + float layer3AmplitudeScale; // A0 + float layer1UVScale; // A4 + float layer2UVScale; // A8 + float layer3UVScale; // AC + float layer1NoiseFalloff; // B0 + float layer2NoiseFalloff; // B4 + float layer3NoiseFalloff; // B8 + float siltAmount; // BC + Color siltLightColor; // C0 + Color siltDarkColor; // C4 + }; + VisualData visualData; // B0 + UInt64 unk178; // 178 + UInt64 unk180; // 180 + UInt64 unk188; // 188 + UInt64 unk190; // 190 + UInt64 unk198; // 198 + UInt32 unk1A0; // 1A0 - 9 + UInt32 unk1A4; // 1A4 - 9 + UInt32 unk1A8; // 1A8 - 4 + float unk1AC; // 1AC + float unk1B0; // 1B0 + float unk1B4; // 1B4 + float unk1B8; // 1B8 + float unk1BC; // 1BC + SpellItem * consumeSpell; // 1C0 + SpellItem * contactSpell; // 1C8 + UInt64 unk1D0[(0x208 - 0x1D0) >> 3]; // 30 + TESImageSpace * imageSpace; // 208 + float linearVelocityX; // 210 + float linearVelocityY; // 214 + float linearVelocityZ; // 218 + float angularVelocityX; // 21C + float angularVelocityY; // 220 + float angularVelocityZ; // 224 +}; + +// 98 +class BGSPerk : public TESForm +{ +public: + enum { kTypeID = kFormType_PERK }; + + // parents + TESFullName fullName; // 20 + TESDescription description; // 30 + TESIcon icon; // 48 + + UInt8 trait; // 58 + UInt8 perkLevel; // 59 + UInt8 numRanks; // 5A + UInt8 playable; // 5B + UInt8 hidden; // 5C + UInt8 unk5D; // 5D + UInt8 unk5E; // 5E + UInt8 unk5F; // 5F + Condition * condition; // 60 + tArray perkEntries; // 68 + BGSPerk * nextPerk; // 80 + BGSSoundDescriptorForm * sound; // 88 + BSFixedString swfPath; // 90 +}; + +// 88 +class BGSConstructibleObject : public TESForm +{ +public: + enum { kTypeID = kFormType_COBJ }; + + BGSPickupPutdownSounds pickupPutdownSounds; // 20 + TESDescription description; // 38 + + struct Component + { + BGSComponent * component; // 00 + UInt32 count; // 08 + }; + + tArray * components; // 50 + Condition * conditions; // 58 + TESForm * createdObject; // 60 + BGSKeyword * workbenchKeyword; // 68 + UInt16 createdCount; // 70 + UInt16 priority; // 72 + UInt32 unk74; // 74 + void * unk78; // 78 + UInt64 unk80; // 80 +}; +STATIC_ASSERT(offsetof(BGSConstructibleObject, createdObject) == 0x60); +STATIC_ASSERT(offsetof(BGSConstructibleObject, workbenchKeyword) == 0x68); +STATIC_ASSERT(sizeof(BGSConstructibleObject) == 0x88); + +// 38 +class TESGlobal : public TESForm +{ +public: + enum { kTypeID = kFormType_GLOB }; + + const char* editorID; // 20 + UInt64 unk28; // 28 + float value; // 30 + UInt32 unk34; // 34 +}; + +// 28 +class BGSStoryManagerTreeForm : public TESForm +{ +public: + UInt64 unk20; // 20 +}; + +// 2F0 +class TESQuest : public BGSStoryManagerTreeForm +{ +public: + + enum { kTypeID = kFormType_QUST }; + + TESFullName fullName; // 28 + UInt64 unk38[(0xF0 - 0x38) >> 3]; // 038 + UInt32 unkF0; // 0F0 + UInt16 flags; // 0F4 + UInt8 priority; // 0F6 + UInt8 type; // 0F7 + + UInt64 unk0F8[(0x2F0 - 0xF8) >> 3]; // 0F8 + + bool IsCompleted() + { + return ((flags >> 1) & 1); + } + + void SetActive(bool active) + { + if (active) + { + flags |= 0x800; + } + else + { + flags &= ~0x800; + } + } + + MEMBER_FN_PREFIX(TESQuest); + // broken address + DEFINE_MEMBER_FN(ForceRefTo, UInt32, 0x00375050, UInt32 aliasId, TESObjectREFR * reference); + DEFINE_MEMBER_FN(NewGame_Internal, UInt8, 0x005D70C0, UInt8 * unk1, UInt8 unk2); + + UInt8 NewGame_Hook(UInt8 * unk1, UInt8 unk2); +}; +STATIC_ASSERT(offsetof(TESQuest, type) == 0xF7); + +// 28 +class BGSBaseAlias // Not actually a form, but its used like one in Papyrus +{ +public: + virtual ~BGSBaseAlias(); + virtual void Unk01(void) = 0; + virtual void Unk02(void) = 0; + virtual void Unk03(void) = 0; + virtual void Unk04(void) = 0; + virtual void Unk05(void) = 0; + virtual void Unk06(void) = 0; + virtual void Unk07(void) = 0; + + enum { kTypeID = kFormType_Alias }; + + StringCache::Ref name; // 08 + TESQuest * owner; // 10 + UInt64 aliasId; // 18 + UInt16 flags; // 20 + UInt16 pad22; // 22 + UInt32 pad24; // 24 +}; + +// 48 +class BGSRefAlias : public BGSBaseAlias +{ +public: + enum { kTypeID = kFormType_ReferenceAlias }; + + UInt64 unk28; // 28 + UInt64 unk30; // 30 + UInt64 unk38; // 38 + UInt64 unk40; // 40 +}; + +// 50 +class BGSRefCollectionAlias : public BGSBaseAlias +{ +public: + enum { kTypeID = kFormType_RefCollectionAlias }; + + UInt64 unk48; // 48 +}; + +// 60 +class BGSLocAlias : public BGSBaseAlias +{ +public: + enum { kTypeID = kFormType_LocationAlias }; + + UInt64 unk28; // 28 + UInt64 unk30; // 30 + UInt64 unk38; // 38 + UInt64 unk40; // 40 + SInt64 unk48; // 48 + SInt64 unk50; // 50 + UInt32 unk58; // 58 + UInt32 unk5C; // 5C +}; + +// 08 +class IFormFactory +{ +public: + virtual ~IFormFactory(); + + virtual TESForm * Create(void) = 0; + virtual const char * GetName(void) = 0; + virtual UInt32 GetTypeID(void) = 0; + + virtual UInt32 Unk_04(void); // return 0 + virtual UInt32 Unk_05(void); // return 0x7E + virtual UInt32 Unk_06(void); // return 9 + virtual UInt32 Unk_07(void); // return 0 + + static IFormFactory * GetFactoryForType(UInt32 type) + { + if(type > kFormType_Max) + return nullptr; + + IFormFactory ** factoryList = g_formFactoryList; + return factoryList[type]; + } +}; + +// 10 +class ConcreteFormFactory : public IFormFactory +{ +public: + const char * name; // 08 +}; + +struct ObjectModMiscPair +{ + BGSMod::Attachment::Mod * key; + TESObjectMISC * miscObject; + + operator BGSMod::Attachment::Mod*() const { return key; } + + static inline UInt32 GetHash(BGSMod::Attachment::Mod ** key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)*key, 0); + return hash; + } +}; + +extern RelocPtr > g_modAttachmentMap; diff --git a/f4se/f4se/GameInput.cpp b/f4se/f4se/GameInput.cpp new file mode 100644 index 0000000..e5f62ce --- /dev/null +++ b/f4se/f4se/GameInput.cpp @@ -0,0 +1,87 @@ +#include "f4se/GameInput.h" + +// 143DFBBBC9422B50436ED42E5F2E83564DD49923+280 +RelocPtr g_inputEventTable(0x05A5FA60); +// 78EBF165B2B1065FD2448FCFC00FEA5B8AA7B28F+9C +RelocPtr g_inputDeviceMgr(0x05A5F668); +// C34730A09C170DD83B6E000FC6B0D2E1EAEC63A4+121 +RelocPtr g_inputMgr(0x059DA210); + +// 596277207F98DF39B4A6684C55367AE307B03594+17 +RelocPtr g_menuControls(0x058D0AD8); // BSInputEventReceiver + +// F719767F2CF934BC5BEE3EF3CBDFC3AA7BF47C13+2A8 +RelocPtr g_playerControls(0x059DA218); + +UInt8 InputManager::AllowTextInput(bool allow) +{ + if(allow) + { + if(allowTextInput == 0xFF) + _WARNING("InputManager::AllowTextInput: counter overflow"); + else + allowTextInput++; + } + else + { + if(allowTextInput == 0) + _WARNING("InputManager::AllowTextInput: counter underflow"); + else + allowTextInput--; + } + + return allowTextInput; +} + +UInt32 InputManager::GetMappedKey(BSFixedString name, UInt32 deviceType, UInt32 contextIdx) +{ + ASSERT(contextIdx < kContextCount); + + tArray * mappings; + if (deviceType == InputEvent::kDeviceType_Mouse) + mappings = &context[contextIdx]->mouseMap; + else if (deviceType == InputEvent::kDeviceType_Gamepad) + mappings = &context[contextIdx]->gamepadMap; + else + mappings = &context[contextIdx]->keyboardMap; + + for (UInt32 i=0; i < mappings->count; i++) + { + InputContext::Mapping m; + if (!mappings->GetNthItem(i, m)) + break; + if (m.name == name) + return m.buttonID; + } + + // Unbound + return 0xFF; +} + +BSFixedString InputManager::GetMappedControl(UInt32 buttonID, UInt32 deviceType, UInt32 contextIdx) +{ + ASSERT(contextIdx < kContextCount); + + // 0xFF == unbound + if (buttonID == 0xFF) + return BSFixedString(); + + tArray * mappings; + if (deviceType == InputEvent::kDeviceType_Mouse) + mappings = &context[contextIdx]->mouseMap; + else if (deviceType == InputEvent::kDeviceType_Gamepad) + mappings = &context[contextIdx]->gamepadMap; + else + mappings = &context[contextIdx]->keyboardMap; + + for (UInt32 i=0; i < mappings->count; i++) + { + InputContext::Mapping m; + if (!mappings->GetNthItem(i, m)) + break; + if (m.buttonID == buttonID) + return m.name; + } + + return BSFixedString(); +} diff --git a/f4se/f4se/GameInput.h b/f4se/f4se/GameInput.h new file mode 100644 index 0000000..160b178 --- /dev/null +++ b/f4se/f4se/GameInput.h @@ -0,0 +1,391 @@ +#pragma once + +#include "f4se_common/Utilities.h" +#include "f4se_common/Relocation.h" +#include "f4se/GameTypes.h" +#include "f4se/GameEvents.h" + +// 28 +class InputEvent +{ +public: + virtual ~InputEvent(); + + virtual bool IsIDEvent(); + virtual BSFixedString * GetControlID(); + + enum + { + kDeviceType_Keyboard = 0, + kDeviceType_Mouse, + kDeviceType_Gamepad, + kDeviceType_Kinect + }; + + enum + { + kEventType_Button = 0, + kEventType_MouseMove, + kEventType_Char, + kEventType_Thumbstick, + kEventType_DeviceConnect, + kEventType_Kinect, + kEventType_Unk6 + }; + + UInt64 deviceType; // 08 + UInt64 eventType; // 10 + InputEvent * next; // 18 + UInt32 unk20; // 20 + UInt32 unk24; // 24 - When this != 2 it means stop processing +}; +STATIC_ASSERT(sizeof(InputEvent) == 0x28); + +// 08 +class IDEvent +{ +public: + BSFixedString controlID; // 00 +}; + +// 40 +class ButtonEvent : public IDEvent, public InputEvent +{ +public: + UInt32 keyMask; // 30 + UInt32 flags; // 34 (00000038 when ALT is pressed, 0000001D when STRG is pressed) + float isDown; // 38 + float timer; // 3C (hold duration) +}; +STATIC_ASSERT(sizeof(ButtonEvent) == 0x040); + +// 30 +class CharacterEvent : public InputEvent +{ +public: + UInt32 keyCode; +}; +STATIC_ASSERT(sizeof(CharacterEvent) == 0x030); + +// 40 +class MouseMoveEvent : public IDEvent +{ +public: + UInt32 unk28[4]; +}; + +// 40 +class CursorMoveEvent : public IDEvent, public InputEvent +{ +public: + UInt32 unk28[4]; +}; + +// 48 +class ThumbstickEvent : public IDEvent, public InputEvent +{ +public: + UInt32 unk20[6]; +}; +STATIC_ASSERT(sizeof(ThumbstickEvent) == 0x048); + +// 30 +class DeviceConnectEvent : public InputEvent +{ +public: + UInt32 unk20[2]; +}; +STATIC_ASSERT(sizeof(DeviceConnectEvent) == 0x030); + +// 40 +class KinectEvent : public IDEvent, public InputEvent +{ +public: + UInt32 unk28[4]; +}; +STATIC_ASSERT(sizeof(KinectEvent) == 0x040); + +// EF0 +class InputEventTable +{ +public: + UInt64 unk00; + ButtonEvent buttonEvents[30]; + CharacterEvent characterEvents[15]; + MouseMoveEvent mouseMoveEvents[3]; + CursorMoveEvent cursorMoveEvents[3]; + ThumbstickEvent thumbstickEvents[6]; + DeviceConnectEvent deviceConnectEvents[3]; + KinectEvent kinectEvents[3]; +}; +//STATIC_ASSERT(offsetof(InputEventTable, mouseMoveEvents) == 0xA6C); +//STATIC_ASSERT(sizeof(InputEventTable) == 0xEF0); +extern RelocPtr g_inputEventTable; + +class BSInputDevice +{ +public: + virtual ~BSInputDevice(); + + virtual void Unk_01(); + virtual void Unk_02(); + virtual bool IsEnabled(); + virtual void Unk_04(); + virtual void Unk_05(); + + UInt64 unk008; // init to 0 + UInt64 unk010; + UInt32 unk018; // padding? + UInt32 unk01C; // init to 0 + UInt32 unk020; + UInt32 unk024; + void* unk028; // init to unk_1438CDC80 in 1.3.47 + UInt64 unk030; + UInt64 unk038; // init to 0 + void* unk040; + UInt32 unk048; + UInt32 unk04C; // init to 0 + UInt32 unk050; + UInt32 unk054; + void* unk058; // init to &unk_1438CDC84 in 1.3.47 + UInt64 unk060; + UInt64 unk068; // init to 0 +}; +STATIC_ASSERT(offsetof(BSInputDevice, unk058) == 0x058); +STATIC_ASSERT(sizeof(BSInputDevice) == 0x70); + +class BSKeyboardDevice : public BSInputDevice +{ +public: +}; +STATIC_ASSERT(sizeof(BSKeyboardDevice) == 0x70); + +// 070 +class BSVirtualKeyboardDevice : public BSKeyboardDevice +{ + +}; +STATIC_ASSERT(sizeof(BSVirtualKeyboardDevice) == 0x70); + + +// 270 +class BSPCKeyboardDevice : public BSKeyboardDevice +{ +public: + UInt64 unk070[(0x270-0x70)/sizeof(UInt64)]; +}; +STATIC_ASSERT(sizeof(BSPCKeyboardDevice) == 0x270); + +class BSMouseDevice : public BSInputDevice +{ + +}; +STATIC_ASSERT(sizeof(BSVirtualKeyboardDevice) == 0x70); + +// 88 +class BSPCMouseDevice : public BSMouseDevice +{ +public: + UInt64 unk070; + UInt64 unk078; + UInt64 unk080; +}; +STATIC_ASSERT(sizeof(BSPCMouseDevice) == 0x88); + +class BSGamepadDevice : public BSInputDevice +{ +}; + + +class BSGamepadDeviceDelegate : public BSGamepadDevice +{ + +}; + +class BSPCGamepadDevice : public BSGamepadDeviceDelegate +{ + +}; + +// B8 +class BSPCGamepadDeviceHandler : public BSGamepadDevice +{ +public: + UInt64 unk070[(0xB8-0x70)/sizeof(UInt64)]; +}; +STATIC_ASSERT(sizeof(BSPCGamepadDeviceHandler) == 0xB8); + +// 70 +class BSPCVirtualKeyboardDevice : public BSVirtualKeyboardDevice +{ + + +}; +STATIC_ASSERT(sizeof(BSPCVirtualKeyboardDevice) == 0x70); + +// 10 +class BSInputEventUser +{ +public: + BSInputEventUser() : enabled(false) { } + BSInputEventUser(bool bEnabled) : enabled(bEnabled) { } + virtual ~BSInputEventUser() { }; + + virtual bool IsEnabled(InputEvent * inputEvent = nullptr) { return enabled; }; + virtual void OnKinectEvent(KinectEvent * inputEvent) { }; + virtual void OnDeviceConnectEvent(DeviceConnectEvent * inputEvent) { }; + virtual void OnThumbstickEvent(ThumbstickEvent * inputEvent) { }; + virtual void OnCursorMoveEvent(CursorMoveEvent * inputEvent) { }; + virtual void OnMouseMoveEvent(MouseMoveEvent * inputEvent) { }; + virtual void OnCharacterEvent(CharacterEvent * inputEvent) { }; + virtual void OnButtonEvent(ButtonEvent * inputEvent) { }; + + bool enabled; + +private: + // GameMenuBase:BSInputEventUser override should be the only one calling this function + friend class GameMenuBase; + DEFINE_MEMBER_FN_1(Impl_OnGameMenuBaseButtonEvent, bool, 0x0210F730, ButtonEvent * button); +}; + +// 30 +class PlayerInputHandler : public BSInputEventUser +{ +public: + PlayerInputHandler() : BSInputEventUser(true), + unk10(0), unk18(0), unk19(0), unk1A(0), unk1C(0), unk20(0), unk21(0), unk22(0), unk24(0), unk28(0), unk2C(0) { } + + virtual void Unk_09() { }; + virtual void Unk_0A(void * unk1) { }; + + UInt64 unk10; // 10 + UInt8 unk18; // 18 + UInt8 unk19; // 19 + UInt16 unk1A; // 1A + UInt32 unk1C; // 1C + UInt8 unk20; // 20 + UInt8 unk21; // 21 unk21 == 1 has something to do with an additional vfunc + UInt16 unk22; // 22 + UInt32 unk24; // 24 + UInt8 unk28; // 28 + UInt32 unk2C; // 2C +}; + +// 10 +class BSInputEventReceiver +{ +public: + virtual ~BSInputEventReceiver(); + + UInt32 unk08; // 08 + UInt32 unk0C; // 0C +}; + +class MenuControls : public BSInputEventReceiver +{ +public: + virtual ~MenuControls(); // Executes receiving function + + UInt64 unk10; // 10 + tArray inputEvents; // 18 + BSInputEventUser * events[8]; // 30 +}; + +extern RelocPtr g_menuControls; + +class PlayerControls : public BSInputEventReceiver +{ +public: + virtual ~PlayerControls(); + + BSTEventSink openCloseEvent; // 10 + BSTEventSink menuModeChangeEvent; // 18 + BSTEventSink furnitureEvent; // 20 + BSTEventSink userEventEnabledEvent; // 28 + void* movementInterface; // 30 - IMovementPlayerControls + BSTEventSink quickContainerStateEvent; // 38 + + UInt64 unk40; // 40 + UInt64 unk48; // 48 + UInt64 unk50; // 50 + UInt64 unk58; // 58 + UInt64 unk60; // 60 + UInt32 unk68; // 68 + UInt32 unk6C; // 6C + UInt32 unk70; // 70 + UInt32 unk74; // 74 + UInt32 unk78; // 78 + UInt32 unk7C; // 7C + UInt32 unk80; // 80 + float unk84; // 84 + UInt32 unk88; // 88 + UInt32 unk8C; // 8C + tArray inputEvents1; // 90 + tArray inputEvents2; // A8 - This subset has to do with unk20 and unk21 on the handler + // ... +}; +extern RelocPtr g_playerControls; + +class InputDeviceManager +{ +public: + UInt64 unk00; // 000 + BSPCKeyboardDevice* keyboardDevice; // 008 + BSPCMouseDevice* mouseDevice; // 010 + BSPCGamepadDeviceHandler* gamepadHandler; // 018 + BSPCGamepadDeviceHandler* gamepadHandler020; // 020 + BSPCVirtualKeyboardDevice* virtualKeyboardDevice; // 028 + + bool IsGamepadEnabled() + { + return gamepadHandler ? gamepadHandler->IsEnabled() : false; + } +}; +extern RelocPtr g_inputDeviceMgr; + +// 148 +class InputManager +{ +public: + enum + { + kContext_Gameplay = 0, + kContextCount = (0x108/8) // 33 + }; + + struct InputContext + { + // 18 + struct Mapping + { + BSFixedString name; // 00 + UInt32 buttonID; // 08 + UInt32 sortIndex; // 0C + UInt32 unk10; // 10 + UInt32 unk14; // 14 + }; + + tArray keyboardMap; + tArray mouseMap; + tArray gamepadMap; + }; + + void * unk00; // 000 + InputContext * context[kContextCount]; // 008 + tArray unk110; // 110 + tArray unk128; // 128 + UInt8 allowTextInput; // 140 + UInt8 unk141; // 141 + UInt8 unk142; // 142 + UInt8 unk143; // 143 + UInt32 unk144; // 144 + + UInt8 AllowTextInput(bool allow); + UInt32 GetMappedKey(BSFixedString name, UInt32 deviceType, UInt32 contextIdx); + BSFixedString GetMappedControl(UInt32 buttonID, UInt32 deviceType, UInt32 contextIdx); +}; +STATIC_ASSERT(offsetof(InputManager, unk110) == 0x110); +STATIC_ASSERT(offsetof(InputManager, unk128) == 0x128); +STATIC_ASSERT(offsetof(InputManager, allowTextInput) == 0x140); + +extern RelocPtr g_inputMgr; diff --git a/f4se/f4se/GameMemory.cpp b/f4se/f4se/GameMemory.cpp new file mode 100644 index 0000000..1827167 --- /dev/null +++ b/f4se/f4se/GameMemory.cpp @@ -0,0 +1 @@ +#include "f4se/GameMemory.h" diff --git a/f4se/f4se/GameMemory.h b/f4se/f4se/GameMemory.h new file mode 100644 index 0000000..ce24746 --- /dev/null +++ b/f4se/f4se/GameMemory.h @@ -0,0 +1,135 @@ +#pragma once + +// 08 +class IMemoryStoreBase +{ +public: + struct MemInfo + { + char * debugName; // 00 + UInt64 bytesAllocated; // 08 + UInt64 allocChunkSize2; // 10 + UInt64 maxSize; // 18 + UInt32 unk20; // 20 - heap overhead? AbstractHeap::unk88 * 0x20 + 0x2A8 + UInt32 pad24; // 24 + UInt64 remaining; // 28 + }; + + virtual ~IMemoryStoreBase(); + + virtual UInt64 ConvertAddr(UInt64 addr) = 0; + virtual void GetStats(MemInfo * dst) = 0; + virtual bool OwnedBy(void * ptr) = 0; // returns true if ptr is managed by this + +// void ** _vtbl; // 00 +}; + +// 08 +class IMemoryStore : public IMemoryStoreBase +{ +public: + virtual ~IMemoryStore(); + + virtual void * Alloc(UInt64 len, UInt32 align) = 0; // probably alloc + virtual void Free(void * buf) = 0; // probably free + virtual UInt32 Unk06() = 0; +}; + +// 08 +class IMemoryHeap : public IMemoryStore +{ +public: + struct HeapInfo + { + char * debugName; // 00 + UInt64 maxSize; // 08 + UInt64 allocChunkSize2; // 10 + UInt64 unk18; // 18 + UInt32 unk20; // 20 + UInt32 unk24; // 24 + UInt64 unk28; // 28 + UInt64 unk30; // 30 + UInt64 unk38; // 38 + UInt64 unk40; // 40 + UInt64 unk48; // 48 + UInt64 unk50; // 50 + UInt64 unk58; // 58 + UInt64 unk60; // 60 + }; + + virtual ~IMemoryHeap(); + + virtual char * GetDebugName(); + virtual void * InternalAlloc(UInt64 len) = 0; + virtual void InternalFree(void * buf) = 0; + virtual bool OwnedBy(void * buf); + virtual void Unk0B() = 0; + virtual void GetHeapStats(HeapInfo * stats); + virtual bool Unk0D(); + virtual UInt32 Unk0E(); +}; + +// A8 +class AbstractHeap : public IMemoryStore +{ +public: + virtual ~AbstractHeap(); + + virtual void * RawAlloc(UInt32 unk0, UInt64 size) = 0; + virtual void RawFree(void * buf) = 0; + virtual UInt32 Unk11(); + virtual UInt32 Unk12(); + virtual void Unk13(); + virtual void Unk14(); + + CRITICAL_SECTION lock; // 08 + + char * debugName; // 30 - ctor arg2 + UInt64 unk38; // 38 - init'd to 0x30 in ctor + UInt32 unk40; // 40 - ctor arg3 + UInt32 pad44; // 44 + UInt64 maxSize; // 48 - ctor arg0, guessing based on context + UInt64 allocChunkSize1; // 50 - ctor arg1 + UInt64 unk58; // 58 - ctor arg1 + UInt64 unk60; // 60 - init'd to 0 in ctor + UInt64 unk68; // 68 - init'd to 0 in ctor + UInt64 unk70; // 70 - init'd to 0 in ctor + UInt64 unk78; // 78 - init'd to 0 in ctor + UInt64 unk80; // 80 - init'd to 0 in ctor + UInt32 unk88; // 88 - init'd to 0 in ctor + UInt32 pad8C; // 8C + UInt64 unk90; // 90 - init'd to 0 in ctor + UInt64 unk98; // 98 - init'd to 0 in ctor + UInt32 unkA0; // A0 - init'd to 0 in ctor + UInt8 unkA4; // A4 - ctor arg5 + UInt8 unkA5; // A5 - ctor arg4 + UInt16 padA6; // A6 +}; + +// 2B0 +class MemoryHeap : public AbstractHeap +{ +public: + virtual ~MemoryHeap(); + + UInt8 unk0A8[0x100]; // 0A8 + UInt8 unk1A8[0x100]; // 1A8 + UInt8 unk2A8; // 2A8 + UInt8 pad2A9[7]; // 2A9 +}; + +// 38 +// heap designed for single-time allocs. freed memory is never reclaimed +class ZeroOverheadHeap : public IMemoryHeap +{ +public: + virtual ~ZeroOverheadHeap(); + + UInt64 size; // 08 + char * debugName; // 10 + void * buffer; // 18 + void * curPtr; // 20 + UInt64 unk28; // 28 + UInt32 unk30; // 30 + UInt32 pad34; // 34 +}; diff --git a/f4se/f4se/GameMenus.cpp b/f4se/f4se/GameMenus.cpp new file mode 100644 index 0000000..353612b --- /dev/null +++ b/f4se/f4se/GameMenus.cpp @@ -0,0 +1,135 @@ +#include "f4se/GameMenus.h" + +// 2CA5233612B3158658DB6DB9C90FD0258F1836E2+AD +RelocPtr g_ui(0x058D0898); + +RelocAddr <_HasHUDContext> HasHUDContext(0x00A4F390); + +RelocAddr<_GetChildElement> GetChildElement(0x020F0DC0); + +// 2CA5233612B3158658DB6DB9C90FD0258F1836E2+F1 +RelocPtr g_uiMessageManager(0x058D0AC8); + +// E8B45849BEED1FD76CD4D25F030C48F09D0B41F1+90 +RelocPtr g_menuTableLock(0x065774B8); + +bool UI::IsMenuOpen(const BSFixedString & menuName) +{ + return CALL_MEMBER_FN(this, IsMenuOpen)(menuName); +} + +bool UI::IsMenuRegistered(BSFixedString & menuName) +{ + BSReadLocker locker(g_menuTableLock); + MenuTableItem * item = menuTable.Find(&menuName); + if (item) { + return true; + } + + return false; +} + +IMenu * UI::GetMenu(BSFixedString & menuName) +{ + if (!menuName.data->Get()) + return nullptr; + + BSReadLocker locker(g_menuTableLock); + MenuTableItem * item = menuTable.Find(&menuName); + if (!item) { + return nullptr; + } + + IMenu * menu = item->menuInstance; + if(!menu) { + return nullptr; + } + + return menu; +} + +IMenu * UI::GetMenuByMovie(GFxMovieView * movie) +{ + if (!movie) { + return nullptr; + } + + BSReadLocker locker(g_menuTableLock); + + IMenu * menu = nullptr; + menuTable.ForEach([movie, &menu](MenuTableItem * item) + { + IMenu * itemMenu = item->menuInstance; + if(itemMenu) { + GFxMovieView * view = itemMenu->movie; + if(view) { + if(movie == view) { + menu = itemMenu; + return false; + } + } + } + return true; + }); + + return menu; +} + +bool UI::UnregisterMenu(BSFixedString & name, bool force) +{ + BSReadAndWriteLocker locker(g_menuTableLock); + MenuTableItem * item = menuTable.Find(&name); + if (!item || (item->menuInstance && !force)) { + return false; + } + + return menuTable.Remove(&name); +} + +HUDComponentBase::HUDComponentBase(GFxValue * parent, const char * componentName, HUDContextArray * contextList) +{ + Impl_ctor(parent, componentName, contextList); +} + +HUDComponentBase::~HUDComponentBase() +{ + for(UInt32 i = 0; i < contexts.count; i++) + { + contexts.entries[i].Release(); + } + + Heap_Free(contexts.entries); + contexts.count = 0; +} + +void HUDComponentBase::UpdateVisibilityContext(void * unk1) +{ + HasHUDContext(&contexts, unk1); + bool bVisible = IsVisible(); + double alpha = 0.0; + if(bVisible) { + alpha = 100.0; + } + + BSGFxDisplayObject::BSDisplayInfo dInfo; + void * unk2 = GetExtDisplayInfo(&dInfo, this); + SetExtDisplayInfoAlpha(unk2, alpha); + SetExtDisplayInfo(&dInfo); + + unkEC = bVisible == false; +} + +void HUDComponentBase::ColorizeComponent() +{ + SetFilterColor(isWarning); +} + +GameMenuBase::GameMenuBase() : IMenu() +{ + Impl_ctor(); +} + +GameMenuBase::~GameMenuBase() +{ + Impl_dtor(); +} \ No newline at end of file diff --git a/f4se/f4se/GameMenus.h b/f4se/f4se/GameMenus.h new file mode 100644 index 0000000..90909ea --- /dev/null +++ b/f4se/f4se/GameMenus.h @@ -0,0 +1,448 @@ +#pragma once + +#include "f4se_common/Utilities.h" +#include "f4se_common/Relocation.h" + +#include "f4se/GameInput.h" +#include "f4se/GameTypes.h" +#include "f4se/GameUtilities.h" +#include "f4se/ScaleformAPI.h" +#include "f4se/ScaleformCallbacks.h" +#include "f4se/ScaleformValue.h" + +typedef GFxValue* (*_GetChildElement)(GFxValue * parent, GFxValue & child, const char * path); +extern RelocAddr<_GetChildElement> GetChildElement; + +enum MessageType +{ + kMessage_Refresh = 0, + kMessage_Open, + kMessage_Close = 3, + kMessage_Scaleform = 5,//keydown/up + kMessage_Message, + kMessage_Platform = 11 +}; + +class UIMessage +{ +public: + virtual ~UIMessage(); + + BSFixedString name; // 08 + UInt32 type; // 10 +}; + +class UIMessageManager +{ +public: + MEMBER_FN_PREFIX(UIMessageManager); + DEFINE_MEMBER_FN(SendUIMessage, void, 0x0204CB90, BSFixedString& menuName, UInt32 type); + // 325A22C9C57B8175C01F1E071B4E272401994375+CB + DEFINE_MEMBER_FN(SendUIMessageEx, void, 0x012BAA10, BSFixedString& menuName, UInt32 type, UIMessage * pExtraData); +}; +extern RelocPtr g_uiMessageManager; + +class IMenu : + public SWFToCodeFunctionHandler, + public BSInputEventUser +{ +public: + enum + { + //Confirmed + kFlag_PauseGame = 0x01, + kFlag_DoNotDeleteOnClose = 0x02, + kFlag_ShowCursor = 0x04, + kFlag_EnableMenuControl = 0x08, // 1, 2 + kFlag_ShaderdWorld = 0x20, + kFlag_Open = 0x40,//set it after open. + kFlag_DoNotPreventGameSave = 0x800, + kFlag_ApplyDropDownFilter = 0x8000, // + kFlag_BlurBackground = 0x400000, + + //Unconfirmed + kFlag_Modal = 0x10, + kFlag_PreventGameLoad = 0x80, + kFlag_Unk0100 = 0x100, + kFlag_HideOther = 0x200, + kFlag_DisableInteractive = 0x4000, + kFlag_UpdateCursorOnPlatformChange = 0x400, + kFlag_Unk1000 = 0x1000, + kFlag_ItemMenu = 0x2000, + kFlag_Unk10000 = 0x10000, // mouse cursor + kFlag_Unk800000 = 0x800000 + }; + virtual UInt32 ProcessMessage(UIMessage * msg) = 0;//??? + virtual void DrawNextFrame(float unk0, void * unk1) = 0; //210E8C0 + virtual void * Unk_05(void) { return nullptr; }; //return 0; + virtual void * Unk_06(void) { return nullptr; }; //return 0; + virtual bool Unk_07(UInt32 unk0, void * unk1) = 0; + virtual void Unk_08(UInt8 unk0) = 0; + virtual void Unk_09(BSFixedString & menuName, bool unk1) = 0; //UInt64 = 0; //UInt64 + virtual void Unk_0A(void) = 0; + virtual void Unk_0B(void) = 0; + virtual void Unk_0C(void) = 0; + virtual bool Unk_0D(bool unk0) = 0; + virtual bool Unk_0E(void) { return false; }; + virtual bool CanProcessControl(BSFixedString & controlID) { return false; }; + virtual bool Unk_10(void) = 0; + virtual void Unk_11(void) = 0; + virtual void Unk_12(void * unk0) = 0; + + GFxValue stage; // 20 + GFxMovieView * movie; // 40 + BSFixedString unk48; // 48 + BSFixedString menuName; // 50 + UInt32 flags; // 58 + + /* + A A A A A A A A B B B B B B B B C C C C C C C C D D D D D D D D + LoadingMenu 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 1 0 1 1 0 0 0 0 0 1 depth: 000E context: 0003 + Console 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 0 1 1 1 depth: 0013 context: 0006 + LevelUpMenu 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 0 1 1 0 0 0 1 1 1 depth: 0009 context: 0022 + FaderMenu 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 depth: 0006 context: 0022 + CursorMenu 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 depth: 0014 context: 0022 + VignetteMenu 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 depth: 0003 context: 0022 + MessageBoxMenu 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 1 1 0 1 1 1 0 1 depth: 000A context: 0022 + ContainerMenu 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 depth: 0006 context: 0022 + ExamineMenu 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 1 0 1 0 1 0 0 0 1 0 1 depth: 0009 context: 0022 + CookingMenu 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 1 0 1 0 1 0 0 0 1 0 0 depth: 0009 context: 0022 + ExamineConfirmMenu 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 1 1 1 0 1 depth: 0011 context: 0022 + RobotModMenu 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 1 0 1 0 1 0 0 0 1 0 0 depth: 0009 context: 0022 + PowerArmorModMenu 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 1 0 0 1 0 1 0 1 0 0 0 1 0 0 depth: 0009 context: 0022 + WorkshopMenu 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 depth: 0006 context: 0010 + PromptMenu 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 depth: 0005 context: 0022 + SitWaitMenu 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 depth: 0006 context: 0012 + SleepWaitMenu 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 1 0 0 1 1 1 0 0 1 1 0 1 depth: 000A context: 0022 + DialogueMenu 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 depth: 0006 context: 0022 + BarterMenu 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1 0 1 depth: 0006 context: 0022 + LockpickingMenu 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 depth: 0006 context: 000C + BookMenu 0 0 0 0 1 0 0 0 0 1 1 0 1 1 0 0 1 0 0 0 0 0 0 1 0 1 1 0 1 0 0 1 depth: 0009 context: 0008 + SPECIALMenu 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 1 1 0 0 0 0 1 0 0 1 1 1 0 1 1 0 1 depth: 0006 context: 0022 + FavoritesMenu 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 depth: 0006 context: 0001 + HUDMenu 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 depth: 0005 context: 0022 + PowerArmorHUDMenu 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 depth: 0005 context: 0022 + PauseMenu 0 0 0 0 1 0 0 0 0 1 0 0 1 1 0 0 1 0 0 0 1 1 1 0 0 1 0 1 1 1 0 1 depth: 000B context: 0022 + VATSMenu 0 0 0 0 0 0 0 0 1 0 0 0 1 1 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0 1 0 0 depth: 0006 context: 000D + PipboyMenu 0 0 0 0 0 0 0 0 1 0 1 0 1 1 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 1 0 1 depth: 0008 context: 0022 + PipboyHolotapeMenu 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 depth: 0009 context: 0022 + */ + + UInt32 unk5C; // 5C + UInt32 unk60; // 60 init'd as DWord then Byte + UInt8 depth; // 64 defalut is 6. + UInt32 context; // 68 init'd in IMenu::IMenu + UInt32 pad6C; // 6C +}; +STATIC_ASSERT(offsetof(IMenu, movie) == 0x40); +STATIC_ASSERT(offsetof(IMenu, flags) == 0x58); + +// E0 +class GameMenuBase : public IMenu +{ +public: + GameMenuBase(); + virtual ~GameMenuBase(); + + // BSInputEventUser overrides + virtual void OnButtonEvent(ButtonEvent * inputEvent) override { Impl_OnGameMenuBaseButtonEvent(inputEvent); }; + + // IMenu overrides + virtual void Invoke(Args * args) override { } + virtual void RegisterFunctions() override { } + virtual UInt32 ProcessMessage(UIMessage * msg) override { return Impl_ProcessMessage(msg); };//??? + virtual void DrawNextFrame(float unk0, void * unk1) override { return Impl_DrawNextFrame(unk0, unk1); }; //render,HUD menu uses this function to update its HUD components. + virtual bool Unk_07(UInt32 unk0, void * unk1) override { return Impl_Unk07(unk0, unk1); }; + virtual void Unk_08(UInt8 unk0) override { return Impl_Unk08(unk0); }; + virtual void Unk_09(BSFixedString & menuName, bool unk1) override { return Impl_Unk09(menuName, unk1); }; //UInt64 + virtual void Unk_0A(void) override { return Impl_Unk0A(); }; + virtual void Unk_0B(void) override { return Impl_Unk0B(); } + virtual void Unk_0C(void) override { return Impl_Unk0C(); }; + virtual bool Unk_0D(bool unk0) override { return Impl_Unk0D(unk0); } + virtual bool Unk_0E(void) override { return false; }; + virtual bool CanProcessControl(BSFixedString & controlID) override { return false; }; + virtual bool Unk_10(void) override { return Impl_Unk10(); } //90 - E0 + virtual void Unk_11(void) override { return Impl_Unk11(); }; + virtual void Unk_12(void * unk0) override { return Impl_Unk12(unk0); } + virtual void Unk_13(void * unk0, void * unk1) { return Impl_Unk13(unk0, unk1); } + + tArray subcomponents; // 70 + BSGFxShaderFXTarget * shaderTarget; // 88 + void * unk90; // 90 + UInt64 unk98[(0xE0 - 0x98) >> 3]; // 98 + + DEFINE_STATIC_HEAP(ScaleformHeap_Allocate, ScaleformHeap_Free) +private: + // ??_7GameMenuBase@@6B@ + + DEFINE_MEMBER_FN_0(Impl_ctor, void *, 0x00B324E0); + DEFINE_MEMBER_FN_0(Impl_dtor, void *, 0x00B325A0); + DEFINE_MEMBER_FN_2(Impl_DrawNextFrame, void, 0x0210EED0, float unk0, void * unk1); + DEFINE_MEMBER_FN_1(Impl_ProcessMessage, UInt32, 0x0210EE50, UIMessage * msg); + DEFINE_MEMBER_FN_2(Impl_Unk07, bool, 0x0210F310, UInt32 unk0, void * unk1); + DEFINE_MEMBER_FN_1(Impl_Unk08, void, 0x00B32A50, UInt8 unk0); + DEFINE_MEMBER_FN_2(Impl_Unk09, void, 0x0210F550, BSFixedString & menuName, bool unk1); + DEFINE_MEMBER_FN_0(Impl_Unk0A, void, 0x00B32AC0); + DEFINE_MEMBER_FN_0(Impl_Unk0B, void, 0x00B32B80); + DEFINE_MEMBER_FN_0(Impl_Unk0C, void, 0x00B32BC0) + DEFINE_MEMBER_FN_1(Impl_Unk0D, bool, 0x0210F6A0, bool unk0); + DEFINE_MEMBER_FN_0(Impl_Unk10, bool, 0x00B32870); + DEFINE_MEMBER_FN_0(Impl_Unk11, void, 0x00B32900); + DEFINE_MEMBER_FN_1(Impl_Unk12, void, 0x00B32970, void * unk0); + DEFINE_MEMBER_FN_2(Impl_Unk13, void, 0x00B329C0, void * unk0, void * unk1); +}; +STATIC_ASSERT(offsetof(GameMenuBase, shaderTarget) == 0x88); + +// 218 +class LooksMenu : public GameMenuBase +{ +public: + BSTEventSink eventSink; // E0 + UInt64 unkE8; // E8 + void * unkF0; // F0 - LooksInputRepeatHandler + UInt64 unkF8[(0x150-0xF8)/8]; + UInt32 nextBoneID; // 150 + UInt32 currentBoneID; // 154 + UInt64 unk158[(0x1E0-0x158)/8]; + UInt32 unk1E0; // 1E0 + UInt32 unk1E4; // 1E4 + UInt64 unk1E8[(0x218-0x1E8)/8]; + + DEFINE_MEMBER_FN_0(LoadCharacterParameters, void, 0x00B41580); // This function updates all the internals from the current character + // It's followed by a call to onCommitCharacterPresetChange +}; +STATIC_ASSERT(offsetof(LooksMenu, nextBoneID) == 0x150); + +// 20 +template +class HUDContextArray +{ +public: + T * entries; // 00 + UInt32 count; // 08 + UInt32 unk0C; // 0C + UInt32 flags; // 10 + UInt32 unk14; // 14 + UInt32 unk18; // 18 + bool unk1C; // 1C +}; + +// F8 +class HUDComponentBase : public BSGFxShaderFXTarget +{ +public: + HUDComponentBase(GFxValue * parent, const char * componentName, HUDContextArray * contextList); + virtual ~HUDComponentBase(); + + virtual bool Unk_02(void * unk1) { return false; } + virtual void Unk_03() { } + virtual void UpdateComponent() { Impl_UpdateComponent(); } // Does stuff + virtual void UpdateVisibilityContext(void * unk1); + virtual void ColorizeComponent(); + virtual bool IsVisible() { return Impl_IsVisible(); } + virtual bool Unk_08() { return contexts.unk1C; } + + UInt64 unkB0; // B0 + UInt64 unkB8; // B8 + UInt64 unkC0; // C0 + HUDContextArray contexts; // C8 + float unkE8; // E8 + UInt32 unkEC; // EC + UInt8 unkF0; // F0 + UInt8 unkF1; // F1 + bool isWarning; // F2 - This chooses the warning color over the default color + UInt8 padF3[5]; // F3 + + MEMBER_FN_PREFIX(HUDComponentBase); + DEFINE_MEMBER_FN_3(Impl_ctor, HUDComponentBase *, 0x00A22A70, GFxValue * parent, const char * componentName, HUDContextArray * contextList); + DEFINE_MEMBER_FN_0(Impl_IsVisible, bool, 0x00A22DB0); + DEFINE_MEMBER_FN_0(Impl_UpdateComponent, void, 0x00A22B10); + +}; +STATIC_ASSERT(offsetof(HUDComponentBase, contexts) == 0xC8); +STATIC_ASSERT(offsetof(HUDComponentBase, unkE8) == 0xE8); +STATIC_ASSERT(sizeof(HUDComponentBase) == 0xF8); + +typedef bool (* _HasHUDContext)(HUDContextArray * contexts, void * unk1); +extern RelocAddr <_HasHUDContext> HasHUDContext; + + +// 110 +class HUDComponents +{ +public: + UInt64 unk00; // 00 + HUDComponentBase * components[0x1E]; // 08 + UInt64 unk98; // 98 + UInt64 unk100; // 100 + UInt32 numComponents; // 108 - 0x1E +}; + +// 220 +class HUDMenu : public GameMenuBase +{ +public: + BSTEventSink inputEnabledSink; // E0 + BSTEventSink requestHudModesSink; // E8 + HUDComponents children; // F0 + UInt64 unk200; // 200 + UInt64 unk208; // 208 + UInt64 unk210; // 210 + UInt64 unk218; // 218 +}; +STATIC_ASSERT(offsetof(HUDMenu, unk200) == 0x200); + +// 18 +class PipboySubMenu : public BSTEventSink +{ +public: + + virtual ~PipboySubMenu(); + + virtual void Unk02(); // Pure, called by PipboySubMenu::ReceiveEvent + + GFxValue *value; + UInt64 unk10; +}; + +// 18 +class PipboyQuestMenu : public PipboySubMenu +{ +public: + + virtual ~PipboyQuestMenu(); +}; + + +// 18 +class PipboyValue +{ +public: + virtual ~PipboyValue(); + virtual void Unk01(); // Sets unk0C to 0 + virtual void Unk02(); // pure + virtual void Unk03(void *arg1); + virtual void Unk04(); // pure + + UInt32 unk08; // 08 - init'd to incremental variable + UInt8 unk0C; // 0C - init'd to 1 + UInt8 unk0D; // 0D - init'd to 1 + UInt16 pad0E; // 0E + PipboyValue *unk10; // 10 +}; + +template +class PipboyPrimitiveValue : public PipboyValue +{ +public: + + T value; // 18 +}; +STATIC_ASSERT(offsetof(PipboyPrimitiveValue, value) == 0x18); + +class PipboyObject : public PipboyValue +{ +public: + struct PipboyTableItem + { + BSFixedString key; + PipboyValue *value; + + bool operator==(const BSFixedString & a_name) const { return key == a_name; } + operator BSFixedString() const { return key; } + + static inline UInt32 GetHash(BSFixedString * key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)key->data, 0); + return hash; + } + }; + + + virtual ~PipboyObject(); + + tHashSet table; // 18 + //... +}; +STATIC_ASSERT(offsetof(PipboyObject, table) == 0x18); + +// 00C +class MenuTableItem +{ +public: + typedef IMenu * (*CallbackType)(void); + BSFixedString name; // 000 + IMenu * menuInstance; // 008 0 if the menu is not currently open + CallbackType menuConstructor; // 010 + void * unk18; // 018 + + bool operator==(const MenuTableItem & rhs) const { return name == rhs.name; } + bool operator==(const BSFixedString a_name) const { return name == a_name; } + operator UInt64() const { return (UInt64)name.data->Get(); } + + static inline UInt32 GetHash(BSFixedString * key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)key->data, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tname: %s", name.data->Get()); + _MESSAGE("\t\tinstance: %08X", menuInstance); + } +}; + +// 250 ? +class UI +{ +public: + virtual ~UI(); + + virtual void Unk_01(void); + + typedef IMenu* (*CreateFunc)(void); + typedef tHashSet MenuTable; + + bool IsMenuOpen(const BSFixedString & menuName); + IMenu * GetMenu(BSFixedString & menuName); + IMenu * GetMenuByMovie(GFxMovieView * movie); + void Register(const char* name, CreateFunc creator) + { + CALL_MEMBER_FN(this, RegisterMenu)(name, creator, 0); + } + bool IsMenuRegistered(BSFixedString & menuName); + + template + void ForEachMenu(T & menuFunc) + { + g_menuTableLock->LockForReadAndWrite(); + menuTable.ForEach(menuFunc); + g_menuTableLock->Release(); + } + + bool UnregisterMenu(BSFixedString & name, bool force = false); + + UInt64 unk08; // 08 + UInt64 unk10; // 10 + BSTEventDispatcher menuOpenCloseEventSource; // 70 + UInt64 unk70[(0x190 - 0x70) / 8]; + tArray menuStack; // 190 + MenuTable menuTable; // 1A8 + UInt64 unk1D8; // 1D8 + UInt32 numPauseGame; // 1E0 isInMenuMode + volatile SInt32 numFlag2000; // 1E4 + volatile SInt32 numFlag80; // 1E8 + UInt32 numFlag20; // 1EC + // ... + +protected: + MEMBER_FN_PREFIX(UI); + DEFINE_MEMBER_FN(RegisterMenu, void, 0x02043CF0, const char * name, CreateFunc creator, UInt64 unk1); + DEFINE_MEMBER_FN(IsMenuOpen, bool, 0x02042160, const BSFixedString & name); +}; + +extern RelocPtr g_menuTableLock; +extern RelocPtr g_ui; diff --git a/f4se/f4se/GameMessages.cpp b/f4se/f4se/GameMessages.cpp new file mode 100644 index 0000000..db7a975 --- /dev/null +++ b/f4se/f4se/GameMessages.cpp @@ -0,0 +1,4 @@ +#include "f4se/GameMessages.h" + +// C23839741F04EC9FEBD007709BC69ED7012F147A+5D +RelocPtr *> g_messageQueue(0x05AC64F0); diff --git a/f4se/f4se/GameMessages.h b/f4se/f4se/GameMessages.h new file mode 100644 index 0000000..8e0b25b --- /dev/null +++ b/f4se/f4se/GameMessages.h @@ -0,0 +1,52 @@ +#pragma once + +#include "f4se_common/Utilities.h" + +class ScrapHeap; +class BSGeometry; +class BGSTextureSet; + +struct BSPackedTask +{ + +}; + +template +class BSTMessageQueue +{ +public: + virtual void Unk_01() = 0; + virtual void Unk_02() = 0; + virtual void Unk_03() = 0; + virtual void Unk_04() = 0; +}; + +template +class BSTCommonMessageQueue : public BSTMessageQueue +{ +public: + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); + virtual void Unk_06(); + + volatile UInt32 m_refCount; // 08 + UInt32 pad0C; // 0C +}; + +template +class BSTCommonScrapHeapMessageQueue : public BSTCommonMessageQueue +{ +public: + virtual void Unk_05(); + virtual void Unk_06(); + + ScrapHeap * m_pScrapHeap; + + MEMBER_FN_PREFIX(BSTCommonScrapHeapMessageQueue); + DEFINE_MEMBER_FN(SetTextureSet, void, 0x00D5DF60, BSGeometry * geometry, BGSTextureSet * textureSet); +}; + +extern RelocPtr *> g_messageQueue; diff --git a/f4se/f4se/GameObjects.cpp b/f4se/f4se/GameObjects.cpp new file mode 100644 index 0000000..4049cfc --- /dev/null +++ b/f4se/f4se/GameObjects.cpp @@ -0,0 +1,61 @@ +#include "f4se/GameObjects.h" +#include "f4se/GameReferences.h" +#include "f4se/GameUtilities.h" + +RelocAddr <_PlaceAtMe_Native> PlaceAtMe_Native(0x0140B0E0); + +bool TESObjectARMA::GetNodeName(char * dstBuff, TESNPC * npc, TESObjectARMO * armor) +{ + if(npc) { + UInt32 sex = npc ? CALL_MEMBER_FN(npc, GetSex)() : 0; + sprintf_s(dstBuff, MAX_PATH, " (%08X)[%d]/ (%08X)", formID, sex, armor->formID); + return true; + } + + return false; +} + +void TESNPC::ChangeHeadPart(BGSHeadPart * headPart, bool bRemovePart, bool bRemoveExtraParts) +{ + if(bRemovePart) + CALL_MEMBER_FN(this, ChangeHeadPartRemovePart)(headPart, bRemoveExtraParts); + else + CALL_MEMBER_FN(this, ChangeHeadPart)(headPart); +} + +BGSHeadPart * TESNPC::GetHeadPartByType(UInt32 type, bool bOverlays) +{ + BGSHeadPart ** parts = headParts; + UInt32 numParts = numHeadParts; + if(bOverlays) { + parts = CALL_MEMBER_FN(this, GetOverlayHeadParts)(); + numParts = CALL_MEMBER_FN(this, GetNumOverlayHeadParts)(); + } + + for(UInt32 i = 0; i < numParts; ++i) + { + BGSHeadPart * part = parts[i]; + if(part && part->type == type) + { + return part; + } + } + + return nullptr; +} + +BGSColorForm * TESNPC::GetHairColor() +{ + if(!headData || headData->hairColor == nullptr) + { + TESRace * raceForm = race.race; + if(raceForm) { + CharacterCreation::CharGenData * charGenData = raceForm->chargenData[ CALL_MEMBER_FN(this, GetSex)()]; + if(charGenData) { + return charGenData->defaultColor; + } + } + } + + return headData->hairColor; +} diff --git a/f4se/f4se/GameObjects.h b/f4se/f4se/GameObjects.h new file mode 100644 index 0000000..7d6feb9 --- /dev/null +++ b/f4se/f4se/GameObjects.h @@ -0,0 +1,803 @@ +#pragma once + +#include "f4se_common/Utilities.h" + +#include "f4se/GameFormComponents.h" +#include "f4se/GameForms.h" +#include "f4se/GameEvents.h" +#include "f4se/GameCustomization.h" +#include "f4se/GameUtilities.h" +#include "f4se/NiTextures.h" + +class TESClass; +class TESCombatStyle; +class TESFaction; +class BGSOutfit; +class BGSHeadPart; +class BGSColorForm; +class BGSFootstepSet; +class SpellItem; +class TESObjectMISC; +class BGSDamageType; +class VirtualMachine; + +typedef TESObjectREFR* (* _PlaceAtMe_Native)(VirtualMachine* vm, UInt32 stackId, TESObjectREFR** target, TESForm* form, SInt32 count, bool bForcePersist, bool bInitiallyDisabled, bool bDeleteWhenAble); +extern RelocAddr<_PlaceAtMe_Native> PlaceAtMe_Native; + +// 20+ +class TESObject : public TESForm +{ +public: + virtual void Unk_48(); + virtual void Unk_49(); + virtual void Unk_4A(); + virtual void Unk_4B(); + virtual void Unk_4C(); + virtual void Unk_4D(); + virtual void Unk_4E(); + virtual void Unk_4F(); + virtual void Unk_50(); + virtual void Unk_51(); + virtual void Unk_52(); + virtual void Unk_53(); +}; + +// 68 +class TESBoundObject : public TESObject +{ +public: + virtual void Unk_54(); + virtual void Unk_55(); + virtual TBO_InstanceData * CloneInstanceData(TBO_InstanceData * other); + virtual void Unk_57(); + virtual void Unk_58(); + virtual void Unk_59(); + virtual void Unk_5A(); + virtual void Unk_5B(); + virtual void Unk_5C(); + virtual void Unk_5D(); + virtual void Unk_5E(); + virtual void Unk_5F(); + virtual void Unk_60(); + virtual void Unk_61(); + virtual void Unk_62(); + virtual void Unk_63(); + virtual void Unk_64(); + + struct Bound + { + UInt16 x; + UInt16 y; + UInt16 z; + }; + Bound bounds1; // 20 + Bound bounds2; // 26 + BGSMod::Template::Items templateItems; // 30 + BGSPreviewTransform previewTransform; // 50 + BGSSoundTagComponent soundTagComponent; // 60 +}; +STATIC_ASSERT(offsetof(TESBoundObject, templateItems) == 0x30); +STATIC_ASSERT(offsetof(TESBoundObject, previewTransform) == 0x50); +STATIC_ASSERT(offsetof(TESBoundObject, soundTagComponent) == 0x60); +STATIC_ASSERT(sizeof(TESBoundObject) == 0x68); + +// 68 +class TESBoundAnimObject : public TESBoundObject +{ +public: + +}; + +// 1B0 +class TESActorBase : public TESBoundAnimObject +{ +public: + virtual void Unk_65(); + virtual void Unk_66(); + virtual void Unk_67(); + virtual void Unk_68(); + + // parents + TESActorBaseData actorData; // 68 + TESContainer container; // D0 + TESSpellList spellList; // E8 + TESAIForm aiForm; // F8 + TESFullName fullName; // 120 - CF 0x20 + ActorValueOwner actorValueOwner; // 130 + BGSDestructibleObjectForm destructibleObjectForm; // 138 + BGSSkinForm skinForm; // 148 + BGSKeywordForm keywords; // 158 + BGSAttackDataForm attackData; // 178 + BGSPerkRankArray perkRankArray; // 188 + BGSPropertySheet propertySheet; // 1A0 +}; +STATIC_ASSERT(sizeof(TESActorBase) == 0x1B0); + +// 308 +class TESNPC : public TESActorBase +{ +public: + enum { kTypeID = kFormType_NPC_ }; + + // parents + TESRaceForm race; // 1B0 - CF 0x2000000 + BGSOverridePackCollection overridePackCollection; // 1C0 + BGSForcedLocRefType forcedLocRefType; // 1F8 + BGSNativeTerminalForm nativeTerminalForm; // 208 + BSTEventSink menuOpenCloseEvent; // 210 + BGSAttachParentArray attachParentArray; // 220 + + struct HeadData + { + HeadData() : hairColor(nullptr), unk08(nullptr), faceTextures(nullptr) { } + + BGSColorForm * hairColor; // 00 + void * unk08; // 08 + BGSTextureSet * faceTextures; // 10 + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) + }; + + UInt32 unk238; // 238 + UInt16 unk23C; // 23C + UInt8 unk23E; // 23E + UInt8 pad23F; // 23F + TESClass * npcClass; // 240 - CF 0x400 + HeadData * headData; // 248 - CF 0x800 + UInt64 unk250; // 250 + TESCombatStyle * combatStyle; // 258 + UInt64 unk260; // 260 + UInt64 unk268; // 268 - CF 0x2000000 + TESNPC * templateNPC; // 270 + float weightThin; // 278 - CF 0x4000 + float weightMuscular; // 27C - CF 0x4000 + float weightLarge; // 280 - CF 0x4000 + float unk284; // 284 + float unk288; // 288 + UInt32 unk28C; // 28C + UInt64 unk290; // 290 + void * unk298; // 298 + UInt64 unk2A0; // 2A0 + UInt64 unk2A8; // 2A8 + BGSOutfit * outfit[2]; // 2B0 - CF 0x40000, 0x80000 + UInt64 unk2C0; // 2C0 + TESFaction * unk2C8; // 2C8 + BGSHeadPart ** headParts; // 2D0 + tArray * morphSetValue; // 2D8 - 5 elements (MRSV) - CF 0x4000 + + // 08 + struct MorphSetData // (MSDK/MSDV) pair + { + UInt32 key; // 00 + float value; // 04 + + bool operator==(const UInt32 a_key) const { return key == a_key; } + operator UInt32() const { return key; } + static inline UInt32 GetHash(const UInt32 * key) + { + UInt32 hash; + CalculateCRC32_32(&hash, *key, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tkey: %08X ", key); + _MESSAGE("\t\tdata: %f", value); + } + }; + + // 28 + struct FaceMorphRegion // (FMRI/FMRS) pair + { + UInt32 index; // 00 + float value[8]; // 04 + + bool operator==(const UInt32 a_key) const { return index == a_key; } + operator UInt32() const { return index; } + static inline UInt32 GetHash(const UInt32 * key) + { + UInt32 hash; + CalculateCRC32_32(&hash, *key, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tkey: %08X ", index); + for(UInt32 i = 0; i < 8; i++) + _MESSAGE("\t\tdata: %f", value[i]); + } + }; + + tHashSet * morphRegionData; // 2E0 - (key links to CharacterCreation::FaceMorphRegion::index) + UInt8 numHeadParts; // 2E8 - CF 0x800 + UInt8 unk2E9; // 2E9 + struct SkinColor + { + UInt8 red; + UInt8 green; + UInt8 blue; + UInt8 alpha; + } skinColor; // - CF 0x800 + UInt8 unk2EE; // 2EE + UInt8 unk2EF; // 2EF + UInt64 unk2F0; // 2F0 - CF 0x800 + tHashSet * morphSetData; // 2F8 - (key links to CharacterCreation::MorphGroup::Preset::index) + tArray * tints; // 300 - CF 0x800 + + MEMBER_FN_PREFIX(TESNPC); + DEFINE_MEMBER_FN(ctor, TESNPC*, 0x005AE800); + DEFINE_MEMBER_FN(HasOverlays, bool, 0x005C0110); + DEFINE_MEMBER_FN(GetOverlayHeadParts, BGSHeadPart**, 0x005C0230); + DEFINE_MEMBER_FN(GetNumOverlayHeadParts, int, 0x005C02E0); + DEFINE_MEMBER_FN(GetSex, SInt64, 0x005A2560); // npc->actorData.unk08 & 1 + DEFINE_MEMBER_FN(ChangeHeadPartRemovePart, void, 0x005B5560, BGSHeadPart *, bool bRemoveExtraParts); + DEFINE_MEMBER_FN(ChangeHeadPart, void, 0x005B9B30, BGSHeadPart *); + DEFINE_MEMBER_FN(GetSkinColorFromTint, void, 0x005B58E0, NiColorA * outColor, BGSCharacterTint::PaletteEntry* paletteEntry, bool allowCustomization); // This function alters the npc's Skin Color values + + void ChangeHeadPart(BGSHeadPart * headPart, bool bRemovePart, bool bRemoveExtraParts); + BGSHeadPart * GetHeadPartByType(UInt32 type, bool bOverlays = false); + + BGSColorForm * GetHairColor(); +}; +STATIC_ASSERT(offsetof(TESNPC, npcClass) == 0x240); +STATIC_ASSERT(offsetof(TESNPC, combatStyle) == 0x258); +STATIC_ASSERT(offsetof(TESNPC, outfit) == 0x2B0); +STATIC_ASSERT(offsetof(TESNPC, tints) == 0x300); +STATIC_ASSERT(sizeof(TESNPC) == 0x308); + +// 1B0 +class TESAmmo : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_AMMO }; + + TESFullName fullName; // 068 + BGSModelMaterialSwap materialSwap; // 078 BGSModelMaterialSwap + TESIcon icon; // 0B8 + BGSMessageIcon messageIcon; // 0C8 + TESValueForm value; // 0E0 + BGSDestructibleObjectForm destructible; // 0F0 + BGSPickupPutdownSounds pickupSounds; // 100 + TESDescription description; // 118 + BGSKeywordForm keywordForm; // 130 + TESWeightForm weight; // 150 + + UInt64 unk160[(0x1B0 - 0x160)/8]; // 160 +}; + +// 300 +class TESObjectWEAP : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_WEAP }; + + // 138 + struct InstanceData : public TBO_InstanceData + { + public: + BGSSoundDescriptorForm * unk10; // 10 BGSSoundDescriptorForm * + UInt64 unk18; // 18 + UInt64 unk20; // 20 + BGSSoundDescriptorForm *unk28; // 28 BGSSoundDescriptorForm * + BGSSoundDescriptorForm * unk30; // 30 BGSSoundDescriptorForm * + BGSSoundDescriptorForm * unk38; // 38 BGSSoundDescriptorForm * + BGSSoundDescriptorForm * unk40; // 40 BGSSoundDescriptorForm * + BGSSoundDescriptorForm * unk48; // 48 BGSSoundDescriptorForm * + UInt64 unk50; // 50 + BGSImpactDataSet * unk58; // 58 BGSImpactDataSet* + TESLevItem * addAmmoList; // 60 TESLevItem * + TESAmmo * ammo; // 68 TESAmmo * + BGSEquipSlot * equipSlot; // 70 BGSEquipSlot* + SpellItem * unk78; // 78 SpellItem* + BGSKeywordForm * keywords; // 80 + BGSAimModel * aimModel; // 88 BGSAimModel * + BGSZoomData * zoomData; // 90 BGSZoomData* + + struct FiringData + { + BGSProjectile * projectileOverride; // 00 + float unk00; // 08 + float leftMotorStrength; // 0C + float rightMotorStrength; // 10 + float duration; // 14 + float unk18; // 18 + float unk1C; // 1C + float sightedTransition; // 20 + UInt32 period; // 24 + UInt32 unk28; // 28 + UInt32 numProjectiles; // 2C + }; + + FiringData * firingData; // 98 + tArray * enchantments; // A0 + tArray * materialSwaps; // A8 + tArray * damageTypes; // B0 + tArray * modifiers; // B8 + float unkC0; // C0 + float reloadSpeed; // C4 + float speed; // C8 + float reach; // CC + float minRange; // D0 + float maxRange; // D4 + float attackDelay; // D8 + float unkD8; // DC + float outOfRangeMultiplier; // E0 + float secondary; // E4 + float critChargeBonus; // E8 + float weight; // EC + float unkEC; // F0 + float actionCost; // F4 + float fullPowerSeconds; // F8 + float minPowerShot; // FC + UInt32 unk100; // 100 + float critDamageMult; // 104 + UInt32 stagger; // 108 + UInt32 value; // 10C + + enum WeaponFlags + { + kFlag_IgnoresNormalResist = 0x0000002, + kFlag_MinorCrime = 0x0000004, + kFlag_ChargingReload = 0x0000008, + kFlag_HideBackpack = 0x0000010, + kFlag_NonHostile = 0x0000040, + kFlag_NPCsUseAmmo = 0x0000200, + kFlag_RepeatableSingleFire = 0x0000800, + kFlag_HasScope = 0x0001000, + kFlag_HoldInputToPower = 0x0002000, + kFlag_Automatic = 0x0004000, + kFlag_CantDrop = 0x0008000, + kFlag_ChargingAttack = 0x0010000, + kFlag_NotUsedInNormalCombat = 0x0020000, + kFlag_BoundWeapon = 0x0040000, + kFlag_SecondaryWeapon = 0x0200000, + kFlag_BoltAction = 0x0400000, + kFlag_NoJamAfterReload = 0x0800000, + kFlag_DisableShells = 0x1000000, + }; + + UInt32 flags; // 110 + UInt32 unk114; // 114 + UInt32 unk118; // 118 + UInt32 unk11C; // 11C + ActorValueInfo * skill; // 120 + ActorValueInfo * damageResist; // 128 + UInt16 ammoCapacity; // 130 + UInt16 baseDamage; // 132 + UInt16 unk134; // 134 + UInt8 accuracyBonus; // 136 + UInt8 unk137; // 137 + }; + + // 150 + struct Data : public InstanceData + { + public: + BGSModelMaterialSwap* swap138; // 138 + UInt64 unk140; // 140 + BGSMod::Attachment::Mod* embeddedMod; // 148 + }; + + TESFullName fullName; // 068 + BGSModelMaterialSwap materialSwap; // 078 BGSModelMaterialSwap + TESIcon icon; // 0B8 + TESEnchantableForm enchantable; // 0C8 TESEnchantableForm + BGSDestructibleObjectForm destructible; // 0E0 BGSDestructibleObjectForm + BGSEquipType equipType; // 0F0 BGSEquipType + BGSPreloadable preloadable; // 100 BGSPreloadable + BGSMessageIcon messageIcon; // 108 BGSMessageIcon + BGSPickupPutdownSounds pickupSounds; // 120 + BGSBlockBashData blockBash; // 128 + BGSKeywordForm keyword; // 150 + TESDescription description; // 170 TESDescription + BGSInstanceNamingRulesForm namingRules; // 188 BGSInstanceNamingRulesForm + Data weapData; // 198 TESObjectWeap::Data + BGSAttachParentArray attachParentArray; // 2E8 BGSAttachParentArray +}; +STATIC_ASSERT(offsetof(TESObjectWEAP, previewTransform) == 0x50); +STATIC_ASSERT(offsetof(TESObjectWEAP, destructible) == 0x0E0); +STATIC_ASSERT(offsetof(TESObjectWEAP::InstanceData, unk114) == 0x114); +STATIC_ASSERT(sizeof(TESObjectWEAP::InstanceData) == 0x138); +STATIC_ASSERT(sizeof(TESObjectWEAP) == 0x300); + + +// 2E0 +class TESObjectARMO : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_ARMO }; + + TESFullName fullName; // 068 + TESRaceForm raceForm; // 078 + TESEnchantableForm enchantable; // 088 + BGSDestructibleObjectForm destructible; // 0A0 + BGSPickupPutdownSounds pickupPutdown; // 0B0 + TESBipedModelForm bipedModel; // 0C8 + BGSEquipType equipType; // 1D0 + BGSBipedObjectForm bipedObject; // 1E0 + BGSBlockBashData blockBash; // 1F0 + BGSKeywordForm keywordForm; // 208 + TESDescription description; // 228 + BGSInstanceNamingRulesForm namingRules; // 240 + // 58 + struct InstanceData : public TBO_InstanceData + { + public: + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + BGSKeywordForm * keywords; // 28 + tArray * damageTypes; // 30 + UInt64 unk38; // 38 + float weight; // 40 + SInt32 pad44; // 44 + UInt32 value; // 48 + UInt32 health; // 4C + UInt32 unk50; // 50 + UInt16 armorRating; // 54 + UInt16 unk56; // 56 + }; + InstanceData instanceData; // 250 - 2A8 ( 592 - 680) + + struct ArmorAddons + { + void * unk00; // 00 + TESObjectARMA * armorAddon; // 08 + }; + + tArray addons; // 2A8 + UInt64 unk2C0; // 2C0 + BGSAttachParentArray parentArray; // 2C8 + +}; +STATIC_ASSERT(sizeof(TESObjectARMO::InstanceData) == 0x58); +STATIC_ASSERT(offsetof(TESObjectARMO, parentArray) == 0x2C8); +STATIC_ASSERT(sizeof(TESObjectARMO) == 0x2E0); + +// 228 +class TESObjectARMA : public TESObject +{ +public: + enum { kTypeID = kFormType_ARMA }; + + TESRaceForm raceForm; // 20 + BGSBipedObjectForm bipedObject; // 30 + UInt64 unk040; // 40 + UInt64 unk048; // 48 + BGSModelMaterialSwap swap50[2]; // 50 + BGSModelMaterialSwap swapD0[2]; // D0 + BGSModelMaterialSwap swap150[2]; // 150 + BGSTextureSet* unk1D0[2]; // 1D0 + BGSListForm* unk1E0; // 1E0 + UInt64 unk1E8; // 1E8 + tArray additionalRaces; // 1F0 + BGSFootstepSet* footstepSet; // 208 + BGSArtObject* art; // 210 + void* unk218; // 218 + void* unk220; // 220 + + // Constructs a node name from the specified armor and actor + bool GetNodeName(char * dest, TESNPC * refr, TESObjectARMO * armor); +}; +STATIC_ASSERT(offsetof(TESObjectARMA, unk220) == 0x220); +STATIC_ASSERT(sizeof(TESObjectARMA) == 0x228); + + +// 350 +class BGSTextureSet : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_TXST }; + + BSTextureSet textureSet; // 68 + + void * unk78; // 78 + TESTexture texture[8]; // 80 + UInt64 unk100[(0x350-0x100)/8]; // 100 +}; +STATIC_ASSERT(sizeof(BGSTextureSet) == 0x350); + + +class MagicItem : public TESBoundObject +{ +public: + TESFullName name; // 68 + BGSKeywordForm keywordForm; // 78 + UnkArray effectItemsProbably; // 98 + UInt64 unk0B0[4]; // B0 +}; +STATIC_ASSERT(offsetof(MagicItem, unk0B0) == 0x0B0); +STATIC_ASSERT(sizeof(MagicItem) == 0x0D0); + +// 1E0 +class AlchemyItem : public MagicItem +{ +public: + enum { kTypeID = kFormType_ALCH }; + + BGSModelMaterialSwap materialSwap; // 0D0 + TESIcon icon; // 110 + BGSMessageIcon msgIcon; // 120 + TESWeightForm weightForm; // 138 + BGSEquipType equipType; // 148 + BGSDestructibleObjectForm destructible; // 158 + BGSPickupPutdownSounds pickupPutdown; // 168 + BGSCraftingUseSound craftingSounds; // 180 + TESDescription description; // 190 + UInt32 unk1A8; // 1A8 + UInt32 unk1AC; // 1AC + UInt64 unk1B0[4]; // 1B0 + TESIcon icon1D0; // 1D0 +}; +STATIC_ASSERT(offsetof(AlchemyItem, icon1D0) == 0x1D0); +STATIC_ASSERT(sizeof(AlchemyItem) == 0x1E0); + +// 100 +class EnchantmentItem : public MagicItem +{ +public: + enum { kTypeID = kFormType_ENCH }; + + UInt64 unk0D0[6]; +}; +STATIC_ASSERT(offsetof(EnchantmentItem, unk0D0) == 0x0D0); +STATIC_ASSERT(sizeof(EnchantmentItem) == 0x100); + + +class SpellItem : public MagicItem +{ +public: + enum { kTypeID = kFormType_SPEL }; + + BGSEquipType equipType; // 0D0 + BGSMenuDisplayObject displayObject; // 0E0 + TESDescription description; // 0F0 + UInt64 unk108[5]; // 108 +}; +STATIC_ASSERT(offsetof(SpellItem, description) == 0x0F0); +STATIC_ASSERT(offsetof(SpellItem, unk108) == 0x108); +STATIC_ASSERT(sizeof(SpellItem) == 0x130); + +// Seemingly unused in the game. No entries in the DataHandler +// 1B8 +class ScrollItem : public SpellItem +{ +public: + enum { kTypeID = kFormType_SCRL }; + + BGSModelMaterialSwap materialSwap; // 130 + BGSDestructibleObjectForm destructible; + BGSPickupPutdownSounds pickupPutdown; + TESWeightForm weight; + TESValueForm value; +}; + +// 188 - only one it seems (Copper Pipe) +class IngredientItem : public MagicItem +{ +public: + enum { kTypeID = kFormType_INGR }; + + BGSModelMaterialSwap materialSwap; // 0D0 + TESIcon icon; // 110 + TESWeightForm weight; // 120 + BGSEquipType equipType; // 130 + BGSDestructibleObjectForm destructible; // 140 + BGSPickupPutdownSounds pickupPutdown; // 150 + TESValueForm value; // 168 + UInt64 unk178; // 178 + UInt64 unk180; // 180 +}; +STATIC_ASSERT(offsetof(IngredientItem, unk180) == 0x180); +STATIC_ASSERT(sizeof(IngredientItem) == 0x188); + + +// 170 +class TESObjectCONT : public TESBoundAnimObject +{ +public: + enum { kTypeID = kFormType_CONT }; + + TESContainer container; // 68 + TESFullName fullName; // 80 + BGSModelMaterialSwap materialSwap; // 90 + TESWeightForm weightForm; // D0 +// TESMagicCasterForm magicCaster; +// TESMagicTargetForm magicTarget; + BGSDestructibleObjectForm destructible; // E0 + BGSOpenCloseForm openClose; // F0 + BGSKeywordForm keywordForm; // F8 + BGSForcedLocRefType forcedLockRefType; // 118 + BGSPropertySheet propertySheet; // 128 + BGSNativeTerminalForm nativeTerminal; // 138 + UInt8 unk148; + UInt8 unk149; + UInt8 pad14A[2]; + BGSSoundDescriptorForm* sound150; + BGSSoundDescriptorForm* sound158; + BGSSoundDescriptorForm* sound160; + UInt64 unk168; +}; +STATIC_ASSERT(offsetof(TESObjectCONT, pad14A) == 0x14A); +STATIC_ASSERT(sizeof(TESObjectCONT) == 0x170); + + +// 140 +class TESObjectACTI : public TESBoundAnimObject +{ +public: + enum { kTypeID = kFormType_ACTI }; + + TESFullName fullName; // 68 + BGSModelMaterialSwap materialSwap; // 78 + // TESMagicTargetForm magicTarget; + BGSDestructibleObjectForm destructible; // B8 + BGSOpenCloseForm openClose; // C8 + BGSKeywordForm keywordForm; // D0 + BGSPropertySheet propertySheet; // F0 + BGSForcedLocRefType forcedLocRefType; // 100 + BGSNativeTerminalForm nativeTerminalForm; // 110 + UInt64 unk120; // 120 + UInt64 unk128; // 128 + UInt64 unk130; + UInt8 unk138; + UInt8 pad13C[3]; +}; +STATIC_ASSERT(sizeof(TESObjectACTI) == 0x140); + +// 1A0 +class TESFurniture : public TESObjectACTI +{ +public: + enum { kTypeID = kFormType_FURN }; + +// BGSNavmeshableObject navMeshableObject; + UInt64 unk140; // 140 + + struct Data { + UInt32 unk00; + UInt32 unk04; + }; + + tArray arr148; + //UInt64 unk148; + //UInt32 unk150; + //UInt32 unk154; + //UInt32 unk158; + //UInt32 unk15C; + BGSAttachParentArray unk160; + UInt8 unk178; + float unk17C; + UInt64 unk180; + + struct Data2 + { + UInt64 unk00[4]; + }; + + tArray arr188; // struct { UInt64 data[4]; } + //void* unk188; + //UInt32 unk190; + //UInt32 unk194; + //UInt32 unk198; + //UInt32 unk19C; +}; +STATIC_ASSERT(offsetof(TESFurniture, arr188) == 0x188); +STATIC_ASSERT(sizeof(TESFurniture) == 0x1A0); + +// 1E0 +class BGSTerminal : public TESFurniture +{ +public: + enum { kTypeID = kFormType_TERM }; + + UnkArray arr1A0; + //void* unk1A0; /// 1A0 + //UInt32 unk1A8; + //UInt32 unk1AC; + //UInt32 unk1B0; + //UInt32 unk1B4; + UnkArray arr1B8; + //void* unk1B8; + //UInt32 unk1C0; + //UInt32 unk1C4; + //UInt32 unk1C8; + //UInt32 unk1CC; + void* unk1D0; + void* unk1D8; +}; +STATIC_ASSERT(offsetof(BGSTerminal, arr1B8) == 0x1B8); +STATIC_ASSERT(sizeof(BGSTerminal) == 0x1E0); + + +// 90 +class BGSBendableSpline : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_BNDS }; + + UInt64 unk68[(0x90 - 0x68) >> 3]; +}; +STATIC_ASSERT(sizeof(BGSBendableSpline) == 0x90); + +// A8 +class BGSComponent : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_CMPO }; + + // parents + TESFullName fullName; // 68 + TESValueForm value; // 78 + BGSCraftingUseSound craftingSounds; // 88 + + TESObjectMISC * scrapItem; // 98 + TESGlobal * scrapScalar; // A0 +}; +STATIC_ASSERT(sizeof(BGSComponent) == 0xA8); + +// 168 +class TESObjectMISC : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_MISC }; + + // parents + TESFullName fullName; // 68 + BGSModelMaterialSwap materialSwap; // 78 + TESIcon icon; // B8 + TESValueForm value; // C8 + TESWeightForm weight; // D8 + BGSDestructibleObjectForm destructible; // E8 + BGSMessageIcon messageIcon; // F8 + BGSPickupPutdownSounds pickupPutdown; // 110 + BGSKeywordForm keywordForm; // 128 + BGSFeaturedItemMessage featuredMessage; // 148 + + struct Component + { + BGSComponent * component; + UInt64 count; + }; + + tArray * components; // 158 + UInt64 unk160; // 160 +}; +STATIC_ASSERT(sizeof(TESObjectMISC) == 0x168); + +// 188 +class BGSProjectile : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_PROJ }; + + TESFullName fullName; // 68 + TESModel model; // 78 + BGSPreloadable preloadable; // A8 + BGSDestructibleObjectForm destructible; // B0 + + UInt64 unkC0[(0x188 - 0xC0) >> 3]; +}; + +// D8 +class TESLevCharacter : public TESBoundAnimObject +{ +public: + enum { kTypeID = kFormType_LVLN }; + + TESLeveledList leveledList; // 68 + BGSModelMaterialSwap matSwap; // 98 +}; +STATIC_ASSERT(sizeof(TESLevCharacter) == 0xD8); + +// 98 +class TESLevItem : public TESBoundObject +{ +public: + enum { kTypeID = kFormType_LVLI }; + + TESLeveledList leveledList; // 68 +}; +STATIC_ASSERT(sizeof(TESLevItem) == 0x98); diff --git a/f4se/f4se/GameRTTI.cpp b/f4se/f4se/GameRTTI.cpp new file mode 100644 index 0000000..850e089 --- /dev/null +++ b/f4se/f4se/GameRTTI.cpp @@ -0,0 +1,17 @@ +#include "GameRTTI.h" +#include "f4se_common/Relocation.h" + +typedef void * (* _Runtime_DynamicCast_Internal)(void * srcObj, UInt32 arg1, const void * fromType, const void * toType, UInt32 arg4); + +// 11BCFFABF53E33EAC4BAE470FD237D36B63F868A+ED +RelocAddr <_Runtime_DynamicCast_Internal> Runtime_DynamicCast_Internal(0x02936C62); // __RTDynamicCast + +void * Runtime_DynamicCast(void * srcObj, const void * fromType, const void * toType) +{ + uintptr_t fromTypeAddr = uintptr_t(fromType) + RelocationManager::s_baseAddr; + uintptr_t toTypeAddr = uintptr_t(toType) + RelocationManager::s_baseAddr; + + return Runtime_DynamicCast_Internal(srcObj, 0, (void *)fromTypeAddr, (void *)toTypeAddr, 0); +} + +#include "GameRTTI.inl" diff --git a/f4se/f4se/GameRTTI.h b/f4se/f4se/GameRTTI.h new file mode 100644 index 0000000..3958301 --- /dev/null +++ b/f4se/f4se/GameRTTI.h @@ -0,0 +1,3981 @@ +#pragma once + +void * Runtime_DynamicCast(void * srcObj, const void * fromType, const void * toType); + +#define DYNAMIC_CAST(obj, from, to) ( ## to *) Runtime_DynamicCast((void*)(obj), RTTI_ ## from, RTTI_ ## to) + +extern const void * RTTI_IAIWorldLocation; +extern const void * RTTI_BGSAIWorldLocation; +extern const void * RTTI_BGSAIWorldLocationInteriorCell; +extern const void * RTTI_BGSAIWorldLocationPointRadius; +extern const void * RTTI_BGSAIWorldLocationPrimitive; +extern const void * RTTI_FindTriangleForLocationFilter; +extern const void * RTTI_FindTriangleForLocationFilterCheckDeltaZ; +extern const void * RTTI_BGSAIWorldLocationRefRadius; +extern const void * RTTI_BSTArrayBase__IAllocatorFunctor; +extern const void * RTTI_IPackageDataTypeCheck; +extern const void * RTTI_IProcedureTreeExecState; +extern const void * RTTI_IProcedure; +extern const void * RTTI_BGSProcedureBase; +extern const void * RTTI_BGSProcedureHeadtrack; +extern const void * RTTI_BGSProcedureHeadtrackExecState; +extern const void * RTTI_BGSProcedurePlayIdle; +extern const void * RTTI_BGSProcedurePlayIdleExecState; +extern const void * RTTI_TESActionData; +extern const void * RTTI_BGSActionData; +extern const void * RTTI_Setting; +extern const void * RTTI_INISettingCollection; +extern const void * RTTI_hkBaseObject; +extern const void * RTTI_hknpCollisionQueryCollector; +extern const void * RTTI_hknpClosestHitCollector; +extern const void * RTTI_GameSettingCollection; +extern const void * RTTI_IMovementParameters; +extern const void * RTTI_MovementParameters; +extern const void * RTTI_BGSProcedureRange; +extern const void * RTTI_BGSProcedureRangeExecState; +extern const void * RTTI_BGSProcedureStayAway; +extern const void * RTTI_BGSProcedureStayAwayExecState; +extern const void * RTTI_IAnimationEventDataForEachFunctor; +extern const void * RTTI_BGSRetargetOnDeleteExtraData; +extern const void * RTTI_IForEachDynamicIdleInDir; +extern const void * RTTI_DynamicIdleDataSingletonHelper; +extern const void * RTTI_BGSObjectInstanceExtra; +extern const void * RTTI_BSExtraData; +extern const void * RTTI_ExtraInitActions; +extern const void * RTTI_BaseFormComponent; +extern const void * RTTI_BGSAttachParentArray; +extern const void * RTTI_BGSCraftingUseSound; +extern const void * RTTI_BGSFeaturedItemMessage; +extern const void * RTTI_BGSForcedLocRefType; +extern const void * RTTI_BGSInstanceNamingRulesForm; +extern const void * RTTI_BGSMod__Template__Items; +extern const void * RTTI_TESFullName; +extern const void * RTTI_BGSMod__Template__Item; +extern const void * RTTI_BGSModelMaterialSwap; +extern const void * RTTI_BGSNativeTerminalForm; +extern const void * RTTI_BGSPreviewTransform; +extern const void * RTTI_BGSPropertySheet; +extern const void * RTTI_BGSSoundTagComponent; +extern const void * RTTI_InitActionI; +extern const void * RTTI_BGSConveyorBelt__ConveyorBelt; +extern const void * RTTI_hkReferencedObject; +extern const void * RTTI_hknpAction; +extern const void * RTTI_hknpUnaryAction; +extern const void * RTTI_BGSMotorAction__Action; +extern const void * RTTI_BGSMotorAction__FanMotor; +extern const void * RTTI_NiRefObject; +extern const void * RTTI_hknpPhysicsSystemData; +extern const void * RTTI_CellHfCollision; +extern const void * RTTI_hknpCollisionFilter; +extern const void * RTTI_hknpAllHitsCollector; +extern const void * RTTI_hknpBSCustomCollisionFilter; +extern const void * RTTI_hknpFlippedGetClosestPointsQueryCollector; +extern const void * RTTI_IFormFactory; +extern const void * RTTI_AlchemyItem; +extern const void * RTTI_BGSDualCastData; +extern const void * RTTI_EffectArchetypes__MainStatusHandler; +extern const void * RTTI_EffectSetting; +extern const void * RTTI_EnchantmentItem; +extern const void * RTTI_IngredientItem; +extern const void * RTTI_BSModelDB__QueuedHandles; +extern const void * RTTI_AnimationSystemUtils__TESModelAndAnimationHandles; +extern const void * RTTI_TESModelDB__TESQueuedHandles; +extern const void * RTTI_MagicItem; +extern const void * RTTI_MagicItem__PreloadableVisitor; +extern const void * RTTI_MagicItemTraversalFunctor; +extern const void * RTTI_MagicItemFindFunctor; +extern const void * RTTI_MagicItemFindKeywordFunctor; +extern const void * RTTI_FindNonExcludedDeliveryFunctor; +extern const void * RTTI_FindEqualsFunctor; +extern const void * RTTI_GetCostliestEffectFunctor; +extern const void * RTTI_LongestDurationFunctor; +extern const void * RTTI_LargestAreaFunctor; +extern const void * RTTI_GetMagicItemDescriptionFunctor; +extern const void * RTTI_ScrollItem; +extern const void * RTTI_SpellItem; +extern const void * RTTI_ExtraCell3D; +extern const void * RTTI_ExtraHavok; +extern const void * RTTI_ExtraRegionList; +extern const void * RTTI_ExtraCellSkyRegion; +extern const void * RTTI_ExtraCellImageSpace; +extern const void * RTTI_ExtraCellGodRays; +extern const void * RTTI_ExtraSeenData; +extern const void * RTTI_ExtraNorthRotation; +extern const void * RTTI_ExtraDetachTime; +extern const void * RTTI_ExtraProcessMiddleLow; +extern const void * RTTI_ExtraCellMusicType; +extern const void * RTTI_ExtraCellAcousticSpace; +extern const void * RTTI_ExtraCellWaterType; +extern const void * RTTI_TESObjectREFRDef__IAliasFunctor; +extern const void * RTTI_TESObjectREFRDef__ILinkedReferenceFunctor; +extern const void * RTTI_ExtraCulledBone; +extern const void * RTTI_ExtraLock; +extern const void * RTTI_ExtraTeleport; +extern const void * RTTI_ExtraStartingPosition; +extern const void * RTTI_ExtraOwnership; +extern const void * RTTI_ExtraGlobal; +extern const void * RTTI_ExtraRank; +extern const void * RTTI_ExtraCount; +extern const void * RTTI_ExtraLeveledItem; +extern const void * RTTI_ExtraOutfitItem; +extern const void * RTTI_ExtraHealth; +extern const void * RTTI_ExtraTimeLeft; +extern const void * RTTI_ExtraCharge; +extern const void * RTTI_ExtraScale; +extern const void * RTTI_ExtraFollower; +extern const void * RTTI_ExtraOriginalReference; +extern const void * RTTI_ExtraPoison; +extern const void * RTTI_ExtraHeadingTarget; +extern const void * RTTI_ExtraCreatureAwakeSound; +extern const void * RTTI_ExtraObjectHealth; +extern const void * RTTI_ExtraActorCause; +extern const void * RTTI_ExtraRadioData; +extern const void * RTTI_ExtraPatrolRefData; +extern const void * RTTI_ExtraNavMeshPortal; +extern const void * RTTI_ExtraOcclusionPlaneRefData; +extern const void * RTTI_ExtraPortalRefData; +extern const void * RTTI_ExtraSceneData; +extern const void * RTTI_ExtraBadPosition; +extern const void * RTTI_ExtraHeadTrackingWeight; +extern const void * RTTI_ExtraFavorCost; +extern const void * RTTI_ExtraTextDisplayData; +extern const void * RTTI_ExtraHorse; +extern const void * RTTI_ExtraEnchantment; +extern const void * RTTI_ExtraForcedTarget; +extern const void * RTTI_ExtraUniqueID; +extern const void * RTTI_ExtraFlags; +extern const void * RTTI_ExtraWaterCurrentZoneData; +extern const void * RTTI_ExtraMissingRefIDs; +extern const void * RTTI_ExtraRangedDistOverride; +extern const void * RTTI_ExtraSoundOutputOverride; +extern const void * RTTI_ExtraEditorID; +extern const void * RTTI_ExtraFavorite; +extern const void * RTTI_ExtraPrimitive; +extern const void * RTTI_ExtraAmmo; +extern const void * RTTI_ExtraCombinedRefs; +extern const void * RTTI_ExtraPreVisRefs; +extern const void * RTTI_ExtraTransitionCellCount; +extern const void * RTTI_ExtraGIDBuffer; +extern const void * RTTI_ExtraFromAlias; +extern const void * RTTI_ExtraOpenCloseActivateRef; +extern const void * RTTI_ExtraTeleportName; +extern const void * RTTI_ExtraCachedScale; +extern const void * RTTI_ExtraReferenceHandles; +extern const void * RTTI_ExtraLocation; +extern const void * RTTI_ExtraTresPassPackage; +extern const void * RTTI_ExtraAttachRef; +extern const void * RTTI_ExtraAttachRefChildren; +extern const void * RTTI_ExtraPowerLinks; +extern const void * RTTI_ExtraReflectedRefs; +extern const void * RTTI_ExtraReflectorRefs; +extern const void * RTTI_ExtraWaterLightRefs; +extern const void * RTTI_ExtraLitWaterRefs; +extern const void * RTTI_ExtraHasNoRumors; +extern const void * RTTI_ExtraModelSwap; +extern const void * RTTI_ExtraRadius; +extern const void * RTTI_ExtraCombatStyle; +extern const void * RTTI_ExtraPackageData; +extern const void * RTTI_ExtraCollisionData; +extern const void * RTTI_ExtraLargeRefOwnerCells; +extern const void * RTTI_ExtraLightData; +extern const void * RTTI_ExtraModRank; +extern const void * RTTI_ExtraAttachedArrows3D; +extern const void * RTTI_ExtraAlphaCutoff; +extern const void * RTTI_ExtraForcedLandingMarker; +extern const void * RTTI_ExtraCellWaterEnvMap; +extern const void * RTTI_ExtraKeywords; +extern const void * RTTI_ExtraMaterialSwap; +extern const void * RTTI_ExtraProjectedDecalData; +extern const void * RTTI_ExtraActivateText; +extern const void * RTTI_ExtraRadioReceiver; +extern const void * RTTI_ExtraRadioRepeater; +extern const void * RTTI_ExtraAnimSounds; +extern const void * RTTI_ExtraActorValueStorage; +extern const void * RTTI_ExtraObjectBreakable; +extern const void * RTTI_ExtraObjectSavedDynamicIdles; +extern const void * RTTI_ExtraObjectSavedUnrecoverableSubgraphs; +extern const void * RTTI_ExtraIgnoredAttractKeywords; +extern const void * RTTI_ExtraInteriorLODWorldspace; +extern const void * RTTI_ExtraBoneScaleMap; +extern const void * RTTI_ExtraFXPickNodes; +extern const void * RTTI_ExtraEssentialProtected; +extern const void * RTTI_ExtraInvestedGold; +extern const void * RTTI_ExtraVoiceType; +extern const void * RTTI_Workshop__ExtraData; +extern const void * RTTI_NonActorMagicCaster; +extern const void * RTTI_ExtraAnimGraphManager; +extern const void * RTTI_ExtraDismemberedLimbs; +extern const void * RTTI_ExtraBiped; +extern const void * RTTI_ExtraLight; +extern const void * RTTI_ExtraLeveledCreature; +extern const void * RTTI_ExtraMapMarker; +extern const void * RTTI_ExtraAction; +extern const void * RTTI_ExtraShouldWear; +extern const void * RTTI_ExtraSeed; +extern const void * RTTI_ExtraPackage; +extern const void * RTTI_ExtraPlayerCrimeList; +extern const void * RTTI_ExtraRunOncePacks; +extern const void * RTTI_ExtraDistantData; +extern const void * RTTI_ExtraEnableStateParent; +extern const void * RTTI_ExtraEnableStateChildren; +extern const void * RTTI_ExtraLinkedRef; +extern const void * RTTI_ExtraLinkedRefChildren; +extern const void * RTTI_ExtraLocationRefType; +extern const void * RTTI_ExtraActivateRef; +extern const void * RTTI_ExtraActivateRefChildren; +extern const void * RTTI_ExtraRandomTeleportMarker; +extern const void * RTTI_ExtraCannotWear; +extern const void * RTTI_ExtraLastFinishedSequence; +extern const void * RTTI_ExtraGhost; +extern const void * RTTI_ExtraItemDropper; +extern const void * RTTI_ExtraDroppedItemList; +extern const void * RTTI_ExtraSavedAnimation; +extern const void * RTTI_ExtraSavedHavokData; +extern const void * RTTI_ExtraRefractionProperty; +extern const void * RTTI_ExtraSound; +extern const void * RTTI_ExtraCreatureMovementSound; +extern const void * RTTI_ExtraActivateLoopSound; +extern const void * RTTI_ExtraFactionChanges; +extern const void * RTTI_ExtraSayTopicInfoOnceADay; +extern const void * RTTI_ExtraEncounterZone; +extern const void * RTTI_ExtraRoomRefData; +extern const void * RTTI_ExtraPatrolRefInUseData; +extern const void * RTTI_ExtraFollowerSwimBreadcrumbs; +extern const void * RTTI_ExtraAliasInstanceArray; +extern const void * RTTI_ExtraDecalGroup; +extern const void * RTTI_ExtraCanTalkToPlayer; +extern const void * RTTI_ExtraSayToTopicInfo; +extern const void * RTTI_ExtraInteraction; +extern const void * RTTI_ExtraLockList; +extern const void * RTTI_ExtraSoul; +extern const void * RTTI_ExtraPackageStartLocation; +extern const void * RTTI_ExtraMasterFileCell; +extern const void * RTTI_ExtraMasterLocation; +extern const void * RTTI_ExtraPersistentCell; +extern const void * RTTI_ExtraRagDollData; +extern const void * RTTI_ExtraUsedMarkers; +extern const void * RTTI_ExtraReservedMarkers; +extern const void * RTTI_ExtraAshPileRef; +extern const void * RTTI_ExtraLeveledItemBase; +extern const void * RTTI_ExtraLevCreaModifier; +extern const void * RTTI_ExtraSpawnContainer; +extern const void * RTTI_ExtraAcousticParent; +extern const void * RTTI_ExtraEmittanceSource; +extern const void * RTTI_ExtraMultiBoundData; +extern const void * RTTI_ExtraMultiBoundRef; +extern const void * RTTI_ExtraMultiBound; +extern const void * RTTI_ExtraPortal; +extern const void * RTTI_ExtraRoom; +extern const void * RTTI_ExtraOcclusionShape; +extern const void * RTTI_ExtraFriendHits; +extern const void * RTTI_ExtraStartingWorldOrCell; +extern const void * RTTI_ExtraEditorRef3DData; +extern const void * RTTI_ExtraEditorRefMoveData; +extern const void * RTTI_ExtraGuardedRefData; +extern const void * RTTI_ExtraIgnoredBySandbox; +extern const void * RTTI_ExtraPromotedRef; +extern const void * RTTI_ExtraAnimationSequencer; +extern const void * RTTI_ExtraRefrPath; +extern const void * RTTI_ExtraCellGrassData; +extern const void * RTTI_ExtraWaterData; +extern const void * RTTI_ExtraGroupConstraint; +extern const void * RTTI_ExtraScriptedAnimDependence; +extern const void * RTTI_ExtraRaceData; +extern const void * RTTI_ExtraMagicCaster; +extern const void * RTTI_ExtraBendableSplineParams; +extern const void * RTTI_ExtraInstanceData; +extern const void * RTTI_ExtraPowerArmor; +extern const void * RTTI_ExtraPowerArmorPreload; +extern const void * RTTI_ExtraInputEnableLayer; +extern const void * RTTI_ExtraDirectAtTarget; +extern const void * RTTI_ExtraRefWeaponSounds; +extern const void * RTTI_ExtraAnimGraphPreload; +extern const void * RTTI_ExtraFurnitureEntryData; +extern const void * RTTI_BSTextureDB__QueuedHandlesWithCallback; +extern const void * RTTI_QueuedActor; +extern const void * RTTI_QueuedCharacter; +extern const void * RTTI_QueuedHead; +extern const void * RTTI_AttachDistant3DTask; +extern const void * RTTI_QueuedModel; +extern const void * RTTI_QueuedReference; +extern const void * RTTI_QueuedAnimationObject; +extern const void * RTTI_BackgroundProcessThread; +extern const void * RTTI_Scaleform__SysAllocBase; +extern const void * RTTI_Scaleform__SysAlloc; +extern const void * RTTI_Scaleform__SysAllocMalloc; +extern const void * RTTI_Scaleform__AcquireInterface; +extern const void * RTTI_QueuedTree; +extern const void * RTTI_QueuedPlayer; +extern const void * RTTI_INIPrefSettingCollection; +extern const void * RTTI_TES; +extern const void * RTTI_AddCellGrassTask; +extern const void * RTTI_BGSLoadFormBuffer; +extern const void * RTTI_BGSStoryManagerBranchNode; +extern const void * RTTI_NEW_REFR_DATA; +extern const void * RTTI_TESDataHandler__ArchiveRegistrationListener; +extern const void * RTTI_TESDataHandler__PSGRegistrationListener; +extern const void * RTTI_BGSStoryManagerTreeForm; +extern const void * RTTI_BGSStoryManagerNodeBase; +extern const void * RTTI_BSModelDB__BSModelProcessor; +extern const void * RTTI_BGSAttackDataForm; +extern const void * RTTI_BGSBipedObjectForm; +extern const void * RTTI_BGSBlockBashData; +extern const void * RTTI_BGSDestructibleObjectForm; +extern const void * RTTI_BGSEquipType; +extern const void * RTTI_BGSIdleCollection; +extern const void * RTTI_IKeywordFormBase; +extern const void * RTTI_BGSKeywordForm; +extern const void * RTTI_BGSMenuDisplayObject; +extern const void * RTTI_BGSMessageIcon; +extern const void * RTTI_BGSOverridePackCollection; +extern const void * RTTI_PerkRankVisitor; +extern const void * RTTI_BGSPerkRankArray; +extern const void * RTTI_BGSPickupPutdownSounds; +extern const void * RTTI_BGSSkinForm; +extern const void * RTTI_TESActorBaseData; +extern const void * RTTI_TESAIForm; +extern const void * RTTI_TESAttackDamageForm; +extern const void * RTTI_TESModelRDT; +extern const void * RTTI_TESBipedModelForm; +extern const void * RTTI_TESContainer; +extern const void * RTTI_TESDescription; +extern const void * RTTI_TESEnchantableForm; +extern const void * RTTI_TESForm; +extern const void * RTTI_IFormFactory__IFactoryVisitor; +extern const void * RTTI_BSStorage; +extern const void * RTTI_BSMemStorage; +extern const void * RTTI_BSVMLoadTask; +extern const void * RTTI_TESHealthForm; +extern const void * RTTI_TESIcon; +extern const void * RTTI_TESImageSpaceModifiableForm; +extern const void * RTTI_TESLeveledList; +extern const void * RTTI_TESModel; +extern const void * RTTI_TESProduceForm; +extern const void * RTTI_TESRaceForm; +extern const void * RTTI_TESReactionForm; +extern const void * RTTI_TESSpellList; +extern const void * RTTI_TESTexture; +extern const void * RTTI_TESValueForm; +extern const void * RTTI_TESWeightForm; +extern const void * RTTI_ActionInput; +extern const void * RTTI_BSAttachTechniques__BSAttachTechnique; +extern const void * RTTI_BGSAttachTechniquesUtil__ProcessTechniquesFunctor; +extern const void * RTTI_BGSAttachTechniquesUtil__AttachTechniquesFunctor; +extern const void * RTTI_BGSAttachTechniquesUtil__DetachTechniquesFunctor; +extern const void * RTTI_BGSParticleArrayAttach; +extern const void * RTTI_BGSParticleArrayAttach__EmitterPolicy; +extern const void * RTTI_BGSParticleArrayAttach__CollectEmitterPolicy; +extern const void * RTTI_BGSParticleArrayAttach__ClearEmitterPolicy; +extern const void * RTTI_BGSHavokGeometryAttach; +extern const void * RTTI_BGSHavokGeometryAttach__ActionPolicy; +extern const void * RTTI_BGSHavokGeometryAttach__AttachPolicy; +extern const void * RTTI_BGSHavokGeometryAttach__DetachPolicy; +extern const void * RTTI_BGSNamedNodeAttach; +extern const void * RTTI_BGSNamedNodeAttach__ActionPolicy; +extern const void * RTTI_BGSNamedNodeAttach__AttachPolicy; +extern const void * RTTI_BGSNamedNodeAttach__DetachPolicy; +extern const void * RTTI_BGSMultiTechniqueAttach; +extern const void * RTTI_BGSMultiTechniqueAttach__ActionPolicy; +extern const void * RTTI_BGSMultiTechniqueAttach__AttachPolicy; +extern const void * RTTI_BGSMultiTechniqueAttach__DetachPolicy; +extern const void * RTTI_ITempEffectFactory; +extern const void * RTTI_BSMultiBoundAABB; +extern const void * RTTI_BSMultiBoundOBB; +extern const void * RTTI_BGSDecalNode; +extern const void * RTTI_QueuedPromoteReferencesTask; +extern const void * RTTI_BSResource__Location; +extern const void * RTTI_BSResource__LooseFileLocation; +extern const void * RTTI_BGSQueuedGrassModelHandles; +extern const void * RTTI_hknpShape; +extern const void * RTTI_hknpConvexShape; +extern const void * RTTI_hknpSphereShape; +extern const void * RTTI_IRaceSubGraphDataFunctor; +extern const void * RTTI_InteractionCombatDataCollectFunctor; +extern const void * RTTI_BGSInventoryItem__StackDataCompareFunctor; +extern const void * RTTI_BGSInventoryItem__CheckStackIDFunctor; +extern const void * RTTI_BGSInventoryItem__CheckExtraDataFunctor; +extern const void * RTTI_BGSInventoryItem__StackDataWriteFunctor; +extern const void * RTTI_BGSInventoryItem__ModCountFunctor; +extern const void * RTTI_SetPlayerHasTakenStackFunctor; +extern const void * RTTI_RemoveExtraFunctor; +extern const void * RTTI_BGSInventoryItem__Stack; +extern const void * RTTI_BGSInventoryItem__FindEquippedStackFunctor; +extern const void * RTTI_BGSInventoryItem__IsUIEquivalentStackFunctor; +extern const void * RTTI_BGSInventoryItem__SetFlagFunctor; +extern const void * RTTI_MatchEquippedAndFavoriteDataFunctor; +extern const void * RTTI_IsFavoriteFunctor; +extern const void * RTTI_SetFavoriteFunctor; +extern const void * RTTI_RemoveFavoriteFunctor; +extern const void * RTTI_SetPoisonFunctor; +extern const void * RTTI_SetEnchantmentFunctor; +extern const void * RTTI_BGSInventoryItem__HasExtraDataFunctor; +extern const void * RTTI_RefMatchFunctor; +extern const void * RTTI_RemoveQuestAliasFunctor; +extern const void * RTTI_SetTimeLeftFunctor; +extern const void * RTTI_ModTimeLeftFunctor; +extern const void * RTTI_ProcessWeaponStrikeMagicFunctor; +extern const void * RTTI_SetItemOwnershipFunctor; +extern const void * RTTI_SetItemOriginalReferenceFunctor; +extern const void * RTTI_BSConnectPoint__ChildOrigin; +extern const void * RTTI_BGSObjectVisibilityManager; +extern const void * RTTI_BSPortal; +extern const void * RTTI_BGSPrimitiveLine; +extern const void * RTTI_BGSPrimitivePlane; +extern const void * RTTI_BGSPrimitiveBox; +extern const void * RTTI_BGSPrimitiveSphere; +extern const void * RTTI_BGSPrimitiveEllipsoid; +extern const void * RTTI_BGSPrimitive; +extern const void * RTTI_BSMultiBoundSphere; +extern const void * RTTI_ReferenceEffectController; +extern const void * RTTI_OwnedController; +extern const void * RTTI_BSAttachRefController; +extern const void * RTTI_BSCloneReserver; +extern const void * RTTI_NiProperty; +extern const void * RTTI_NiAlphaProperty; +extern const void * RTTI_GridArray; +extern const void * RTTI_GridCellArray; +extern const void * RTTI_BSExternalAudioIO__ExternalIOInterface; +extern const void * RTTI_LipSynchAnimDB__LipAudioInterface; +extern const void * RTTI_BSInputEventUser; +extern const void * RTTI_TESCameraState; +extern const void * RTTI_LocalMapCamera; +extern const void * RTTI_LocalMapCamera__DefaultState; +extern const void * RTTI_BSSplatterExtraData; +extern const void * RTTI_hknpConvexPolytopeShape; +extern const void * RTTI_hknpCapsuleShape; +extern const void * RTTI_IMessageBoxCallback; +extern const void * RTTI_ExamineConfirmMenu__ICallback; +extern const void * RTTI_hknpUniqueBodyIdHitCollector; +extern const void * RTTI_hknpAnyHitCollector; +extern const void * RTTI_hknpFlippedShapeCastQueryCollector; +extern const void * RTTI_Workshop__hknpWorkshopCastCollector; +extern const void * RTTI_Workshop__CheckBuildRadiusCollector; +extern const void * RTTI_Workshop__hknpCheckShelterHitsCollector; +extern const void * RTTI_Workshop__RequestScrapCallback; +extern const void * RTTI_Workshop__RequestStoreCallback; +extern const void * RTTI_Workshop__RequestRepairCallback; +extern const void * RTTI_Workshop__hknpWorkshopIntersectCollector; +extern const void * RTTI_Workshop__hknpClosestConnectPointCollector; +extern const void * RTTI_Workshop__hknpCountIntersectingRefsCollector; +extern const void * RTTI_Workshop__ForwardCastCollector; +extern const void * RTTI_Workshop__GroundIntersectCollector; +extern const void * RTTI_Workshop__MainStatusHandler; +extern const void * RTTI_Workshop__WorkshopReferenceEventSink; +extern const void * RTTI_std__error_category; +extern const void * RTTI_std___Generic_error_category; +extern const void * RTTI_std___Iostream_error_category; +extern const void * RTTI_NVFlex__DebrisInstanceManager; +extern const void * RTTI_NVFlex__DebrisNode; +extern const void * RTTI_NVFlex__DebrisRenderer; +extern const void * RTTI_NVFlex__FlexThread; +extern const void * RTTI_hkGeometry; +extern const void * RTTI_MovementHandlerAgentAnimationDriven; +extern const void * RTTI_MovementHandlerAgentGraphDrivenAnimationDriven; +extern const void * RTTI_IPipelineStageInterface; +extern const void * RTTI_IMovementTweenerAgent; +extern const void * RTTI_IMovementInterface; +extern const void * RTTI_IMovementQueryDesiredDeltas; +extern const void * RTTI_IMovementSetPlayerControls; +extern const void * RTTI_MovementHandlerAgentPlayerControls; +extern const void * RTTI_NavMeshObstacleCoverManager; +extern const void * RTTI_BSPathingStreamRead; +extern const void * RTTI_PathingStreamMasterFileRead; +extern const void * RTTI_QuestPathingRequest; +extern const void * RTTI_IPathBuilderFactoryBase; +extern const void * RTTI_TESRegion; +extern const void * RTTI_TESRegionData; +extern const void * RTTI_TESRegionDataGrass; +extern const void * RTTI_TESRegionDataLandscape; +extern const void * RTTI_TESRegionDataManager; +extern const void * RTTI_TESRegionDataMap; +extern const void * RTTI_TESRegionDataObjects; +extern const void * RTTI_TESRegionDataSound; +extern const void * RTTI_TESRegionDataWeather; +extern const void * RTTI_TESRegionObjectBase; +extern const void * RTTI_TESRegionGrassObject; +extern const void * RTTI_TESRegionGrassObjectList; +extern const void * RTTI_TESRegionList; +extern const void * RTTI_TESRegionNoiseFunction; +extern const void * RTTI_TESRegionObject; +extern const void * RTTI_TESRegionObjectList; +extern const void * RTTI_BGSAttractionRule; +extern const void * RTTI_BGSCharacterTint__Entry; +extern const void * RTTI_BGSCharacterTint__MaskEntry; +extern const void * RTTI_BGSCharacterTint__PaletteEntry; +extern const void * RTTI_BGSCharacterTint__Template__Entry; +extern const void * RTTI_BGSCharacterTint__Template__Mask; +extern const void * RTTI_BGSCharacterTint__Template__Palette; +extern const void * RTTI_BGSCharacterTint__TextureSetEntry; +extern const void * RTTI_BGSCharacterTint__Template__TextureSet; +extern const void * RTTI_BGSRefCollectionAlias; +extern const void * RTTI_BSShaderMaterialExtraData; +extern const void * RTTI_BSDismembermentLimbExtraData; +extern const void * RTTI_BSDismembermentExtraData; +extern const void * RTTI_BGSSceneAction; +extern const void * RTTI_BGSSceneActionConversationBase; +extern const void * RTTI_BGSSceneActionPlayerDialogue; +extern const void * RTTI_BGSSceneActionStartScene; +extern const void * RTTI_BSIAudioEffectChain; +extern const void * RTTI_BGSAudioEffectChain; +extern const void * RTTI_BSISoundDescriptor; +extern const void * RTTI_BGSSoundDescriptor; +extern const void * RTTI_BGSAutoWeaponSoundDef; +extern const void * RTTI_BGSComponent; +extern const void * RTTI_BGSCompoundSoundDef; +extern const void * RTTI_BGSDamageType; +extern const void * RTTI_BGSDefaultObject; +extern const void * RTTI_BGSInstanceNamingRules; +extern const void * RTTI_BGSMaterialSwap; +extern const void * RTTI_BGSSoundCategorySnapshot; +extern const void * RTTI_BGSSoundKeywordMapping; +extern const void * RTTI_BGSSoundTagSet; +extern const void * RTTI_BGSTransform; +extern const void * RTTI_BGSAddonNode; +extern const void * RTTI_BGSAddonNodeSoundHandleExtra; +extern const void * RTTI_BGSAimModel; +extern const void * RTTI_BGSArtObject; +extern const void * RTTI_BGSArtObjectCloneTask; +extern const void * RTTI_BGSBendableSpline; +extern const void * RTTI_BGSConstructibleObject; +extern const void * RTTI_BGSDebris; +extern const void * RTTI_BGSPreloadable; +extern const void * RTTI_BGSExplosion; +extern const void * RTTI_BGSHazard; +extern const void * RTTI_BGSLensFlare; +extern const void * RTTI_BSLensFlareSpriteRenderData; +extern const void * RTTI_BGSLensFlareSprite; +extern const void * RTTI_BGSMovableStatic; +extern const void * RTTI_BGSMod__Attachment__Mod; +extern const void * RTTI_BGSOutfit; +extern const void * RTTI_BGSPackIn; +extern const void * RTTI_BGSProjectile; +extern const void * RTTI_BGSStaticCollection; +extern const void * RTTI_BGSStaticCollection__RootFacade; +extern const void * RTTI_BGSTalkingActivator; +extern const void * RTTI_BGSTerminal; +extern const void * RTTI_BGSZoomData; +extern const void * RTTI_TESAmmo; +extern const void * RTTI_TESCombatStyle; +extern const void * RTTI_TESEffectShader; +extern const void * RTTI_TESFlora; +extern const void * RTTI_BGSMod__Property__BridgeI; +extern const void * RTTI_TESFurniture; +extern const void * RTTI_TESGrass; +extern const void * RTTI_TESKey; +extern const void * RTTI_TESLevCharacter; +extern const void * RTTI_TESLevItem; +extern const void * RTTI_TESLevSpell; +extern const void * RTTI_BGSOpenCloseForm; +extern const void * RTTI_TESObjectLIGH; +extern const void * RTTI_TESBoundObject; +extern const void * RTTI_TBO_InstanceData; +extern const void * RTTI_TESObject; +extern const void * RTTI_TESBoundAnimObject; +extern const void * RTTI_TESObjectACTI; +extern const void * RTTI_TESObjectANIO; +extern const void * RTTI_TESObjectARMA; +extern const void * RTTI_TESObjectARMO; +extern const void * RTTI_TESObjectARMO__InstanceData; +extern const void * RTTI_TESObjectBOOK; +extern const void * RTTI_TESObjectCONT; +extern const void * RTTI_TESObjectDOOR; +extern const void * RTTI_IFadeDoneCallback; +extern const void * RTTI_TESObjectMISC; +extern const void * RTTI_TESObjectSTAT; +extern const void * RTTI_TESObjectTREE; +extern const void * RTTI_TESObjectWEAP; +extern const void * RTTI_TESObjectWEAP__InstanceData; +extern const void * RTTI_TESObjectWEAP__Data; +extern const void * RTTI_TESSoulGem; +extern const void * RTTI_BSStaticTriShapeDB__IBatchRequestIter; +extern const void * RTTI_BGSCombinedCellGeometryDB__LoadTask; +extern const void * RTTI_BGSCombinedCellGeometryDB__ModelDB__QueuedHandles; +extern const void * RTTI_BGSCombinedCellGeometryDB__CDX; +extern const void * RTTI_BGSCombinedCellGeometryDB__CDX__PsgBatchIter; +extern const void * RTTI_BGSCombinedCellGeometryDB__PreloadTask; +extern const void * RTTI_BGSCombinedCellGeometryDB__Unpacker__Thread; +extern const void * RTTI_BGSEncounterZone; +extern const void * RTTI_BGSGodRays; +extern const void * RTTI_QueuedPromoteLargeReferencesTask; +extern const void * RTTI_BGSLightingTemplate; +extern const void * RTTI_BGSLocation; +extern const void * RTTI_QueuedPromoteLocationReferencesTask; +extern const void * RTTI_BGSLocationRefType; +extern const void * RTTI_BGSReferenceEffect; +extern const void * RTTI_BGSReferenceGroup; +extern const void * RTTI_BSParticleShaderEmitter; +extern const void * RTTI_BSParticleShaderCubeEmitter; +extern const void * RTTI_BSParticleShaderSnowEmitter; +extern const void * RTTI_BSParticleShaderRainEmitter; +extern const void * RTTI_BGSShaderParticleGeometryData; +extern const void * RTTI_CellLoaderTask; +extern const void * RTTI_ImageSpaceModifierInstanceForm; +extern const void * RTTI_ImageSpaceModifierInstance; +extern const void * RTTI_ImageSpaceModifierInstanceTemp; +extern const void * RTTI_ImageSpaceModifierInstanceDOF; +extern const void * RTTI_ImageSpaceModifierInstanceRB; +extern const void * RTTI_TESClimate; +extern const void * RTTI_TESImageSpace; +extern const void * RTTI_NiFloatData; +extern const void * RTTI_NiColorData; +extern const void * RTTI_TESImageSpaceModifier; +extern const void * RTTI_TESChildCell; +extern const void * RTTI_TESObjectLAND; +extern const void * RTTI_hknpBSMaterialProperties; +extern const void * RTTI_TESLandTexture; +extern const void * RTTI_hkSlot; +extern const void * RTTI_TESObjectCELL; +extern const void * RTTI_TESObjectCELL__IDecalRefFunctor; +extern const void * RTTI_BSHandleRefObject; +extern const void * RTTI_TESObjectREFR; +extern const void * RTTI_ActorValueOwner; +extern const void * RTTI_BGSInventoryItem__ClearEquipFlagsFunctor; +extern const void * RTTI_BGSInventoryList__InventoryWeightFunctor; +extern const void * RTTI_TESObjectREFR__HandleOutfitItemsFunctor; +extern const void * RTTI_OwnedCameraEffectController; +extern const void * RTTI_BSInputEnableManager__IDebugNameFunctor; +extern const void * RTTI_RemoveOutfitItemsFunctor; +extern const void * RTTI_CalcContainerWeight; +extern const void * RTTI_InventoryScoringFunctor; +extern const void * RTTI_GetBestWeaponFunctor; +extern const void * RTTI_GetBestAmmoFunctor; +extern const void * RTTI_GetBestArmorFunctor; +extern const void * RTTI_GetBestLightFunctor; +extern const void * RTTI_IAnimationStreamWrite; +extern const void * RTTI_IAnimationStreamRead; +extern const void * RTTI_BSAnimGraphVisit__BShkbVisitor; +extern const void * RTTI_AnimationStreamLoadGame; +extern const void * RTTI_AnimationStreamSaveGame; +extern const void * RTTI_REFREventCallbacks__IEventCallback; +extern const void * RTTI_REFREventCallbacks__IEventCallbackFactory; +extern const void * RTTI_TESObjectREFRSync__REFRSyncController; +extern const void * RTTI_TESWaterForm; +extern const void * RTTI_TESTexture1024; +extern const void * RTTI_TESWeather; +extern const void * RTTI_TESWorldSpace; +extern const void * RTTI_BGSAcousticSpace; +extern const void * RTTI_NiFormArray; +extern const void * RTTI_BGSCameraPath; +extern const void * RTTI_BGSCameraShot; +extern const void * RTTI_BGSCollisionLayer; +extern const void * RTTI_BGSColorForm; +extern const void * RTTI_BGSDefaultObjectManager; +extern const void * RTTI_BGSFootstep; +extern const void * RTTI_BGSFootstepSet; +extern const void * RTTI_BGSImpactData; +extern const void * RTTI_BGSImpactDataSet; +extern const void * RTTI_BGSListForm; +extern const void * RTTI_BSMaterialObject; +extern const void * RTTI_BGSMaterialObject; +extern const void * RTTI_BGSMaterialType; +extern const void * RTTI_BGSMenuIcon; +extern const void * RTTI_BGSMessage; +extern const void * RTTI_BGSMusicPaletteTrack; +extern const void * RTTI_BGSMusicSilenceTrack; +extern const void * RTTI_BGSMusicSingleTrack; +extern const void * RTTI_BSIMusicTrack; +extern const void * RTTI_BGSMusicTrack; +extern const void * RTTI_BGSMusicTrackFormWrapper; +extern const void * RTTI_BSIMusicType; +extern const void * RTTI_BGSMusicType; +extern const void * RTTI_BSIReverbType; +extern const void * RTTI_BGSReverbParameters; +extern const void * RTTI_BSISoundCategory; +extern const void * RTTI_BGSSoundCategory; +extern const void * RTTI_BGSSoundDescriptorForm; +extern const void * RTTI_BSISoundOutputModel; +extern const void * RTTI_BSISoundOutputModel__BSIAttenuationCharacteristics; +extern const void * RTTI_BGSSoundOutput; +extern const void * RTTI_BGSSoundOutput__DynamicAttenuationCharacteristics; +extern const void * RTTI_BSISoundDescriptor__BSIPlaybackCharacteristics; +extern const void * RTTI_BGSStandardSoundDef; +extern const void * RTTI_BGSStandardSoundDef__SoundPlaybackCharacteristics; +extern const void * RTTI_BSShaderTextureSet; +extern const void * RTTI_BSTextureSet; +extern const void * RTTI_BGSTextureSet; +extern const void * RTTI_IVisitProcedures; +extern const void * RTTI_MiscStatManager__IMiscStatVisitor; +extern const void * RTTI_MiscStatManager__FindStatByCRC; +extern const void * RTTI_TESGlobal; +extern const void * RTTI_TESLoadScreen; +extern const void * RTTI_Script; +extern const void * RTTI_MovementLargeDelta__IEnumVisitor; +extern const void * RTTI_DefaultMessageBoxCallback; +extern const void * RTTI_BSStream; +extern const void * RTTI_IUIMessageData; +extern const void * RTTI_MobIterOperator; +extern const void * RTTI_ConsoleData; +extern const void * RTTI_TESShout; +extern const void * RTTI_TESSound; +extern const void * RTTI_TESWordOfPower; +extern const void * RTTI_BGSAction; +extern const void * RTTI_BGSAssociationType; +extern const void * RTTI_BGSBodyPartData; +extern const void * RTTI_BGSDialogueBranch; +extern const void * RTTI_PerkEntryVisitor; +extern const void * RTTI_BGSEntryPointFunctionData; +extern const void * RTTI_BGSEntryPointFunctionDataOneValue; +extern const void * RTTI_BGSEntryPointFunctionDataTwoValue; +extern const void * RTTI_BGSEntryPointFunctionDataAVAndValue; +extern const void * RTTI_BGSEntryPointFunctionDataLeveledList; +extern const void * RTTI_BGSEntryPointFunctionDataSpellItem; +extern const void * RTTI_BGSEntryPointFunctionDataBooleanGraphVariable; +extern const void * RTTI_BGSEntryPointFunctionDataText; +extern const void * RTTI_BGSEntryPointFunctionDataActivateChoice; +extern const void * RTTI_BGSEquipSlot; +extern const void * RTTI_TESModelTri; +extern const void * RTTI_BGSHeadPart; +extern const void * RTTI_BGSIdleMarker; +extern const void * RTTI_BGSKeyword; +extern const void * RTTI_BGSBaseAlias; +extern const void * RTTI_BGSLocAlias; +extern const void * RTTI_BGSMovementType; +extern const void * RTTI_BGSNote; +extern const void * RTTI_BGSPerkEntry; +extern const void * RTTI_BGSPerk; +extern const void * RTTI_BGSQuestPerkEntry; +extern const void * RTTI_BGSAbilityPerkEntry; +extern const void * RTTI_BGSEntryPointPerkEntry; +extern const void * RTTI_BGSRefAlias; +extern const void * RTTI_BGSRelationship; +extern const void * RTTI_BGSScene; +extern const void * RTTI_BGSSceneActionDialogue; +extern const void * RTTI_BGSSceneActionPackage; +extern const void * RTTI_BGSSceneActionNPCResponseDialogue; +extern const void * RTTI_BGSSceneActionRadio; +extern const void * RTTI_BGSSceneActionTimer; +extern const void * RTTI_BGSVoiceType; +extern const void * RTTI_Reset3DMobIterator; +extern const void * RTTI_TESClass; +extern const void * RTTI_TESEyes; +extern const void * RTTI_TESFaction; +extern const void * RTTI_TESIdleForm; +extern const void * RTTI_TESActorBase; +extern const void * RTTI_TESNPC; +extern const void * RTTI_TESNPC__InstanceData; +extern const void * RTTI_BSTransformExtra; +extern const void * RTTI_TESQuest; +extern const void * RTTI_QueuedPromoteQuestTask; +extern const void * RTTI_BGSTextureModel; +extern const void * RTTI_BGSBehaviorGraphModel; +extern const void * RTTI_TESRace; +extern const void * RTTI_TESTopic; +extern const void * RTTI_TESTopicInfo; +extern const void * RTTI_ZoneEntry; +extern const void * RTTI_TargetEntry; +extern const void * RTTI_BGSZoneTargetListener; +extern const void * RTTI_TrapTargetEntry; +extern const void * RTTI_FOCollisionListener; +extern const void * RTTI_hkStreamWriter; +extern const void * RTTI_hknpModifier; +extern const void * RTTI_hkError; +extern const void * RTTI_hkStreamReader; +extern const void * RTTI_hkFileSystem; +extern const void * RTTI_HavokFileStreambufReader; +extern const void * RTTI_HavokFileStreambufWriter; +extern const void * RTTI_RagdollFurnitureModifier; +extern const void * RTTI_hkWin32FileSystem; +extern const void * RTTI_HavokStreambufFactory; +extern const void * RTTI_HavokError; +extern const void * RTTI_LoadedAreaBound; +extern const void * RTTI_SpecificItemCollector; +extern const void * RTTI_bhkTrapListener; +extern const void * RTTI_TrapEntry; +extern const void * RTTI_TESTrapListener; +extern const void * RTTI_Atmosphere; +extern const void * RTTI_Clouds; +extern const void * RTTI_NiBillboardNode; +extern const void * RTTI_Moon; +extern const void * RTTI_Precipitation; +extern const void * RTTI_Sky; +extern const void * RTTI_TESWeather__SkyStaticFunctor; +extern const void * RTTI_SkyEffectController; +extern const void * RTTI_SkyStaticFindFunctor; +extern const void * RTTI_TempLoadGameBuffer; +extern const void * RTTI_SkyObject; +extern const void * RTTI_Stars; +extern const void * RTTI_Sun; +extern const void * RTTI_BShkNonTransformController; +extern const void * RTTI_BShkFloatController; +extern const void * RTTI_BGShkPhonemeController; +extern const void * RTTI_BSFaceGenAnimationData; +extern const void * RTTI_FutBinaryFileC; +extern const void * RTTI_BSResourceFaceGenBinaryFile; +extern const void * RTTI_BSFaceGenPendingHeadData; +extern const void * RTTI_BSFaceGenDB__TRI__QueuedHandles; +extern const void * RTTI_BSFaceGenModel; +extern const void * RTTI_BSFaceGenModelExtraData; +extern const void * RTTI_BSFaceGenModelMap__Entry; +extern const void * RTTI_BSFaceGenMorphData; +extern const void * RTTI_BSFaceGenMorphDataHead; +extern const void * RTTI_BSFaceGenMorphDataHair; +extern const void * RTTI_BSFaceGenBaseMorphExtraData; +extern const void * RTTI_BSFaceGenNiNode; +extern const void * RTTI_BSFadeNodeCuller; +extern const void * RTTI_ActorValueInfo; +extern const void * RTTI_BGSAttackData; +extern const void * RTTI_BGSAttackDataMap; +extern const void * RTTI_IPackageData; +extern const void * RTTI_IAIWorldLocationHandle; +extern const void * RTTI_IPackageDataAIWorldLocationHandle; +extern const void * RTTI_IAITarget; +extern const void * RTTI_BGSPackageDataBool; +extern const void * RTTI_BGSPackageDataFloat; +extern const void * RTTI_BGSPackageDataInt; +extern const void * RTTI_BGSPackageDataRefOLD; +extern const void * RTTI_BGSPackageDataLocation; +extern const void * RTTI_BGSPackageDataLocationWrapper; +extern const void * RTTI_BGSPackageDataTargetSelector; +extern const void * RTTI_BGSPackageDataObjectList; +extern const void * RTTI_ObjectListItem; +extern const void * RTTI_BGSPackageDataRef; +extern const void * RTTI_BGSPackageDataTopic; +extern const void * RTTI_IProcedureTreeItem; +extern const void * RTTI_BGSProcedureDoneExecState; +extern const void * RTTI_BGSProcedureTreeConditionalItem; +extern const void * RTTI_BGSProcedureTreeBranch; +extern const void * RTTI_BGSProcedureTreeOneChildExecState; +extern const void * RTTI_BGSProcedureTreeSequence; +extern const void * RTTI_BGSProcedureTreeSequenceExecState; +extern const void * RTTI_BGSProcedureTreeStacked; +extern const void * RTTI_BGSProcedureTreeStackedExecState; +extern const void * RTTI_BGSProcedureTreeSimultaneous; +extern const void * RTTI_BGSProcedureTreeSimultaneousExecState; +extern const void * RTTI_BGSProcedureTreeRandom; +extern const void * RTTI_BGSProcedureTreeRandomExecState; +extern const void * RTTI_BGSProcedureDialogueExecState; +extern const void * RTTI_BGSProcedureEatExecState; +extern const void * RTTI_BGSProcedureFindExecState; +extern const void * RTTI_BGSProcedureHoldPositionExecState; +extern const void * RTTI_BGSProcedureKeepAnEyeOnExecState; +extern const void * RTTI_BGSProcedureSayExecState; +extern const void * RTTI_BGSProcedureWaitExecState; +extern const void * RTTI_BGSProcedureDone; +extern const void * RTTI_BGSProcedureAcquire; +extern const void * RTTI_BGSProcedureAcquireExecState; +extern const void * RTTI_BGSProcedureActivate; +extern const void * RTTI_BGSProcedureActivateExecState; +extern const void * RTTI_BGSProcedureDialogue; +extern const void * RTTI_BGSProcedureDialogueActivate; +extern const void * RTTI_BGSProcedureDialogueActivateExecState; +extern const void * RTTI_BGSProcedureEat; +extern const void * RTTI_BGSProcedureEscort; +extern const void * RTTI_BGSProcedureEscortExecState; +extern const void * RTTI_BGSProcedureFind; +extern const void * RTTI_BGSProcedureFlee; +extern const void * RTTI_BGSProcedureFleeExecState; +extern const void * RTTI_BGSProcedureFlightGrab; +extern const void * RTTI_BGSProcedureFlightGrabExecState; +extern const void * RTTI_BGSProcedureFollow; +extern const void * RTTI_BGSProcedureFollowTo; +extern const void * RTTI_BGSProcedureFollowExecState; +extern const void * RTTI_BGSProcedureForceGreet; +extern const void * RTTI_BGSProcedureGuard; +extern const void * RTTI_BGSProcedureGuardArea; +extern const void * RTTI_BGSProcedureGuardExecState; +extern const void * RTTI_BGSProcedureHoldPosition; +extern const void * RTTI_BGSProcedureHover; +extern const void * RTTI_BGSProcedureHoverExecState; +extern const void * RTTI_BGSProcedureKeepAnEyeOn; +extern const void * RTTI_BGSProcedureLock; +extern const void * RTTI_BGSProcedureUnlock; +extern const void * RTTI_BGSProcedureLockUnlockExecState; +extern const void * RTTI_BGSProcedureOrbit; +extern const void * RTTI_BGSProcedureOrbitExecState; +extern const void * RTTI_BGSProcedurePatrolExecState; +extern const void * RTTI_BGSProcedurePatrol; +extern const void * RTTI_BGSProcedurePursue; +extern const void * RTTI_BGSProcedureSandboxExecState; +extern const void * RTTI_BGSProcedureSandbox; +extern const void * RTTI_BGSProcedureSay; +extern const void * RTTI_BGSProcedureSit; +extern const void * RTTI_BGSProcedureSleep; +extern const void * RTTI_BGSProcedureSitSleepExecState; +extern const void * RTTI_BGSProcedureTravel; +extern const void * RTTI_BGSProcedureTravelExecState; +extern const void * RTTI_BGSProcedureTreeProcedure; +extern const void * RTTI_BGSProcedureUseIdleMarker; +extern const void * RTTI_BGSProcedureUseIdleMarkerExecState; +extern const void * RTTI_BGSProcedureUseMagic; +extern const void * RTTI_BGSProcedureUseMagicExecState; +extern const void * RTTI_BGSProcedureUseWeapon; +extern const void * RTTI_BGSProcedureUseWeaponExecState; +extern const void * RTTI_BGSProcedureWait; +extern const void * RTTI_BGSProcedureWander; +extern const void * RTTI_BGSProcedureWanderExecState; +extern const void * RTTI_BGSVisitProceduresInitActorLocation; +extern const void * RTTI_BGSVisitProceduresCheckGuardWarnTarget; +extern const void * RTTI_TESPackage; +extern const void * RTTI_PackageCreator; +extern const void * RTTI_DialoguePackage; +extern const void * RTTI_TESPackageData; +extern const void * RTTI_TESAmbushPackageData; +extern const void * RTTI_TESCustomPackageData; +extern const void * RTTI_IProcedureTreeVisitor; +extern const void * RTTI_CustomUtils__HasForceGreetVisitor; +extern const void * RTTI_TESDialoguePackageData; +extern const void * RTTI_TESEatPackageData; +extern const void * RTTI_TESEscortPackageData; +extern const void * RTTI_TESFollowPackageData; +extern const void * RTTI_TESPatrolPackageData; +extern const void * RTTI_TESUseItemPackageData; +extern const void * RTTI_TESUseWeaponPackageData; +extern const void * RTTI_ActorPackageData; +extern const void * RTTI_UseWeaponActorPackageData; +extern const void * RTTI_PackageLocation; +extern const void * RTTI_BSTempEffect; +extern const void * RTTI_BSTempEffectDebris; +extern const void * RTTI_BSTempEffectGeometryDecal; +extern const void * RTTI_BGSParticleObjectCloneTask; +extern const void * RTTI_BSTempEffectParticle; +extern const void * RTTI_BSTempEffectSPG; +extern const void * RTTI_BSTempEffectWeaponBlood; +extern const void * RTTI_BSTerrainEffect; +extern const void * RTTI_BSPSysHavokUpdateModifier__ICollisionObjectHandler; +extern const void * RTTI_NavMesh; +extern const void * RTTI_BSNavmeshInfoMap__IVisitor; +extern const void * RTTI_NavMeshInfoMap; +extern const void * RTTI_BSNavmeshReferenceObstacleArray; +extern const void * RTTI_ObstacleTaskData; +extern const void * RTTI_PathingCoverLocation; +extern const void * RTTI_BSPathingCellManager; +extern const void * RTTI_Pathing; +extern const void * RTTI_BSPathingCell; +extern const void * RTTI_PathingCell; +extern const void * RTTI_BSPathingDoor; +extern const void * RTTI_PathingDoor; +extern const void * RTTI_BSPathingLockData; +extern const void * RTTI_PathingLockData; +extern const void * RTTI_BSPathingNumericIDVisitor; +extern const void * RTTI_PathingNumericIDVisitor; +extern const void * RTTI_PathingRequest; +extern const void * RTTI_BSPathingSpace; +extern const void * RTTI_PathingSpace; +extern const void * RTTI_TeleportDoorSearch; +extern const void * RTTI_NavMeshSearchClosePoint; +extern const void * RTTI_NavMeshSearchFitSphere; +extern const void * RTTI_NavMeshSearchFlee; +extern const void * RTTI_NavMeshSearchHide; +extern const void * RTTI_BSNavmeshSearchHideFilter; +extern const void * RTTI_NavMeshSearchLOS; +extern const void * RTTI_NavMeshSearchMaxCost; +extern const void * RTTI_BSNavmeshSearchMaxCostFilters; +extern const void * RTTI_NavMeshSearchMultipleGoals; +extern const void * RTTI_NavMeshSearchSLPoint; +extern const void * RTTI_PathingRequestClosePoint; +extern const void * RTTI_PathingRequestClosestGoal; +extern const void * RTTI_PathingRequestCover; +extern const void * RTTI_PathingRequestFlee; +extern const void * RTTI_PathingRequestFly; +extern const void * RTTI_PathingRequestFlyAction; +extern const void * RTTI_PathingRequestFlyHover; +extern const void * RTTI_PathingRequestFlyLand; +extern const void * RTTI_PathingRequestFlyOrbit; +extern const void * RTTI_PathingRequestFlyTakeOff; +extern const void * RTTI_PathingRequestHide; +extern const void * RTTI_PathingRequestLOS; +extern const void * RTTI_PathingRequestOptimalLocation; +extern const void * RTTI_PathingRequestRotate; +extern const void * RTTI_PathingRequestSafeStraightLine; +extern const void * RTTI_PathingRequestStopMoving; +extern const void * RTTI_BGSQueuedTerrainUpdate; +extern const void * RTTI_BGSQueuedTerrainUpgrade; +extern const void * RTTI_BGSQueuedTerrainDowngrade; +extern const void * RTTI_BGSQueuedTerrainInitialLoad; +extern const void * RTTI_BGSQueuedObjectUpgrade; +extern const void * RTTI_BGSQueuedObjectDowngrade; +extern const void * RTTI_BGSQueuedObjectInitialLoad; +extern const void * RTTI_BGSWaterCollisionManager__BGSWaterUpdateI; +extern const void * RTTI_BGSWaterCollisionManager__bhkPlaceableWater; +extern const void * RTTI_BGSWaterCollisionManager__bhkWaterfall; +extern const void * RTTI_BGSWaterCollisionManager__AutoWater; +extern const void * RTTI_TESWaterObject; +extern const void * RTTI_TESWaterReflections; +extern const void * RTTI_TESWaterNormals; +extern const void * RTTI_TESWaterDisplacement; +extern const void * RTTI_BGSStoryManagerTreeVisitor; +extern const void * RTTI_BGSStoryManagerQuestFinder; +extern const void * RTTI_BGSStoryManager; +extern const void * RTTI_BGSStoryManagerEventNode; +extern const void * RTTI_BGSStoryManagerQuestNode; +extern const void * RTTI_BGSStoryTeller; +extern const void * RTTI_BGSLOSData; +extern const void * RTTI_AnimationSystemUtils__QueuedReferenceAnimationTask; +extern const void * RTTI_hkaRaycastInterface; +extern const void * RTTI_hkbSpatialQueryInterface; +extern const void * RTTI_BSIFootIkRaycastInterfaceDB; +extern const void * RTTI_CachedRaycastData; +extern const void * RTTI_BGSFootIkRaycastInterfaceDB; +extern const void * RTTI_IGamebryoSequenceGeneratorHolderSingleton; +extern const void * RTTI_BGSGamebryoSequenceGeneratorHolderSingleton; +extern const void * RTTI_ISynchronizedAnimationSingleton; +extern const void * RTTI_BGSSynchronizedAnimationInstance; +extern const void * RTTI_BGSSynchronizedAnimationManager; +extern const void * RTTI_SynchronizedAnimationSingleton; +extern const void * RTTI_BSLimbIKModifierUtilityCastInfo; +extern const void * RTTI_BSLimbIKModifierUtility; +extern const void * RTTI_IAnimationGraphManagerHolder; +extern const void * RTTI_IAnimationGraphManagerLoadingTask; +extern const void * RTTI_SimpleAnimationGraphManagerLoadingTask; +extern const void * RTTI_SimpleAnimationGraphManagerHolder; +extern const void * RTTI_WeaponAnimationGraphManagerHolder; +extern const void * RTTI_TESCamera; +extern const void * RTTI_BSAlignBoneModifierInterface; +extern const void * RTTI_BSLookAtModifierInterface; +extern const void * RTTI_AttractionObjectLOSUtils__CacheEntry; +extern const void * RTTI_ThirdPersonState; +extern const void * RTTI_DialogueCameraState; +extern const void * RTTI_VATSCamera__BaseState; +extern const void * RTTI_VATSCamera__ModifierBase; +extern const void * RTTI_VATSCamera__StationaryCameraState; +extern const void * RTTI_VATSCamera__LookAtCameraState; +extern const void * RTTI_VATSCamera__ActorLookCameraState; +extern const void * RTTI_AimModel__Component; +extern const void * RTTI_AimModelPlayerComponent; +extern const void * RTTI_CombatPath; +extern const void * RTTI_CombatObject; +extern const void * RTTI_CombatObjectBase; +extern const void * RTTI_CombatPathDestinationRef; +extern const void * RTTI_CombatPathDestinationActor; +extern const void * RTTI_CombatChargingSearchData; +extern const void * RTTI_CombatBehaviorTreeWrapperNode; +extern const void * RTTI_CombatBehaviorTreeCreateContextNodeBase; +extern const void * RTTI_CombatBehaviorTreeChargingMovement; +extern const void * RTTI_CombatBehaviorTreeChargingCloseMovement; +extern const void * RTTI_CombatSharedObject; +extern const void * RTTI_CombatMovementRequest; +extern const void * RTTI_CombatMovementRequestFollowActor; +extern const void * RTTI_CombatBehaviorTreeMovementRequest; +extern const void * RTTI_CombatBehaviorTreeGrenade; +extern const void * RTTI_CombatBehaviorTreeGun; +extern const void * RTTI_CombatBehaviorTreeThrow; +extern const void * RTTI_CombatArea; +extern const void * RTTI_CombatAreaShape; +extern const void * RTTI_CombatPathingSearchArea; +extern const void * RTTI_CombatPathingRequestCovered; +extern const void * RTTI_CombatCoveredPathDebugData; +extern const void * RTTI_CombatCoveredPath; +extern const void * RTTI_CombatPathBuilderCovered; +extern const void * RTTI_CombatPositionTracker; +extern const void * RTTI_CombatSuppressiveFireBehavior; +extern const void * RTTI_CombatPathController; +extern const void * RTTI_CombatTunnelPathController; +extern const void * RTTI_CombatViewController; +extern const void * RTTI_CombatViewControllerStandard; +extern const void * RTTI_CombatViewControllerGlance; +extern const void * RTTI_CombatViewControllerPath; +extern const void * RTTI_MovementParametersFixed; +extern const void * RTTI_PassengerInteraction; +extern const void * RTTI_BGSPerk__FindPerkInRanksVisitor; +extern const void * RTTI_BGSPerk__AddPerkVisitor; +extern const void * RTTI_BGSPerk__ApplyPerksVisitor; +extern const void * RTTI_BGSInventoryItem__SetHealthFunctor; +extern const void * RTTI_BGSInventoryItem__ModifyModDataFunctor; +extern const void * RTTI_PowerArmor__EventHandler; +extern const void * RTTI_BSInputEventReceiver; +extern const void * RTTI_BSIdleInputWatcher; +extern const void * RTTI_BSInputEventSingleUser; +extern const void * RTTI_BuildABot__InputHandler; +extern const void * RTTI_CheckFastTravelCommand; +extern const void * RTTI_ClearIdleCommand; +extern const void * RTTI_DropItemCommand; +extern const void * RTTI_FastTravelCommand; +extern const void * RTTI_CheckHardcoreFastTravelCommand; +extern const void * RTTI_PipboyCommandResult; +extern const void * RTTI_FastTravelCommandResult; +extern const void * RTTI_LocalMapSnapshotCommand; +extern const void * RTTI_PipboyCommand; +extern const void * RTTI_RemoveCustomMapMarkerCommand; +extern const void * RTTI_SetCustomMapMarkerCommand; +extern const void * RTTI_SetQuickkeyCommand; +extern const void * RTTI_SortInventoryCommand; +extern const void * RTTI_ToggleComponentFavoriteCommand; +extern const void * RTTI_ToggleQuestActiveStatusCommand; +extern const void * RTTI_ToggleRadioStationCommand; +extern const void * RTTI_UseItemCommand; +extern const void * RTTI_Json__FastWriter; +extern const void * RTTI_PipboyCompanionService; +extern const void * RTTI_ButtonHintBar; +extern const void * RTTI_FlatScreenModel; +extern const void * RTTI_HUDScreenModel; +extern const void * RTTI_HUDActionPointMeter; +extern const void * RTTI_HUDActiveEffectsDisplay; +extern const void * RTTI_HUDActiveEffectsModel; +extern const void * RTTI_HUDActorValueMeterBase; +extern const void * RTTI_HUDAmmoCounter; +extern const void * RTTI_HUDExplosiveAmmoCounter; +extern const void * RTTI_HUDCompassBase; +extern const void * RTTI_HUDCompass; +extern const void * RTTI_PowerArmorHUDCompass; +extern const void * RTTI_HUDCompassMarkerBase; +extern const void * RTTI_HUDCompassDirectionMarker; +extern const void * RTTI_HUDCompassMarker; +extern const void * RTTI_HUDComponentBase; +extern const void * RTTI_HUDCriticalMeter; +extern const void * RTTI_HUDCrosshair; +extern const void * RTTI_HUDCrosshair__FXCrosshairBase; +extern const void * RTTI_HUDCrosshair__FXCrosshairBase__FXCrosshairTicks; +extern const void * RTTI_HUDDirectionalHitIndicatorDataModel; +extern const void * RTTI_HUDEnemyHealthMeter; +extern const void * RTTI_HUDExperienceMeter; +extern const void * RTTI_HUDExplosivesDataModel; +extern const void * RTTI_HUDFatigueWarning; +extern const void * RTTI_HUDFloatingQuestMarkers; +extern const void * RTTI_HUDFloatingQuestMarkers__QuestMarker; +extern const void * RTTI_HUDMessages; +extern const void * RTTI_HUDMessagesModel; +extern const void * RTTI_HUDMeterBase; +extern const void * RTTI_HUDMeter; +extern const void * RTTI_GameUIModel; +extern const void * RTTI_HUDDataModel; +extern const void * RTTI_HUDNotificationsModel; +extern const void * RTTI_HUDPerkVaultBoy; +extern const void * RTTI_HUDPerkVaultBoyModel; +extern const void * RTTI_HUDPlayerHealthMeter; +extern const void * RTTI_HUDQuestVaultBoy; +extern const void * RTTI_HUDObjectiveUpdates; +extern const void * RTTI_HUDQuestUpdates; +extern const void * RTTI_HUDQuickContainer; +extern const void * RTTI_HUDQuickContainerDataModel; +extern const void * RTTI_HUDRadiationMeter; +extern const void * RTTI_HUDRadiationModel; +extern const void * RTTI_HUDRollover; +extern const void * RTTI_HUDRolloverModel; +extern const void * RTTI_HUDSubtitleText; +extern const void * RTTI_HUDTutorialText; +extern const void * RTTI_HUDData; +extern const void * RTTI_HUDDataBase; +extern const void * RTTI_ContainerMenuBase__IValidFunc; +extern const void * RTTI_OpenBarterMenuMessage; +extern const void * RTTI_BarterMenu; +extern const void * RTTI_InventoryUpdateData; +extern const void * RTTI_nsBarterMenu__ValidFunc; +extern const void * RTTI_nsBarterMenu__VendorCapsTooLowCallback; +extern const void * RTTI_nsBarterMenu__CancelTradeInProgressCallback; +extern const void * RTTI_nsBarterMenu__ConfirmTradeCallback; +extern const void * RTTI_nsBarterMenu__HideMenuCallback; +extern const void * RTTI_nsBarterMenu__ConfirmInvestmentCallback; +extern const void * RTTI_ButtonBarMenu; +extern const void * RTTI_ContainerMenuBase; +extern const void * RTTI_ContainerMenuBase__FXQuantityMenu; +extern const void * RTTI_CookingMenu; +extern const void * RTTI_ExamineConfirmMenu; +extern const void * RTTI_ExamineConfirmMenu__InitData; +extern const void * RTTI_ExamineConfirmMenu__InitDataBuild; +extern const void * RTTI_ExamineConfirmMenu__InitDataScrap; +extern const void * RTTI_ExamineConfirmMenu__InitDataRepairFailure; +extern const void * RTTI_Scaleform__GFx__Value__ObjectInterface__ArrVisitor; +extern const void * RTTI_BGSInventoryItem__WriteTextExtra; +extern const void * RTTI_ExamineMenu; +extern const void * RTTI_ExamineMenu__FXExamineMenu; +extern const void * RTTI_ExamineMenu__FXExamineMenu__FXExamineSubMenu; +extern const void * RTTI_GameMenuBase; +extern const void * RTTI_GenericMenu; +extern const void * RTTI_PipboyOpeningSequenceMenu; +extern const void * RTTI_FlashHitIndicator; +extern const void * RTTI_FlashStealth; +extern const void * RTTI_FlashLocationText; +extern const void * RTTI_FlashVaultBoyCondition; +extern const void * RTTI_FlashExplosiveIndicators; +extern const void * RTTI_FlashExplosiveIndicators__SingleExplosiveIndicator; +extern const void * RTTI_FlashDirectionalHitIndicators; +extern const void * RTTI_FlashDirectionalHitIndicators__SingleDirectionalHitIndicator; +extern const void * RTTI_LevelUpMenu; +extern const void * RTTI_nsLevelUpMenu__LevelUpPromptRequest; +extern const void * RTTI_InputEvent; +extern const void * RTTI_IDEvent; +extern const void * RTTI_ButtonEvent; +extern const void * RTTI_LooksInputRepeatHandler; +extern const void * RTTI_LooksMenu; +extern const void * RTTI_LooksMenuUtils__ExitConfirmCallback; +extern const void * RTTI_LooksMenuUtils__ChangePresetConfirmCallback; +extern const void * RTTI_CreationClubMenu; +extern const void * RTTI_CreationClub__AsyncModRequest; +extern const void * RTTI_CreationClub__CreationListRequest; +extern const void * RTTI_CreationClub__BundleChildrenRequest; +extern const void * RTTI_CreationClub__CategorySearchRequest; +extern const void * RTTI_CreationClub__GetCreationRequest; +extern const void * RTTI_CreationClub__GetIntRequest; +extern const void * RTTI_CreationClub__DownloadImageRequest; +extern const void * RTTI_CreationClub__StringStringRequest; +extern const void * RTTI_CreationClub__SavedGFxValueRequest; +extern const void * RTTI_CreationClub__DownloadCreationRequest; +extern const void * RTTI_CreationClub__CreationChanged_ConfirmResetCallback; +extern const void * RTTI_ModManager__AsyncModRequest; +extern const void * RTTI_ModManager__DownloadModRequest; +extern const void * RTTI_ModManager__CategorySearchRequest; +extern const void * RTTI_ModManager__GetDetailsRequest; +extern const void * RTTI_ModManager__GetDependencyRequest; +extern const void * RTTI_ModManager__DownloadImageRequest; +extern const void * RTTI_ModManager__SavedGFxValueRequest; +extern const void * RTTI_ModManager__GetIntRequest; +extern const void * RTTI_ModManager__StringStringRequest; +extern const void * RTTI_ModManager__ModsChanged_ConfirmResetCallback; +extern const void * RTTI_ModManager__ReportMod_CategoryCallback; +extern const void * RTTI_ModManager__ReportMod_ConfirmCallback; +extern const void * RTTI_ModManager__DeleteMod_ConfirmCallback; +extern const void * RTTI_ModManager__DeleteLibraryMod_ConfirmCallback; +extern const void * RTTI_ModManager__DeleteAllMods_ConfirmCallback; +extern const void * RTTI_ModManager__CloseModManager_ConfirmCallback; +extern const void * RTTI_IMultiActivateUser; +extern const void * RTTI_MultiActivateManager; +extern const void * RTTI_MultiActivateMenu; +extern const void * RTTI_CompanionListener; +extern const void * RTTI_ActivateChoiceListener; +extern const void * RTTI_PauseMenu; +extern const void * RTTI_BSUIMessageData; +extern const void * RTTI_nsPauseMenu__UserDisengagedCallback; +extern const void * RTTI_HolotapeDataManager; +extern const void * RTTI_HolotapeMenu; +extern const void * RTTI_PipboyHolotapeMenu; +extern const void * RTTI_TerminalHolotapeMenu; +extern const void * RTTI_PipboyLogMenu; +extern const void * RTTI_PipboyMapMenu; +extern const void * RTTI_PipboyInventoryMenu; +extern const void * RTTI_PipboyMenu; +extern const void * RTTI_PipboyPerksMenu; +extern const void * RTTI_PipboyQuestMenu; +extern const void * RTTI_PipboyRadioMenu; +extern const void * RTTI_PipboyPlayerInfoMenu; +extern const void * RTTI_PipboySpecialMenu; +extern const void * RTTI_PipboyStatsMenu; +extern const void * RTTI_PipboySubMenu; +extern const void * RTTI_PipboyWorkshopMenu; +extern const void * RTTI_PlayBinkMenu; +extern const void * RTTI_nsPlayBinkMenu__InitDataMessage; +extern const void * RTTI_PowerArmorModMenu; +extern const void * RTTI_IPromptMenuRequest; +extern const void * RTTI_PromptMenu; +extern const void * RTTI_PromptMenuData; +extern const void * RTTI_RobotModMenu; +extern const void * RTTI_ScopeMenuUtil__FadeCallback; +extern const void * RTTI_ScopeMenu; +extern const void * RTTI_SPECIALMenu; +extern const void * RTTI_nsSPECIALMenu__ConfirmResetPointsCallback; +extern const void * RTTI_StartMenuBase; +extern const void * RTTI_nsStartMenu_InputMapping__RemapHandler; +extern const void * RTTI_StreamingInstallMenu; +extern const void * RTTI_TerminalMenu; +extern const void * RTTI_TerminalRunResultsCallback; +extern const void * RTTI_TerminalMenuButtons; +extern const void * RTTI_TerminalMenuUtils__HolotapeSelectionCallback; +extern const void * RTTI_Scaleform__RefCountImpl; +extern const void * RTTI_Scaleform__RefCountImplCore; +extern const void * RTTI_Scaleform__GFx__FunctionHandler; +extern const void * RTTI_SWFToCodeFunctionHandler; +extern const void * RTTI_VATSMenuPrivate__VatsMenuObj; +extern const void * RTTI_VATSMenu; +extern const void * RTTI_HUDActionPointData; +extern const void * RTTI_VATSMenuPrivate__ASFunctionHandler; +extern const void * RTTI_VATSMenuPrivate__VatsMenuObj__FXPartInfo; +extern const void * RTTI_VertibirdMenu; +extern const void * RTTI_VignetteMenu; +extern const void * RTTI_WorkbenchMenuBase; +extern const void * RTTI_nsInventory3DManager__NewInventoryMenuItemLoadTask; +extern const void * RTTI_nsInventory3DManager__WorkshopMenuItemLoadTask; +extern const void * RTTI_WorkshopMenuGeometry; +extern const void * RTTI_WorkshopMenu; +extern const void * RTTI_WorkshopMenu__FXWorkshopMenu; +extern const void * RTTI_nsWorkshopMenu__WorkshopMenuPromptRequest; +extern const void * RTTI_nsWS_Caravan__MessageData; +extern const void * RTTI_Workshop_CaravanMenu; +extern const void * RTTI_Workshop_CaravanMenu__FXWorkshopCaravanMenu; +extern const void * RTTI_PipboyArray; +extern const void * RTTI_PipboyDataGroup; +extern const void * RTTI_PipboyInventoryData; +extern const void * RTTI_PipboyLogData; +extern const void * RTTI_PipboyLogData__PopulateStatsVisitor; +extern const void * RTTI_PipboyManager; +extern const void * RTTI_IPipboyThrottledValue; +extern const void * RTTI_PipboyMapData; +extern const void * RTTI_PipboyObject; +extern const void * RTTI_PipboyPerksData; +extern const void * RTTI_PipboyQuestData; +extern const void * RTTI_PipboyPlayerInfoData; +extern const void * RTTI_PipboySpecialData; +extern const void * RTTI_PipboyStatsData; +extern const void * RTTI_PipboyStatusData; +extern const void * RTTI_PipboyValue; +extern const void * RTTI_PipboyWorkshopData; +extern const void * RTTI_PipboyRadioData; +extern const void * RTTI_AbsorbEffect; +extern const void * RTTI_AccumulatingValueModifierEffect; +extern const void * RTTI_ActiveEffect; +extern const void * RTTI_ActiveEffectReferenceEffectController; +extern const void * RTTI_ActiveEffect__ForEachHitEffectVisitor; +extern const void * RTTI_BSAnimationGraphChannel; +extern const void * RTTI_MagicItemDataCollector; +extern const void * RTTI_ActorMagicCaster; +extern const void * RTTI_BSAttachTechniques__AttachTechniqueInput; +extern const void * RTTI_RefAttachTechniqueInput; +extern const void * RTTI_CloakEffect; +extern const void * RTTI_MagicTarget__IPostCreationModification; +extern const void * RTTI_CommandEffect; +extern const void * RTTI_CommandSummonedEffect; +extern const void * RTTI_CureEffect; +extern const void * RTTI_DamageEffect; +extern const void * RTTI_DarknessEffect; +extern const void * RTTI_DetectLifeEffect; +extern const void * RTTI_DisarmEffect; +extern const void * RTTI_DisguiseEffect; +extern const void * RTTI_DisguiseEffect__DetectionChecker; +extern const void * RTTI_DispelEffect; +extern const void * RTTI_DualValueModifierEffect; +extern const void * RTTI_EtherealizationEffect; +extern const void * RTTI_GuideEffect; +extern const void * RTTI_ImmunityEffect; +extern const void * RTTI_InvisibilityEffect; +extern const void * RTTI_JetpackEffect; +extern const void * RTTI_LightEffect; +extern const void * RTTI_LockEffect; +extern const void * RTTI_MagicCaster; +extern const void * RTTI_MagicCaster__PostCreationCallback; +extern const void * RTTI_ProcessLists__GetActorsFilter; +extern const void * RTTI_ActorTargetCheck; +extern const void * RTTI_MagicTarget; +extern const void * RTTI_FindEffectKeywordOnTargetFunctor; +extern const void * RTTI_ModelReferenceEffect; +extern const void * RTTI_NightEyeEffect; +extern const void * RTTI_NonActorMagicTarget; +extern const void * RTTI_OpenEffect; +extern const void * RTTI_ParalysisEffect; +extern const void * RTTI_PeakValueModifierEffect; +extern const void * RTTI_ReanimateEffect; +extern const void * RTTI_ReferenceEffect; +extern const void * RTTI_WeaponEnchantmentController; +extern const void * RTTI_ScriptEffect; +extern const void * RTTI_ScriptedRefEffect; +extern const void * RTTI_ShaderReferenceEffect; +extern const void * RTTI_SlowTimeEffect; +extern const void * RTTI_SoulTrapEffect; +extern const void * RTTI_StaggerEffect; +extern const void * RTTI_StimpakEffect; +extern const void * RTTI_BSPathing__CheckLineOfSightFilter; +extern const void * RTTI_CheckLineOfSightFilterMisc; +extern const void * RTTI_SummonPlacementEffect; +extern const void * RTTI_SummonCreatureEffect; +extern const void * RTTI_TargetValueModifierEffect; +extern const void * RTTI_RallyEffect; +extern const void * RTTI_DemoralizeEffect; +extern const void * RTTI_TurnUndeadEffect; +extern const void * RTTI_BanishEffect; +extern const void * RTTI_CalmEffect; +extern const void * RTTI_FrenzyEffect; +extern const void * RTTI_TelekinesisEffect; +extern const void * RTTI_ValueModifierEffect; +extern const void * RTTI_TESAudio__ScriptedMusicState; +extern const void * RTTI_BGSPlayerMusicChanger; +extern const void * RTTI_TESAudio__DuckingMgmt__InstancedIni; +extern const void * RTTI_TESAudio__DuckingMgmt__AttenInstancedIni; +extern const void * RTTI_MenuTopicManager; +extern const void * RTTI_ActorUtils__ArmorRatingVisitorBase; +extern const void * RTTI_BGSGameWarningsHandler__GameWarningsSink; +extern const void * RTTI_JobListManager__ServingThread; +extern const void * RTTI_BGSSaveLoadManager; +extern const void * RTTI_BGSSaveLoadThread; +extern const void * RTTI_BGSSaveLoadStatsMap; +extern const void * RTTI_BGSLoadGameBuffer; +extern const void * RTTI_BGSSaveFormBuffer; +extern const void * RTTI_BGSSaveGameBuffer; +extern const void * RTTI_SaveStorageWrapper; +extern const void * RTTI_LoadStorageWrapper; +extern const void * RTTI_std___System_error_category; +extern const void * RTTI_BSResource__CacheDrive__Op; +extern const void * RTTI_BGSMoviePlayer; +extern const void * RTTI_IExplosionFactory; +extern const void * RTTI_ChainExplosion; +extern const void * RTTI_EnhanceWeaponEffect; +extern const void * RTTI_IGCQueue; +extern const void * RTTI_IDDQueueCollection; +extern const void * RTTI_RegSettingCollection; +extern const void * RTTI_BSTreeManager__IQueryCullingCamera; +extern const void * RTTI_IMovementControllerNPCFunctor; +extern const void * RTTI_HelpMessageManager; +extern const void * RTTI_UISaveLoadManager; +extern const void * RTTI_BGSImpactManager; +extern const void * RTTI_Main; +extern const void * RTTI_MovementAvoidBoxEventAdapter; +extern const void * RTTI_PathManagerPositionPlayerAdapter; +extern const void * RTTI_CharacterCollisionMessagePlayerAdapter; +extern const void * RTTI_PlayerSleepWaitMovementControllerAdapter; +extern const void * RTTI_PlayerSleepWaitMovementControllerAdapter__SwitchToLoadedSetControllerFunctor; +extern const void * RTTI_PlayerSleepWaitMovementControllerAdapter__SwitchToUnloadedSetControllerFunctor; +extern const void * RTTI_DoorObstacleAdapter; +extern const void * RTTI_IMemoryManagerFile; +extern const void * RTTI_IMemoryManagerFileFactory; +extern const void * RTTI_MemoryManagerFile; +extern const void * RTTI_SceneGraph; +extern const void * RTTI_SeenData; +extern const void * RTTI_IntSeenData; +extern const void * RTTI_SpawnHazardEffect; +extern const void * RTTI_bhkIAddToWorld; +extern const void * RTTI_BSShaderProperty__ForEachVisitor; +extern const void * RTTI_ProcessObjectsLocal; +extern const void * RTTI_TextureUpdateStage; +extern const void * RTTI_ProcessObjects; +extern const void * RTTI_ProcessObjectsDetached; +extern const void * RTTI_ProcessObjects1; +extern const void * RTTI_ProcessObjects2; +extern const void * RTTI_ProcessObjects3; +extern const void * RTTI_ProcessObjects4; +extern const void * RTTI_ProcessObjectsLOD; +extern const void * RTTI_ProcessObjectsGrass; +extern const void * RTTI_ProcessObjectsDecals; +extern const void * RTTI_TextureUpdateStart; +extern const void * RTTI_TextureUpdateDone; +extern const void * RTTI_Actor; +extern const void * RTTI_IMovementState; +extern const void * RTTI_PackageList__IPackageVisitor; +extern const void * RTTI_IPostAnimationChannelUpdateFunctor; +extern const void * RTTI_ActorUtils__ArmorRatingVisitor; +extern const void * RTTI_CombatQueuedEvent; +extern const void * RTTI_EquipActorOutfitItemsFunctor; +extern const void * RTTI_SaveLoadMagicCasterVisitor; +extern const void * RTTI_SaveGameMagicCasterVisitor; +extern const void * RTTI_LoadGameMagicCasterVisitor; +extern const void * RTTI_RevertSelectedSpellFunctor; +extern const void * RTTI_Actor__ForEachSpellVisitor; +extern const void * RTTI_HasSpellVisitor; +extern const void * RTTI_ActorMediator; +extern const void * RTTI_ActorState; +extern const void * RTTI_BGSVisitProceduresProcess; +extern const void * RTTI_BGSVisitProceduresInitActorAnimPose; +extern const void * RTTI_AIProcess__PendingActorHeadData; +extern const void * RTTI_DetectionListener; +extern const void * RTTI_EquippedItemData; +extern const void * RTTI_EquippedWeaponData; +extern const void * RTTI_BGSFootstepManager; +extern const void * RTTI_BSDoorHavokController; +extern const void * RTTI_BSPlayerDistanceCheckController; +extern const void * RTTI_BSSimpleScaleController; +extern const void * RTTI_HighActorCuller; +extern const void * RTTI_MountInteraction; +extern const void * RTTI_IMovementPlayerControlsFilter; +extern const void * RTTI_PlayerCharacter; +extern const void * RTTI_BShkbUtils__GraphInspectionFunctor; +extern const void * RTTI_PlayerRegionState; +extern const void * RTTI_ProcessLists; +extern const void * RTTI_IMovementFormIDManager; +extern const void * RTTI_ProcessListMovementInterface; +extern const void * RTTI_RefrInteraction; +extern const void * RTTI_SyncQueueObj; +extern const void * RTTI_ActorMover; +extern const void * RTTI_bhkCharacterCollisionHandler; +extern const void * RTTI_CharacterCollisionHandler; +extern const void * RTTI_IMovementAvoidanceManager; +extern const void * RTTI_MovementAvoidanceManager; +extern const void * RTTI_IMovementMessageInterface; +extern const void * RTTI_IMovementDirectControl; +extern const void * RTTI_IMovementPlannerDirectControl; +extern const void * RTTI_IMovementSelectIdle; +extern const void * RTTI_IMovementMotionDrivenControl; +extern const void * RTTI_IMovementQueryAnimDeltas; +extern const void * RTTI_MovementControllerNPC; +extern const void * RTTI_BSPathingStreamWrite; +extern const void * RTTI_BSPathingStreamSimpleBufferWrite; +extern const void * RTTI_MovementHandlerAgentDirectControl; +extern const void * RTTI_MovementHandlerAgentPlayerControlsActionTrigger; +extern const void * RTTI_IMovementSetSprinting; +extern const void * RTTI_MovementHandlerAgentSprintActionTrigger; +extern const void * RTTI_MovementHandlerAgentStairsHelper; +extern const void * RTTI_MovementMessage; +extern const void * RTTI_MovementMessageActorCollision; +extern const void * RTTI_IMovementQueryState; +extern const void * RTTI_MovementPlannerAgentDirectControl; +extern const void * RTTI_IMovementSetWarp; +extern const void * RTTI_MovementPlannerAgentWarp; +extern const void * RTTI_ITweenerNodeFollowerSetup; +extern const void * RTTI_MovementTweenerAgentNodeFollower; +extern const void * RTTI_PathingStreamLoadGame; +extern const void * RTTI_PathingStreamSaveGame; +extern const void * RTTI_IMovementPlayerControls; +extern const void * RTTI_PlayerInputHandler; +extern const void * RTTI_HeldStateHandler; +extern const void * RTTI_PlayerControls; +extern const void * RTTI_MovementHandler; +extern const void * RTTI_LookHandler; +extern const void * RTTI_SprintHandler; +extern const void * RTTI_ReadyWeaponHandler; +extern const void * RTTI_AutoMoveHandler; +extern const void * RTTI_ToggleRunHandler; +extern const void * RTTI_ActivateHandler; +extern const void * RTTI_JumpHandler; +extern const void * RTTI_AttackBlockHandler; +extern const void * RTTI_RunHandler; +extern const void * RTTI_SneakHandler; +extern const void * RTTI_TogglePOVHandler; +extern const void * RTTI_MeleeThrowHandler; +extern const void * RTTI_GrabRotationHandler; +extern const void * RTTI_IStaticAvoidNodeManager; +extern const void * RTTI_StaticAvoidNodeManager; +extern const void * RTTI_AlarmPackage; +extern const void * RTTI_CustomActorPackageData; +extern const void * RTTI_EscortActorPackageData; +extern const void * RTTI_FleePackage; +extern const void * RTTI_GuardPackageData; +extern const void * RTTI_GuardActorPackageData; +extern const void * RTTI_PatrolActorPackageData; +extern const void * RTTI_SpectatorPackage; +extern const void * RTTI_TrespassPackage; +extern const void * RTTI_ActorKnowledge; +extern const void * RTTI_DetectionCollector; +extern const void * RTTI_DetectionState; +extern const void * RTTI_WeaponSwingHandler; +extern const void * RTTI_AttackWinStartHandler; +extern const void * RTTI_AttackWinEndHandler; +extern const void * RTTI_AttackStopHandler; +extern const void * RTTI_RecoilStopHandler; +extern const void * RTTI_LeftHandSpellFireHandler; +extern const void * RTTI_RightHandSpellFireHandler; +extern const void * RTTI_VoiceSpellFireHandler; +extern const void * RTTI_LeftHandSpellCastHandler; +extern const void * RTTI_RightHandSpellCastHandler; +extern const void * RTTI_VoiceSpellCastHandler; +extern const void * RTTI_WeaponBeginDrawHandler; +extern const void * RTTI_WeaponBeginSheatheHandler; +extern const void * RTTI_WeaponDrawHandler; +extern const void * RTTI_WeaponSheatheHandler; +extern const void * RTTI_CameraOverrideStartHandler; +extern const void * RTTI_CameraOverrideStopHandler; +extern const void * RTTI_HitFrameHandler; +extern const void * RTTI_AnticipateAttackHandler; +extern const void * RTTI_StaggeredStopHandler; +extern const void * RTTI_ChairEnterHandler; +extern const void * RTTI_PlayerChairEnterHandler; +extern const void * RTTI_PlayerTerminalEnterHandler; +extern const void * RTTI_BedEnterHandler; +extern const void * RTTI_PlayerBedEnterHandler; +extern const void * RTTI_ChairFurnitureExitHandler; +extern const void * RTTI_BedFurnitureExitHandler; +extern const void * RTTI_PlayerFurnitureExitHandler; +extern const void * RTTI_KillActorHandler; +extern const void * RTTI_HeadTrackingOnHandler; +extern const void * RTTI_HeadTrackingOffHandler; +extern const void * RTTI_HeadTrackingRotationOnHandler; +extern const void * RTTI_HeadTrackingRotationOffHandler; +extern const void * RTTI_FlightTakeOffHandler; +extern const void * RTTI_FlightCruisingHandler; +extern const void * RTTI_FlightHoveringHandler; +extern const void * RTTI_FlightLandingHandler; +extern const void * RTTI_FlightPerchingHandler; +extern const void * RTTI_FlightLandHandler; +extern const void * RTTI_FlightLandEndHandler; +extern const void * RTTI_FlightActionHandler; +extern const void * RTTI_FlightActionEntryEndHandler; +extern const void * RTTI_FlightActionEndHandler; +extern const void * RTTI_FlightActionGrabHandler; +extern const void * RTTI_FlightActionReleaseHandler; +extern const void * RTTI_FlightCrashLandStartHandler; +extern const void * RTTI_BowDrawnHandler; +extern const void * RTTI_BowReleaseHandler; +extern const void * RTTI_ArrowAttachHandler; +extern const void * RTTI_ArrowDetachHandler; +extern const void * RTTI_BowZoomStartHandler; +extern const void * RTTI_BowZoomStopHandler; +extern const void * RTTI_InterruptCastHandler; +extern const void * RTTI_EndSummonAnimationHandler; +extern const void * RTTI_PickNewIdleHandler; +extern const void * RTTI_DoNotInterruptAnimationHandler; +extern const void * RTTI_DeathStopHandler; +extern const void * RTTI_ActionActivateDoneHandler; +extern const void * RTTI_StopMountCameraHandler; +extern const void * RTTI_PairedStopHandler; +extern const void * RTTI_CameraShakeHandler; +extern const void * RTTI_KillMoveStartHandler; +extern const void * RTTI_KillMoveEndHandler; +extern const void * RTTI_DeathEmoteHandler; +extern const void * RTTI_AddRagdollHandler; +extern const void * RTTI_MotionDrivenHandler; +extern const void * RTTI_AnimationDrivenHandler; +extern const void * RTTI_AllowRotationHandler; +extern const void * RTTI_RemoveRagdollHandler; +extern const void * RTTI_RagdollStartHandler; +extern const void * RTTI_GetUpStartHandler; +extern const void * RTTI_GetUpEndHandler; +extern const void * RTTI_RagdollAndGetUpHandler; +extern const void * RTTI_MountDismountEndHandler; +extern const void * RTTI_NPCAttachHandler; +extern const void * RTTI_NPCDetachHandler; +extern const void * RTTI_EnableCharacterBumperHandler; +extern const void * RTTI_DisableCharacterBumperHandler; +extern const void * RTTI_EnableCollisionHandler; +extern const void * RTTI_DisableCollisionHandler; +extern const void * RTTI_StartFloatingHandler; +extern const void * RTTI_StopFloatingHandler; +extern const void * RTTI_AnimationObjectLoadHandler; +extern const void * RTTI_AnimationObjectDrawHandler; +extern const void * RTTI_EnableCharacterPitchHandler; +extern const void * RTTI_DisableCharacterPitchHandler; +extern const void * RTTI_JumpAnimEventHandler; +extern const void * RTTI_IdleDialogueEnterHandler; +extern const void * RTTI_IdleDialogueExitHandler; +extern const void * RTTI_AnimatedCameraStartHandler; +extern const void * RTTI_AnimatedCameraDeltaStartHandler; +extern const void * RTTI_AnimatedCameraEndHandler; +extern const void * RTTI_PitchOverrideStartHandler; +extern const void * RTTI_PitchOverrideEndHandler; +extern const void * RTTI_ZeroPitchHandler; +extern const void * RTTI_WeaponFireHandler; +extern const void * RTTI_ReloadStateChangeHandler; +extern const void * RTTI_ReloadCompleteHandler; +extern const void * RTTI_SightedStateChangeHandler; +extern const void * RTTI_PlayerSightedStateChangeHandler; +extern const void * RTTI_AttackStateChangeHandler; +extern const void * RTTI_AttackStateChangeHandler0; +extern const void * RTTI_AttackStateChangeHandler1; +extern const void * RTTI_AttackStateChangeHandler2; +extern const void * RTTI_AttackStateChangeHandler3; +extern const void * RTTI_AttackStateChangeHandler4; +extern const void * RTTI_RelaxedStateChangeHandler; +extern const void * RTTI_AlertStateChangeHandler; +extern const void * RTTI_ReadyStateChangeHandler; +extern const void * RTTI_GunDownStateChangeHandler; +extern const void * RTTI_SneakStateChangeHandler; +extern const void * RTTI_PlayerSneakStateChangeHandler; +extern const void * RTTI_LoopingActivateHandler; +extern const void * RTTI_ReevaluateGraphStateHandler; +extern const void * RTTI_UncullBoneHandler; +extern const void * RTTI_CullFurnitureBoneHandler; +extern const void * RTTI_UncullFurnitureBoneHandler; +extern const void * RTTI_CullWeaponsHandler; +extern const void * RTTI_UncullWeaponsHandler; +extern const void * RTTI_IdleFlavorHandler; +extern const void * RTTI_PerformActivationHandler; +extern const void * RTTI_AttachReferenceHandler; +extern const void * RTTI_PipboyBootSequenceHandler; +extern const void * RTTI_FaceEmotionalIdleHandler; +extern const void * RTTI_UseStimpakHandler; +extern const void * RTTI_UseStealthBoyHandler; +extern const void * RTTI_HolotapeLoadedHandler; +extern const void * RTTI_AwakeSoundFadeHandler; +extern const void * RTTI_AwakeSoundStopHandler; +extern const void * RTTI_AwakeSoundPauseHandler; +extern const void * RTTI_AwakeSoundResumeHandler; +extern const void * RTTI_PairedAttackDialogueHandler; +extern const void * RTTI_WeaponAttachHandler; +extern const void * RTTI_WeaponDetachHandler; +extern const void * RTTI_StartPCapVoiceHandler; +extern const void * RTTI_EjectShellCasingHandler; +extern const void * RTTI_SandManKillHandler; +extern const void * RTTI_KnockdownHandler; +extern const void * RTTI_KnockdownTargetHandler; +extern const void * RTTI_SyncDeferDeathHandler; +extern const void * RTTI_SyncEarlyExitHandler; +extern const void * RTTI_PlayerFastEquipSoundHandler; +extern const void * RTTI_DesyncInteractionHandler; +extern const void * RTTI_UpdateSightedHandler; +extern const void * RTTI_AnimationObject; +extern const void * RTTI_BGShkMatFadeController; +extern const void * RTTI_BSDirectAtModifierInterface; +extern const void * RTTI_BSPassByTargetTriggerModifierInterface; +extern const void * RTTI_BSRagdollContactListenerModifierInterface; +extern const void * RTTI_ArrowProjectile; +extern const void * RTTI_BarrierProjectile; +extern const void * RTTI_BeamProjectile; +extern const void * RTTI_ConeProjectile; +extern const void * RTTI_Explosion; +extern const void * RTTI_ExplosionClosestHitCollector; +extern const void * RTTI_FlameProjectile; +extern const void * RTTI_GrenadeProjectile; +extern const void * RTTI_Hazard; +extern const void * RTTI_MissileProjectile; +extern const void * RTTI_Projectile; +extern const void * RTTI_HealthDamageFunctor; +extern const void * RTTI_VATS; +extern const void * RTTI_IValidateGoalFunctor; +extern const void * RTTI_VATSUtils__ValidateStrangerLineOfSightGoalToOriginFunctor; +extern const void * RTTI_CombatAimController; +extern const void * RTTI_CombatAimControllerBase; +extern const void * RTTI_CombatTrackTargetAimController; +extern const void * RTTI_CombatMeleeAimController; +extern const void * RTTI_CombatProjectileAimController; +extern const void * RTTI_CombatDisableAimController; +extern const void * RTTI_CombatMatchTargetAimController; +extern const void * RTTI_CombatProjectileDebugData; +extern const void * RTTI_CombatAreaReference; +extern const void * RTTI_CombatAreaStandard; +extern const void * RTTI_CombatAreaHoldPosition; +extern const void * RTTI_CombatBlackboardMemberBase; +extern const void * RTTI_CombatCoverSearchResult; +extern const void * RTTI_CombatCoverLocation; +extern const void * RTTI_CombatCoverSearchDebugData; +extern const void * RTTI_CombatCoverSearch; +extern const void * RTTI_CombatEnterCoverPathController; +extern const void * RTTI_CombatMantlePathController; +extern const void * RTTI_CombatCluster; +extern const void * RTTI_CombatGroupDetectionListener; +extern const void * RTTI_CombatFormObject; +extern const void * RTTI_CombatInventoryItem; +extern const void * RTTI_CombatInventoryItemComposite; +extern const void * RTTI_CombatInventoryItemMelee; +extern const void * RTTI_CombatInventoryItemRanged; +extern const void * RTTI_CombatInventoryItemThrown; +extern const void * RTTI_CombatInventoryItemGrenade; +extern const void * RTTI_CombatInventoryItemShield; +extern const void * RTTI_CombatInventoryItemOneHandedBlock; +extern const void * RTTI_CombatInventoryItemTorch; +extern const void * RTTI_CombatInventoryItemMagic; +extern const void * RTTI_CombatMagicCaster; +extern const void * RTTI_CombatInventoryItemStaff; +extern const void * RTTI_CombatInventoryItemPotion; +extern const void * RTTI_CombatInventoryItemScroll; +extern const void * RTTI_CombatMagicItemData; +extern const void * RTTI_CombatMagicItemSkillChecker; +extern const void * RTTI_CombatMagicCasterOffensive; +extern const void * RTTI_CombatMagicCasterWard; +extern const void * RTTI_CombatMagicCasterRestore; +extern const void * RTTI_CombatMagicCasterSummon; +extern const void * RTTI_CombatMagicCasterStagger; +extern const void * RTTI_CombatMagicCasterDisarm; +extern const void * RTTI_CombatMagicCasterCloak; +extern const void * RTTI_CombatMagicCasterLight; +extern const void * RTTI_CombatMagicCasterInvisibility; +extern const void * RTTI_CombatMagicCasterChameleon; +extern const void * RTTI_CombatMagicCasterBoundItem; +extern const void * RTTI_CombatMagicCasterArmor; +extern const void * RTTI_CombatMagicCasterTargetEffect; +extern const void * RTTI_CombatMagicCasterReanimate; +extern const void * RTTI_CombatMagicCasterParalyze; +extern const void * RTTI_CombatMagicCasterScript; +extern const void * RTTI_CombatTargetLocationSearchResult; +extern const void * RTTI_CombatTargetLocation; +extern const void * RTTI_CombatTargetLocationSearch; +extern const void * RTTI_CombatTargetSelector; +extern const void * RTTI_CombatTargetSelectorStandard; +extern const void * RTTI_CombatTargetSelectorPreferred; +extern const void * RTTI_CombatTargetSelectorFixed; +extern const void * RTTI_CombatTargetSelectorRandom; +extern const void * RTTI_CombatThreat; +extern const void * RTTI_CombatThreatExplosion; +extern const void * RTTI_CombatThreatProjectile; +extern const void * RTTI_CombatThreatLOF; +extern const void * RTTI_CombatThreatMelee; +extern const void * RTTI_ProjectileLOSCollector; +extern const void * RTTI_CombatBehaviorResource; +extern const void * RTTI_CombatBehaviorTreeRootNode; +extern const void * RTTI_CombatBehaviorTreeLinkNode; +extern const void * RTTI_CombatBehaviorTree; +extern const void * RTTI_CombatBehaviorTreeCombat; +extern const void * RTTI_CombatBehaviorTreeNode; +extern const void * RTTI_CombatAcquireSearchDebugData; +extern const void * RTTI_CombatBehaviorTreeAcquireObject; +extern const void * RTTI_CombatBehaviorTreeAction; +extern const void * RTTI_CombatBehaviorTreeActionEquipmentSelector; +extern const void * RTTI_CombatBehaviorTreeAvoidThreat; +extern const void * RTTI_CombatBehaviorTreeBlock; +extern const void * RTTI_CombatApproachTargetPathController; +extern const void * RTTI_CombatBehaviorTreeCloseMovement; +extern const void * RTTI_CombatBehaviorTreeFindAttackLocation; +extern const void * RTTI_CombatFlankingSearchData; +extern const void * RTTI_CombatBehaviorTreeFlankingMovement; +extern const void * RTTI_CombatBehaviorTreeFlee; +extern const void * RTTI_CombatBehaviorTreeHide; +extern const void * RTTI_CombatBehaviorTreeFlight; +extern const void * RTTI_DiveBombPathController; +extern const void * RTTI_CombatBehaviorTreeLowCombat; +extern const void * RTTI_CombatBehaviorTreeMagic; +extern const void * RTTI_CombatStaticBlackboardMemberBase; +extern const void * RTTI_CombatMeleeDebugData; +extern const void * RTTI_CombatBehaviorTreeMelee; +extern const void * RTTI_CombatDisableActionController; +extern const void * RTTI_CombatBehaviorTreeCheckUnreachableTarget; +extern const void * RTTI_CombatBehaviorTreeReturnToCombatArea; +extern const void * RTTI_CombatBehaviorTreeFindValidLocation; +extern const void * RTTI_CombatBehaviorTreeMovement; +extern const void * RTTI_CombatBehaviorTreeBow; +extern const void * RTTI_CombatBehaviorTreeRangedMovement; +extern const void * RTTI_CombatBehaviorTreeSearch; +extern const void * RTTI_CombatSearchLockData; +extern const void * RTTI_CombatBehaviorTreeUseCover; +extern const void * RTTI_CombatBehaviorMoveInCover; +extern const void * RTTI_CombatFindCoverPathSpeedController; +extern const void * RTTI_CombatChangePositionPathController; +extern const void * RTTI_CombatBehaviorTreeUsePotion; +extern const void * RTTI_CombatPathDestinationFollowActor; +extern const void * RTTI_CombatAnimatedPath; +extern const void * RTTI_CombatPathBuilder; +extern const void * RTTI_CombatNavmeshSearch; +extern const void * RTTI_IPathBuilder; +extern const void * RTTI_CombatPathBuilderStandard; +extern const void * RTTI_CombatPathBuilderOpen; +extern const void * RTTI_CombatPathMovementMessage; +extern const void * RTTI_CombatPathingDebugData; +extern const void * RTTI_CombatPathingRequest; +extern const void * RTTI_CombatPathingRequestStandard; +extern const void * RTTI_CombatPathingRequestAdapter; +extern const void * RTTI_CombatPathingRequestMultiGoal; +extern const void * RTTI_CombatPathDestinationRefs; +extern const void * RTTI_CombatFollowTargetPathController; +extern const void * RTTI_CombatPathingTweener; +extern const void * RTTI_HorseCameraState; +extern const void * RTTI_PlayerCamera; +extern const void * RTTI_AutoVanityState; +extern const void * RTTI_FreeCameraState; +extern const void * RTTI_IronSightsState; +extern const void * RTTI_FurnitureCameraState; +extern const void * RTTI_PlayerCameraTransitionState; +extern const void * RTTI_BleedoutCameraState; +extern const void * RTTI_FirstPersonState; +extern const void * RTTI_TweenMenuCameraState; +extern const void * RTTI_VATSCameraState; +extern const void * RTTI_BookMenu; +extern const void * RTTI_Console; +extern const void * RTTI_IStringForwarder; +extern const void * RTTI_ConsoleNativeUIMenu; +extern const void * RTTI_OpenContainerMenuMessage; +extern const void * RTTI_ContainerMenu; +extern const void * RTTI_nsContainerMenu__ConfirmTakeAllTheThingsCallback; +extern const void * RTTI_nsContainerMenu__CanPickPocketAliasChecker; +extern const void * RTTI_nsContainerMenu__ValidFunc; +extern const void * RTTI_CreditsMenu; +extern const void * RTTI_CursorMenu; +extern const void * RTTI_DialogueMenu; +extern const void * RTTI_FaderMenu; +extern const void * RTTI_FavoritesManager; +extern const void * RTTI_FavoritesMenu; +extern const void * RTTI_PowerArmorGeometry; +extern const void * RTTI_PowerArmorHUDMenu; +extern const void * RTTI_HUDMenu; +extern const void * RTTI_HUDPowerArmorLowBatterWarningText; +extern const void * RTTI_HUDStealthMeter; +extern const void * RTTI_HUDLocationText; +extern const void * RTTI_ExplosiveIndicators; +extern const void * RTTI_DirectionalHitIndicators; +extern const void * RTTI_HUDFlashLightWidget; +extern const void * RTTI_PowerArmorHUDUtils__MainStatusHandler; +extern const void * RTTI_Inventory3DManager; +extern const void * RTTI_nsInventory3DManager__NewInventoryMenuItemMaterialSwapMaterialLoadTask; +extern const void * RTTI_nsInventory3DManager__NewInventoryMenuItemMaterialSwapTextureLoadTask; +extern const void * RTTI_LoadingMenu; +extern const void * RTTI_LockpickingMenu; +extern const void * RTTI_MainMenu; +extern const void * RTTI_nsMainMenu__DeviceSelectCallback; +extern const void * RTTI_nsMainMenu__NoDurangoUserSignedInCallback; +extern const void * RTTI_nsMainMenu__DLCChanged_ConfirmResetCallback; +extern const void * RTTI_MenuControls; +extern const void * RTTI_GFxConvertHandler; +extern const void * RTTI_DisconnectHandler; +extern const void * RTTI_ClickHandler; +extern const void * RTTI_QuickSaveLoadHandler; +extern const void * RTTI_MenuOpenHandler; +extern const void * RTTI_ScreenshotHandler; +extern const void * RTTI_CameraZoomHandler; +extern const void * RTTI_PipboyHandler; +extern const void * RTTI_MessageBoxData; +extern const void * RTTI_MessageBoxMenu; +extern const void * RTTI_MessageBoxMenu__FXMessageBoxMenu; +extern const void * RTTI_SafeZoneMenu; +extern const void * RTTI_SitWaitMenu; +extern const void * RTTI_SleepWaitMenu; +extern const void * RTTI_TitleSequenceMenu; +extern const void * RTTI_TutorialMenu; +extern const void * RTTI_UIBlurManager; +extern const void * RTTI_FaderData; +extern const void * RTTI_RefHandleUIData; +extern const void * RTTI_TESFormUIData; +extern const void * RTTI_LoadingMenuData; +extern const void * RTTI_DialogueMenuUtils__DialogueMessageData; +extern const void * RTTI_GameScript__Internal__AnimationCallbacks; +extern const void * RTTI_GameScript__DelayFunctor; +extern const void * RTTI_GameScript__DelayFunctorFactory; +extern const void * RTTI_BSScript__IHandleReaderWriter; +extern const void * RTTI_GameScript__BaseHandleReaderWriter; +extern const void * RTTI_GameScript__DataFileHandleReaderWriter; +extern const void * RTTI_GameScript__SaveFileHandleReaderWriter; +extern const void * RTTI_GameScript__CombatEventHandler; +extern const void * RTTI_GameScript__FragmentEventHandler; +extern const void * RTTI_GameScript__CustomEventHandler; +extern const void * RTTI_GameScript__Logger; +extern const void * RTTI_GameScript__InventoryEventHandler; +extern const void * RTTI_GameScript__Internal__LOSEventData; +extern const void * RTTI_GameScript__LOSEventHandler; +extern const void * RTTI_GameScript__Internal__LOSDetectionEventData; +extern const void * RTTI_GameScript__Internal__LOSDirectEventData; +extern const void * RTTI_GameScript__MenuEventHandler; +extern const void * RTTI_GameScript__PathingCallbackMgr; +extern const void * RTTI_GameScript__Internal__IProfileCallQuery; +extern const void * RTTI_GameScript__Profiler; +extern const void * RTTI_GameScript__QuestCallbackMgr; +extern const void * RTTI_GameScript__SavePatcher; +extern const void * RTTI_GameScript__SleepEventHandler; +extern const void * RTTI_GameScript__SoundCallbackMgr; +extern const void * RTTI_BSScript__IStore; +extern const void * RTTI_GameScript__Store; +extern const void * RTTI_BSResource__ArchiveFoundStreamCB; +extern const void * RTTI_BSCounterStorage; +extern const void * RTTI_BSScript__IObjectHandlePolicy; +extern const void * RTTI_GameVM; +extern const void * RTTI_GameScript__HandlePolicy; +extern const void * RTTI_GameScript__ObjectBindPolicy; +extern const void * RTTI_BSScript__IStackCallbackFunctor; +extern const void * RTTI_BSScript__IStackCallbackSaveInterface; +extern const void * RTTI_BSScript__IProfilePolicy; +extern const void * RTTI_BSScript__ILoader; +extern const void * RTTI_BSScript__ObjectBindPolicy; +extern const void * RTTI_BSScript__IClientVM; +extern const void * RTTI_BSScript__ISavePatcherInterface; +extern const void * RTTI_GameScript__BasicEventHandler; +extern const void * RTTI_GameScript__TeleportEventHandler; +extern const void * RTTI_GameScript__TimerEventHandler; +extern const void * RTTI_GameScript__TrackedStatsEventHandler; +extern const void * RTTI_GameScript__DistanceEventHandler; +extern const void * RTTI_GameScript__WaitEventHandler; +extern const void * RTTI_CCallbackBase; +extern const void * RTTI_BSSteamSystemUtility; +extern const void * RTTI_BSSteamAwardsSystemUtility; +extern const void * RTTI_Json__StyledWriter; +extern const void * RTTI_Json__Exception; +extern const void * RTTI_Json__RuntimeError; +extern const void * RTTI_Json__LogicError; +extern const void * RTTI_Json__CharReader; +extern const void * RTTI_Json__CharReader__Factory; +extern const void * RTTI_Json__CharReaderBuilder; +extern const void * RTTI_Json__StreamWriter; +extern const void * RTTI_Json__StreamWriter__Factory; +extern const void * RTTI_Json__StreamWriterBuilder; +extern const void * RTTI_Json__Writer; +extern const void * RTTI_Json__OurCharReader; +extern const void * RTTI_Json__BuiltStyledStreamWriter; +extern const void * RTTI_hkLoader; +extern const void * RTTI_BShkbHkxDB__QueuedHandles; +extern const void * RTTI_BShkbGlobalTransitionData; +extern const void * RTTI_BSAnimationDBData; +extern const void * RTTI_BShkbHkxDB__ProjectDBData; +extern const void * RTTI_BShkbHkxDB__hkxDBData; +extern const void * RTTI_BShkbHkxDB__StreamAdaptor; +extern const void * RTTI_hknpRefMaterial; +extern const void * RTTI_hknpMaterialLibrary; +extern const void * RTTI_hknpSurfaceVelocity; +extern const void * RTTI_hknpLinearSurfaceVelocity; +extern const void * RTTI_hkReferencedObjectLock; +extern const void * RTTI_hkTask; +extern const void * RTTI_hknpConstraintSolver; +extern const void * RTTI_hknpWeldingModifier; +extern const void * RTTI_hknpWorld; +extern const void * RTTI_hknpSimulation; +extern const void * RTTI_hknpContactConstraintSolver; +extern const void * RTTI_hknpCollisionDetector; +extern const void * RTTI_hkSecondaryCommandDispatcher; +extern const void * RTTI_hknpInternalCommandProcessor; +extern const void * RTTI_hknpConstraintAtomSolver; +extern const void * RTTI_hknpDefaultModifierSet; +extern const void * RTTI_hknpPostCollideTask; +extern const void * RTTI_hknpPostSolveTask; +extern const void * RTTI_hkDebugCommandProcessor; +extern const void * RTTI_hknpConvexCompositeCollisionDetector; +extern const void * RTTI_hknpCompositeCompositeCollisionDetector; +extern const void * RTTI_hknpCompositeCollisionDetector; +extern const void * RTTI_hknpSetShapeKeyACdDetector; +extern const void * RTTI_hknpShapeKeyArrayCacheCdDetector; +extern const void * RTTI_hknpApiCommandProcessor; +extern const void * RTTI_hknpManifoldEventCreator; +extern const void * RTTI_hknpSingleThreadedSimulation; +extern const void * RTTI_hknpContactImpulseClippedEventCreator; +extern const void * RTTI_hknpContactImpulseEventCreator; +extern const void * RTTI_hknpMassChangerModifier; +extern const void * RTTI_hknpTriggerModifier; +extern const void * RTTI_hknpRestitutionModifier; +extern const void * RTTI_hknpSoftContactModifier; +extern const void * RTTI_hknpTriangleWeldingModifier; +extern const void * RTTI_hknpNeighborWeldingModifier; +extern const void * RTTI_hknpMotionWeldingModifier; +extern const void * RTTI_hknpConvexConvexCollisionDetector; +extern const void * RTTI_hknpSignedDistanceFieldCollisionDetector; +extern const void * RTTI_hknpTriggerCollisionDetector; +extern const void * RTTI_hknpConstraintForceEventCreator; +extern const void * RTTI_hknpConstraintForceExceededEventCreator; +extern const void * RTTI_hknpMxContactSolver; +extern const void * RTTI_hknpStreamContactSolverBase; +extern const void * RTTI_hknpStreamContactSolver; +extern const void * RTTI_hknpActionManager; +extern const void * RTTI_hknpShapeMassProperties; +extern const void * RTTI_hknpPhysicsSystem; +extern const void * RTTI_hknpCompositeShape; +extern const void * RTTI_hknpHeightFieldShape; +extern const void * RTTI_hknpCompressedHeightFieldShape; +extern const void * RTTI_hkCrc32StreamWriter; +extern const void * RTTI_hkTagfileWriter; +extern const void * RTTI_hkTagfileWriter__AddDataObjectListener; +extern const void * RTTI_hkTypeInfoRegistry; +extern const void * RTTI_hkTagfileReader; +extern const void * RTTI_hkBinaryTagfileWriter; +extern const void * RTTI_hkXmlTagfileReader; +extern const void * RTTI_hkXmlTagfileWriter; +extern const void * RTTI_hkMemoryAllocator; +extern const void * RTTI_hkContainerTempAllocator__Allocator; +extern const void * RTTI_hkContainerHeapAllocator__Allocator; +extern const void * RTTI_hkContainerDebugAllocator__Allocator; +extern const void * RTTI_hkVtableClassRegistry; +extern const void * RTTI_hkClassNameRegistry; +extern const void * RTTI_hkBuiltinTypeRegistry; +extern const void * RTTI_hkDynamicClassNameRegistry; +extern const void * RTTI_hkDefaultClassNameRegistry; +extern const void * RTTI_hkDefaultBuiltinTypeRegistry; +extern const void * RTTI_hkLifoAllocator; +extern const void * RTTI_hkcdGskBase__ShapeInterface; +extern const void * RTTI_hkcdGsk_Vector4ShapeInterface; +extern const void * RTTI_hknpTriangleShape; +extern const void * RTTI_hknpDynamicCompoundShapeData; +extern const void * RTTI_hknpShapeKeyMask; +extern const void * RTTI_hknpCompoundShapeKeyMask; +extern const void * RTTI_hknpStaticCompoundShapeData; +extern const void * RTTI_hknpCompoundShapeInternalsKeyMask; +extern const void * RTTI_hknpStaticCompoundShapeKeyMask; +extern const void * RTTI_hkGeometryUtils__IVertices; +extern const void * RTTI_hkcdSimdTree; +extern const void * RTTI_hkcdSimdTree__LeafCollector; +extern const void * RTTI_hknpCompressedMeshShapeData; +extern const void * RTTI_hknpCompressedMeshShapeInternals__KeyMask; +extern const void * RTTI_BSAnimationGraphManager; +extern const void * RTTI_SubgraphAndOffsetDBData; +extern const void * RTTI_AnimationClipDataSingleton; +extern const void * RTTI_hkaSkeleton; +extern const void * RTTI_hkbEventPayload; +extern const void * RTTI_hkbStringEventPayload; +extern const void * RTTI_hkbAnimationBindingSet; +extern const void * RTTI_BShkbAnimationGraph; +extern const void * RTTI_BSIRagdollDriver; +extern const void * RTTI_BSAnimationGraphLoadScrapper; +extern const void * RTTI_bhkIWorldStepListener; +extern const void * RTTI_AnimationSpeedInformationDBData; +extern const void * RTTI_hknpCompoundShape; +extern const void * RTTI_hknpDynamicCompoundShape; +extern const void * RTTI_hkcdSimdTree__BuildContext; +extern const void * RTTI_hknpExternMeshShapeData; +extern const void * RTTI_hknpExternMeshShapeBuildContext; +extern const void * RTTI_hknpCharacterContext; +extern const void * RTTI_IAnimationClipLoaderSingleton; +extern const void * RTTI_AnimationFileManagerSingleton; +extern const void * RTTI_AnimationStanceDataSingletonDBData; +extern const void * RTTI_IAnimationStanceData; +extern const void * RTTI_hkFileSystem__Iterator__Impl; +extern const void * RTTI_hkErrStream; +extern const void * RTTI_hknpScaledConvexShapeBase; +extern const void * RTTI_hknpScaledConvexShape; +extern const void * RTTI_hknpMotionPropertiesLibrary; +extern const void * RTTI_AnimationSpeedContour; +extern const void * RTTI_IndividualClipAnimationSpeedContour; +extern const void * RTTI_SpeedSampledAnimationSpeedContour; +extern const void * RTTI_CollectionAnimationSpeedContour; +extern const void * RTTI_hkbBehaviorGraph__GlobalTransitionData; +extern const void * RTTI_hkbSymbolIdMap; +extern const void * RTTI_IBehaviorGraphSwapSingleton; +extern const void * RTTI_IBehaviorGraphSymbolSwapSingleton; +extern const void * RTTI_BSBehaviorGraphSwapSingleton; +extern const void * RTTI_BShkbUtils__SearchAllStateMachineChildrenFunctor; +extern const void * RTTI_IiStateTaggingListenerSingleton; +extern const void * RTTI_BSiStateTaggingListenerSingleton; +extern const void * RTTI_hkbRagdollDriver; +extern const void * RTTI_BSIRagdollDriverSingleton; +extern const void * RTTI_hkbAssetLoader; +extern const void * RTTI_IStateMachineCallbackSingleton; +extern const void * RTTI_BSStateMachineCallbackSingleton; +extern const void * RTTI_BSSubBehaviorGraphSingletonData; +extern const void * RTTI_BSSubBehaviorGraphSingletonDataAddToDeferredDeleterTask; +extern const void * RTTI_BShkVisibilityController; +extern const void * RTTI_hkbNode; +extern const void * RTTI_hkbGenerator; +extern const void * RTTI_hkbStateListener; +extern const void * RTTI_BSiStateTaggingGenerator; +extern const void * RTTI_BSiStateTaggingStateListener; +extern const void * RTTI_BGSGamebryoSequenceGenerator; +extern const void * RTTI_BSLimbIKModifierCastInfo; +extern const void * RTTI_BSILimbIKModifierSingleton; +extern const void * RTTI_hkaFootPlacementIkSolver; +extern const void * RTTI_BSIAlignBoneModifierSingleton; +extern const void * RTTI_BSILookAtModifierSingleton; +extern const void * RTTI_hknpCharacterStateManager; +extern const void * RTTI_hkbFootIkDriver; +extern const void * RTTI_hkbPoseMatchingGeneratorInternalState; +extern const void * RTTI_hkbRagdollInterface; +extern const void * RTTI_hkbnpSerializedRagdollInterface; +extern const void * RTTI_hkbnpRagdollSkeletonRemapData; +extern const void * RTTI_hkbnpRagdollScaledSkeleton; +extern const void * RTTI_hkbnpRagdollInterface; +extern const void * RTTI_BSIDirectAtModifierSingleton; +extern const void * RTTI_BSIPassByTargetTriggerModifierSingleton; +extern const void * RTTI_BSIRagdollContactListenerModifierSingleton; +extern const void * RTTI_hkbBehaviorGraph; +extern const void * RTTI_hkbNodeInternalStateInfo; +extern const void * RTTI_hkbVariableValueSet; +extern const void * RTTI_hkBufferedStreamReader; +extern const void * RTTI_hkSeekableStreamReader; +extern const void * RTTI_hkResource; +extern const void * RTTI_LOCALNAMESPACE__hkNativeResource; +extern const void * RTTI_hkOstream; +extern const void * RTTI_hkMemoryTrackStreamWriter; +extern const void * RTTI_hkArrayStreamWriter; +extern const void * RTTI_hkSignal0__Slot; +extern const void * RTTI_hkTaskQueue; +extern const void * RTTI_hkTaskQueue__EmptyTask; +extern const void * RTTI_hkBlockStreamCommandWriter; +extern const void * RTTI_ErrorCommandDispatcher; +extern const void * RTTI_hknpBroadPhaseConfig; +extern const void * RTTI_hknpDefaultBroadPhaseConfig; +extern const void * RTTI_hknpShapeTagCodec; +extern const void * RTTI_hknpNullShapeTagCodec; +extern const void * RTTI_hknpConstraintCollisionFilter; +extern const void * RTTI_hknpBodyQualityLibrary; +extern const void * RTTI_hknpEventDispatcher; +extern const void * RTTI_hknpEventMergeAndDispatcher; +extern const void * RTTI_hknpSpaceSplitter; +extern const void * RTTI_hknpSingleCellSpaceSplitter; +extern const void * RTTI_hknpGridSpaceSplitter; +extern const void * RTTI_hknpDynamicSpaceSplitter; +extern const void * RTTI_hknpMultithreadedSimulation; +extern const void * RTTI_hknpNarrowPhaseTask; +extern const void * RTTI_hknpSurfaceVelocityModifier; +extern const void * RTTI_hknpBroadPhase; +extern const void * RTTI_hknpHybridBroadPhase; +extern const void * RTTI_hkOArchive; +extern const void * RTTI_hkPackfileWriter; +extern const void * RTTI_hkIstream; +extern const void * RTTI_hkDataWorld; +extern const void * RTTI_hkDataObjectImpl; +extern const void * RTTI_hkDataArrayImpl; +extern const void * RTTI_hkDataClassImpl; +extern const void * RTTI_hkDataRefCounted; +extern const void * RTTI_hkDataClassDict; +extern const void * RTTI_hkDataObjectDict; +extern const void * RTTI_hkDataWorldDict; +extern const void * RTTI_VariableIntArrayImplementation; +extern const void * RTTI_ByteArrayImplementation; +extern const void * RTTI_ArrayOfTuplesImplementation; +extern const void * RTTI_ArrayOfTuplesImplementation__View; +extern const void * RTTI_RealArrayImplementation; +extern const void * RTTI_RealArrayView; +extern const void * RTTI_VecArrayImplementation; +extern const void * RTTI_PointerArrayImplementation; +extern const void * RTTI_CstringArrayImplementation; +extern const void * RTTI_ArrayArrayImplementation; +extern const void * RTTI_StructArrayImplementation; +extern const void * RTTI_StructArrayImplementation__Object; +extern const void * RTTI_hkDataClassNative; +extern const void * RTTI_hkDataWorldNative; +extern const void * RTTI_hkDataObjectNative; +extern const void * RTTI_hkDataArrayNative; +extern const void * RTTI_hkSubStreamWriter; +extern const void * RTTI_hkBinaryPackfileWriter; +extern const void * RTTI_hkBinaryTagfileReader; +extern const void * RTTI_hkVersionPatchManager; +extern const void * RTTI_hkVersionPatchManager__ClassWrapper; +extern const void * RTTI_hkDefaultClassWrapper; +extern const void * RTTI_hkSerializeDeprecated; +extern const void * RTTI_hkMallocAllocator; +extern const void * RTTI_hkStaticClassNameRegistry; +extern const void * RTTI_hknpGlobals; +extern const void * RTTI_hkgpConvexHull; +extern const void * RTTI_hkcdSimdTree__IParallelBuild__IRunnable; +extern const void * RTTI_hkcdSimdTreeUtils__Build__EmptyRunnable; +extern const void * RTTI_hkcdSimdTreeUtils__Build__IRefit; +extern const void * RTTI_hkcdSimdTreeUtils__Build__Task; +extern const void * RTTI_hkbEventPayloadList; +extern const void * RTTI_hkaSkeletonMapper; +extern const void * RTTI_hkbAnimationBindingWithTriggers; +extern const void * RTTI_hkbCharacterSetup; +extern const void * RTTI_hkbHandIkDriver; +extern const void * RTTI_hkbHandle; +extern const void * RTTI_hkbCharacter; +extern const void * RTTI_hkbDockingDriver; +extern const void * RTTI_hkaBoneAttachment; +extern const void * RTTI_hknpArrayAction; +extern const void * RTTI_hknpEaseConstraintsAction; +extern const void * RTTI_hkHardwareInfo; +extern const void * RTTI_hkDummySingleton; +extern const void * RTTI_hkStdioStreamReader; +extern const void * RTTI_hkWin32StreamWriter; +extern const void * RTTI_hkBufferedStreamWriter; +extern const void * RTTI_hknpDecoratorShape; +extern const void * RTTI_hkbBindable; +extern const void * RTTI_hkbStateMachine; +extern const void * RTTI_hkbStateMachine__TransitionInfoArray; +extern const void * RTTI_hkbStateMachine__EventPropertyArray; +extern const void * RTTI_hkbStateMachine__StateInfo; +extern const void * RTTI_BSBehaviorGraphSwapGenerator; +extern const void * RTTI_hkbReferencePoseGenerator; +extern const void * RTTI_hkbBehaviorReferenceGenerator; +extern const void * RTTI_hkaAnimationBinding; +extern const void * RTTI_hkbClipGenerator; +extern const void * RTTI_hkbClipTriggerArray; +extern const void * RTTI_hkbBehaviorGraphStringData; +extern const void * RTTI_hkaAnimationContainer; +extern const void * RTTI_hkbBindableCollector; +extern const void * RTTI_hkbBindable__CacheBindablesCollector; +extern const void * RTTI_hkbNode__BoundVariablesCollector; +extern const void * RTTI_hkbReferencedGeneratorSyncInfo; +extern const void * RTTI_hkbWorld; +extern const void * RTTI_hkbSceneModifier; +extern const void * RTTI_hkbFootIkSceneModifier; +extern const void * RTTI_hkbHandIkSceneModifier; +extern const void * RTTI_hkbHandIkFixupSceneModifier; +extern const void * RTTI_hkbDockingSceneModifier; +extern const void * RTTI_hkbBoneWeightArray; +extern const void * RTTI_hkbBlenderGeneratorChild; +extern const void * RTTI_hkbBlenderGenerator; +extern const void * RTTI_hkbPoseMatchingGenerator; +extern const void * RTTI_hkbTransitionEffect; +extern const void * RTTI_hkbGeneratorTransitionEffect; +extern const void * RTTI_hkbPhysicsInterface; +extern const void * RTTI_hknpGroupCollisionFilter; +extern const void * RTTI_hkbnpPhysicsInterface; +extern const void * RTTI_hknpCharacterProxyCinfo; +extern const void * RTTI_hknpCharacterRigidBodyCinfo; +extern const void * RTTI_hknpRagdollData; +extern const void * RTTI_hknpRagdoll; +extern const void * RTTI_hknpBreakableConstraintData; +extern const void * RTTI_hkaDefaultAnimationControl; +extern const void * RTTI_hkaDefaultAnimationControlMapperData; +extern const void * RTTI_hkbProjectStringData; +extern const void * RTTI_hkbProjectData; +extern const void * RTTI_hkMemoryTrackStreamReader; +extern const void * RTTI_hkStackTracer; +extern const void * RTTI_hkDebugDisplay; +extern const void * RTTI_hkDebugDisplayHandler; +extern const void * RTTI_hkProcess; +extern const void * RTTI_hkDebugDisplayProcess; +extern const void * RTTI_hknpPairCollisionFilter; +extern const void * RTTI_hknpSolverData; +extern const void * RTTI_hknpSingleThreadedSolverData; +extern const void * RTTI_hknpPrepareNarrowPhaseTask; +extern const void * RTTI_hknpPrepare2ndRoundNarrowPhaseTask; +extern const void * RTTI_hknpPrepareConstraintsTask; +extern const void * RTTI_hknpBuildConstraintJacobiansTask; +extern const void * RTTI_hknpSolverTask; +extern const void * RTTI_hknpConvexConvexShapeBaseInterface; +extern const void * RTTI_hknpHBPUpdateDirtyBodiesTask; +extern const void * RTTI_hknpHBPUpdateTreeTask; +extern const void * RTTI_hknpHBPTreeVsTreeTask; +extern const void * RTTI_hknpHBPFinishUpdateTask; +extern const void * RTTI_hkDisplayGeometry; +extern const void * RTTI_hkDisplaySphere; +extern const void * RTTI_hkDisplayCapsule; +extern const void * RTTI_hkDisplayConvex; +extern const void * RTTI_hkDisplayAABB; +extern const void * RTTI_hkDisplayWireframe; +extern const void * RTTI_hkpConstraintData; +extern const void * RTTI_hkpLimitedHingeConstraintData; +extern const void * RTTI_hkpFixedConstraintData; +extern const void * RTTI_hkpDeformableFixedConstraintData; +extern const void * RTTI_hkpRagdollLimitsData; +extern const void * RTTI_hkpHingeLimitsData; +extern const void * RTTI_hkpStiffSpringConstraintData; +extern const void * RTTI_hkpBallAndSocketConstraintData; +extern const void * RTTI_hkpWheelConstraintData; +extern const void * RTTI_hkpHingeConstraintData; +extern const void * RTTI_hkpPrismaticConstraintData; +extern const void * RTTI_hkpRagdollConstraintData; +extern const void * RTTI_hkpPointToPlaneConstraintData; +extern const void * RTTI_hkpPulleyConstraintData; +extern const void * RTTI_hkpPointToPathConstraintData; +extern const void * RTTI_hkOffsetOnlyStreamWriter; +extern const void * RTTI_hkObjectWriter; +extern const void * RTTI_hkPlatformObjectWriter; +extern const void * RTTI_hkPlatformObjectWriter__Cache; +extern const void * RTTI_hkMemoryStreamReader; +extern const void * RTTI_hkTypeManager; +extern const void * RTTI_hkPackfileData; +extern const void * RTTI_hkObjectResource; +extern const void * RTTI_hkIArchive; +extern const void * RTTI_hkParserBuffer; +extern const void * RTTI_hkXmlStreamParser; +extern const void * RTTI_hkXmlParser; +extern const void * RTTI_hkXmlParser__StartElement; +extern const void * RTTI_hkXmlParser__EndElement; +extern const void * RTTI_hkXmlParser__Characters; +extern const void * RTTI_hkXmlParser__Node; +extern const void * RTTI_hkgpMesh; +extern const void * RTTI_hkgpMesh__IConvexOverlap; +extern const void * RTTI_hkgpMesh__IConvexOverlap__IConvexShape; +extern const void * RTTI_hkgpMesh__TriangleShape; +extern const void * RTTI_hkgpMesh__ExternShape; +extern const void * RTTI_hkgpTriangulatorBase; +extern const void * RTTI_hkgpJobQueue__IJob; +extern const void * RTTI_IConvexOverlapImpl; +extern const void * RTTI_IConvexOverlapImpl__ShapeInterface; +extern const void * RTTI_hkgpIndexedMesh; +extern const void * RTTI_hkgpIndexedMesh__IVertexRemoval; +extern const void * RTTI_hkgpIndexedMesh__ITriangleRemoval; +extern const void * RTTI_hkgpIndexedMesh__IEdgeCollapse; +extern const void * RTTI_hkgpIndexedMeshInternals__DefaultEdgeCollapseInterface; +extern const void * RTTI_hkbRealEventPayload; +extern const void * RTTI_hkbIntEventPayload; +extern const void * RTTI_hkbNamedEventPayload; +extern const void * RTTI_hkbNamedRealEventPayload; +extern const void * RTTI_hkbNamedIntEventPayload; +extern const void * RTTI_hkbNamedStringEventPayload; +extern const void * RTTI_hkaMirroredSkeleton; +extern const void * RTTI_hkSimpleLocalFrame; +extern const void * RTTI_hkbCharacterControllerDriver; +extern const void * RTTI_hkbCharacterData; +extern const void * RTTI_hkbProjectAssetManager; +extern const void * RTTI_hkDefaultError; +extern const void * RTTI_hkSocket; +extern const void * RTTI_hkSocket__ReaderAdapter; +extern const void * RTTI_hkSocket__WriterAdapter; +extern const void * RTTI_hkbStateMachineInternalState; +extern const void * RTTI_hkaAnimationControl; +extern const void * RTTI_hkbClipGeneratorInternalState; +extern const void * RTTI_hkaAnimation; +extern const void * RTTI_hkaReferencePoseAnimation; +extern const void * RTTI_hkbBehaviorGraphData; +extern const void * RTTI_hkbCondition; +extern const void * RTTI_hkbExpressionCondition; +extern const void * RTTI_hkbParametricMotionGenerator; +extern const void * RTTI_hkbAssetBundle; +extern const void * RTTI_hkbFootIkDriverInfo; +extern const void * RTTI_hkbCharacterStringData; +extern const void * RTTI_hkbHandIkDriverInfo; +extern const void * RTTI_hkbMirroredSkeletonInfo; +extern const void * RTTI_hkbVariableBindingSet; +extern const void * RTTI_hkbAttachmentSceneModifier; +extern const void * RTTI_hkbAttachmentFixupSceneModifier; +extern const void * RTTI_hkbRagdollSceneModifier; +extern const void * RTTI_hkbCharacterControllerSceneModifier; +extern const void * RTTI_hkbBlenderGeneratorInternalState; +extern const void * RTTI_hkbGeneratorTransitionEffectInternalState; +extern const void * RTTI_hkLocalFrameGroup; +extern const void * RTTI_hkLocalFrame; +extern const void * RTTI_hkBlockStreamAllocator; +extern const void * RTTI_hkFixedBlockStreamAllocator; +extern const void * RTTI_hkbnpCharacterProxyController; +extern const void * RTTI_hkbnpCharacterRigidBodyController; +extern const void * RTTI_hkbRagdollController; +extern const void * RTTI_hkbnpRagdollPoweredConstraintController; +extern const void * RTTI_hkbnpRagdollRigidBodyController; +extern const void * RTTI_hkVisualDebugger; +extern const void * RTTI_hkpWrappedConstraintData; +extern const void * RTTI_hkObjectCopier; +extern const void * RTTI_hkbSequencedData; +extern const void * RTTI_hkbEventSequencedData; +extern const void * RTTI_hkbRealVariableSequencedData; +extern const void * RTTI_hkbBoolVariableSequencedData; +extern const void * RTTI_hkbIntVariableSequencedData; +extern const void * RTTI_hkbSequenceStringData; +extern const void * RTTI_hkbModifier; +extern const void * RTTI_hkbSequence; +extern const void * RTTI_hkProcessFactory; +extern const void * RTTI_hknpSolverSchedulerTask; +extern const void * RTTI_hkXmlLexAnalyzer; +extern const void * RTTI_hkgpIndexedMesh__EdgeBarrier; +extern const void * RTTI_hkgpCgoInternal__ICollapse; +extern const void * RTTI_hkLocalFrameCollector; +extern const void * RTTI_hkbClosestLocalFrameCollector; +extern const void * RTTI_hkbInternal__hks__Visitor; +extern const void * RTTI_hkbInternal__hks__MemoryAllocationsByType; +extern const void * RTTI_hkbInternal__hks__netTransport; +extern const void * RTTI_hkbInternal__hks__netTransportTCPIP; +extern const void * RTTI_hkbInternal__LuaPlus__LuaStateOutFile; +extern const void * RTTI_hkbInternal__hks__CompilerStateInterface; +extern const void * RTTI_hkbInternal__hks__CompilerParseAcceptor; +extern const void * RTTI_hkbInternal__hks__CodeGenerator; +extern const void * RTTI_hkbInternal__hks__SimpleCompilerState; +extern const void * RTTI_hkbInternal__hks__Preprocessor; +extern const void * RTTI_hkbInternal__hks__Preprocessor__PreprocessorStateProxy; +extern const void * RTTI_hkbInternal__hks__IProfiler; +extern const void * RTTI_hkbInternal__hks__FrequencyProfiler; +extern const void * RTTI_hkbInternal__hks__GettableProfiler; +extern const void * RTTI_hkbInternal__hks__ChunkVisitor; +extern const void * RTTI_hkbInternal__hks__ReachableChunksAreValid; +extern const void * RTTI_hkbInternal__hks__HeapVisualizer; +extern const void * RTTI_hkbScriptAssetLoader; +extern const void * RTTI_hkBsdSocket; +extern const void * RTTI_hkbCompiledExpressionSet; +extern const void * RTTI_hkbProceduralBlenderGenerator; +extern const void * RTTI_hkxAttributeHolder; +extern const void * RTTI_hkxMesh__UserChannelInfo; +extern const void * RTTI_hkaMeshBinding; +extern const void * RTTI_hkbCharacterController; +extern const void * RTTI_hkbnpCharacterController; +extern const void * RTTI_hknpCharacterProxy; +extern const void * RTTI_hknpCharacterProxyInternals__QueryCollector; +extern const void * RTTI_hknpCharacterRigidBody; +extern const void * RTTI_hknpRagdollMotorController; +extern const void * RTTI_hknpRagdollController; +extern const void * RTTI_hknpRagdollKeyFrameHierarchyController; +extern const void * RTTI_hkProcessHandler; +extern const void * RTTI_hkServerProcessHandler; +extern const void * RTTI_hkCommandRouter; +extern const void * RTTI_hkbSequenceInternalState; +extern const void * RTTI_hkMemorySystem; +extern const void * RTTI_hkbDockingTarget; +extern const void * RTTI_hkbLineDockingTarget; +extern const void * RTTI_hkbPlaneDockingTarget; +extern const void * RTTI_hkxMeshSection; +extern const void * RTTI_hkxMesh; +extern const void * RTTI_hkInspectProcess; +extern const void * RTTI_hkMemorySnapshotProcess; +extern const void * RTTI_hkRemoteObjectProcess; +extern const void * RTTI_hkStatisticsProcess; +extern const void * RTTI_hkDisplaySerializeOStream; +extern const void * RTTI_hkReplayStreamReader; +extern const void * RTTI_hkServerDebugDisplayHandler; +extern const void * RTTI_hkDisplaySerializeIStream; +extern const void * RTTI_hkxIndexBuffer; +extern const void * RTTI_hkxMaterial; +extern const void * RTTI_hkxVertexAnimation; +extern const void * RTTI_hkVdbCommandWriter; +extern const void * RTTI_hkMonitorStreamColorTable; +extern const void * RTTI_hkDisplayCone; +extern const void * RTTI_hkDisplaySemiCircle; +extern const void * RTTI_hkDisplayPlane; +extern const void * RTTI_hkDisplayMesh; +extern const void * RTTI_hkDisplayBox; +extern const void * RTTI_hkDisplayCylinder; +extern const void * RTTI_hkDisplayTaperedCapsule; +extern const void * RTTI_hkxVertexBuffer; +extern const void * RTTI_hkIndexedTransformSet; +extern const void * RTTI_hkxSparselyAnimatedBool; +extern const void * RTTI_hkxSparselyAnimatedInt; +extern const void * RTTI_hkxEnum; +extern const void * RTTI_hkxSparselyAnimatedEnum; +extern const void * RTTI_hkxSparselyAnimatedString; +extern const void * RTTI_hkxAnimatedFloat; +extern const void * RTTI_hkxAnimatedVector; +extern const void * RTTI_hkxAnimatedQuaternion; +extern const void * RTTI_hkxAnimatedMatrix; +extern const void * RTTI_hkTrackerScanSnapshot; +extern const void * RTTI_hkTrackerTypeTreeCache; +extern const void * RTTI_hkTrackerArrayLayoutHandler; +extern const void * RTTI_hkTrackerPointerMapLayoutHandler; +extern const void * RTTI_hkTrackerStringPtrLayoutHandler; +extern const void * RTTI_hkTrackerStringMapLayoutHandler; +extern const void * RTTI_hkTrackerTypeLayout; +extern const void * RTTI_hkTrackerLayoutCalculator; +extern const void * RTTI_hkTrackerLayoutHandler; +extern const void * RTTI_hkTrackerRefPtrLayoutHandler; +extern const void * RTTI_hkTrackerQueueLayoutHandler; +extern const void * RTTI_hkTrackerPadSpuLayoutHandler; +extern const void * RTTI_hkTrackerJobQueueLayoutHandler; +extern const void * RTTI_hkTrackerJobQueueDynamicDataLayoutHandler; +extern const void * RTTI_hkTrackerSetLayoutHandler; +extern const void * RTTI_hkTrackerFlagsLayoutHandler; +extern const void * RTTI_hkTrackerEnumLayoutHandler; +extern const void * RTTI_hkMeshBody; +extern const void * RTTI_hkMemoryMeshBody; +extern const void * RTTI_hkMemoryTracker; +extern const void * RTTI_hkDummyMemoryTrackerImpl; +extern const void * RTTI_hkDefaultMemoryTracker; +extern const void * RTTI_AudioLoadForPlaybackTask; +extern const void * RTTI_AudioLoadToCacheTask; +extern const void * RTTI_BSAudioManagerThread; +extern const void * RTTI_BSNullImplAudio; +extern const void * RTTI_BSIAudioEffectParameters; +extern const void * RTTI_BSAudioEffects__BSOverdriveParams; +extern const void * RTTI_BSAudioEffects__BSStateVariableFilterParams; +extern const void * RTTI_BSAudioEffects__BSDelayEffectParams; +extern const void * RTTI_BSAudio; +extern const void * RTTI_BSGameSound; +extern const void * RTTI_BSXAudio2Audio; +extern const void * RTTI_BSAudioListener; +extern const void * RTTI_BSNullImplAudioListener; +extern const void * RTTI_BSNullImplGameSound; +extern const void * RTTI_BSXAudio2AudioListener; +extern const void * RTTI_BSAudioDataSrcBase; +extern const void * RTTI_BSXAudio2DataSrc; +extern const void * RTTI_BSXAudio2GameSound; +extern const void * RTTI_BSIAudioEffectVisitorBase; +extern const void * RTTI_BSCXAPOWrapper; +extern const void * RTTI_BSCompanionIListener; +extern const void * RTTI_BSCompanionThread; +extern const void * RTTI_BSCompanionAutodiscover; +extern const void * RTTI_BSByteBuffer; +extern const void * RTTI_BSCompanionConnection; +extern const void * RTTI_BSCompanionDirectListener; +extern const void * RTTI_BSCompanionDirectConnection; +extern const void * RTTI_BSCompanionSocket; +extern const void * RTTI_ScrapHeap; +extern const void * RTTI_BSThread; +extern const void * RTTI_CompactingStore__Store; +extern const void * RTTI_CompactingStore__MoveCallback; +extern const void * RTTI_CompactingStore__NoopMoveCallback; +extern const void * RTTI_MemoryHeap; +extern const void * RTTI_UnitTestMemoryHeap; +extern const void * RTTI_IMemoryHeap; +extern const void * RTTI_ZeroOverheadHeap; +extern const void * RTTI_UnitTestZeroOverheadHeap; +extern const void * RTTI_BSTGlobalEvent; +extern const void * RTTI_BSSmallBlockAllocator; +extern const void * RTTI_BSSmallBlockAllocatorUtil__UserPoolBase; +extern const void * RTTI_IMemoryStoreBase; +extern const void * RTTI_IMemoryStore; +extern const void * RTTI_AbstractHeap; +extern const void * RTTI_MouseMoveEvent; +extern const void * RTTI_CursorMoveEvent; +extern const void * RTTI_ThumbstickEvent; +extern const void * RTTI_CharacterEvent; +extern const void * RTTI_DeviceConnectEvent; +extern const void * RTTI_KinectEvent; +extern const void * RTTI_BSInputDevice; +extern const void * RTTI_BSGamepadDevice; +extern const void * RTTI_BSPCKeyboardDevice; +extern const void * RTTI_BSPCMouseDevice; +extern const void * RTTI_BSKeyboardDevice; +extern const void * RTTI_BSPCVirtualKeyboardDevice; +extern const void * RTTI_BSPCGamepadDeviceDelegate; +extern const void * RTTI_BSPCGamepadDeviceHandler; +extern const void * RTTI_BSPCGamepadDevice; +extern const void * RTTI_BSPCOrbisGamepadDevice; +extern const void * RTTI_BSMouseDevice; +extern const void * RTTI_BSVirtualKeyboardDevice; +extern const void * RTTI_NiFile; +extern const void * RTTI_NiBinaryStream; +extern const void * RTTI_NiAllocator; +extern const void * RTTI_BSNiAllocator; +extern const void * RTTI_NiStandardAllocator; +extern const void * RTTI_NiSearchPath; +extern const void * RTTI_NiMemStream; +extern const void * RTTI_BSStringPool__IterationCallback; +extern const void * RTTI_BSSystemFileAsyncFunctor; +extern const void * RTTI_BSSystemFileStreamer__Streamer; +extern const void * RTTI_BSSystemFileStreamer__Streamer__StreamerThread; +extern const void * RTTI_BSSystemFileStreamer__Streamer__ControlThread; +extern const void * RTTI_BSSystemFileStreamer__Streamer__PhysicalReadThread; +extern const void * RTTI_BSJobs__JobThread; +extern const void * RTTI_BSFile; +extern const void * RTTI_BSFile__PageCache__PhysReadType; +extern const void * RTTI_Archive; +extern const void * RTTI_ArchiveFile; +extern const void * RTTI_CompressedArchiveFile; +extern const void * RTTI_BSTaskJobber; +extern const void * RTTI_BSTaskJobber__Task; +extern const void * RTTI_BSSystemFileStorage; +extern const void * RTTI_BSSocket; +extern const void * RTTI_BSSearchPath; +extern const void * RTTI_BSListenSocket; +extern const void * RTTI_BSTaskletManager; +extern const void * RTTI_BSWin32TaskletManager; +extern const void * RTTI_BSTaskletGroupData; +extern const void * RTTI_BSTasklet; +extern const void * RTTI_BSTaskletData; +extern const void * RTTI_BSTaskletManagerCallback; +extern const void * RTTI_BSWin32TaskletGroupData; +extern const void * RTTI_BSResource__IEntryDB; +extern const void * RTTI_BSResource__IEntryDB__PostFlushNotify; +extern const void * RTTI_BSResource__Stream; +extern const void * RTTI_BSResource__AsyncStream; +extern const void * RTTI_BSResource__StreamBase; +extern const void * RTTI_BSResource__Archive2__ReaderStream; +extern const void * RTTI_BSResource__Archive2__Index; +extern const void * RTTI_BSResource__Archive2__Manager__MemoryCacheLocation; +extern const void * RTTI_BSResource__Archive2__AsyncReaderStream; +extern const void * RTTI_BSResource__SDirectory2__ThreadCursor; +extern const void * RTTI_BSResource__LocationTraverser; +extern const void * RTTI_BSResource__CacheDrive; +extern const void * RTTI_BSResource__ICacheDriveOp; +extern const void * RTTI_BSResource__CacheDrive__Impl; +extern const void * RTTI_BSResource__CacheDrive__Task; +extern const void * RTTI_BSResource__MemoryFileLocation; +extern const void * RTTI_BSResource__MemoryFileStream; +extern const void * RTTI_BSResourceNiBinaryStream; +extern const void * RTTI_NiObject; +extern const void * RTTI_NiExtraData; +extern const void * RTTI_NiObjectNET; +extern const void * RTTI_NiNode; +extern const void * RTTI_BSGeometrySegmentData; +extern const void * RTTI_IRendererResourceManager; +extern const void * RTTI_BSFlattenedBoneTree; +extern const void * RTTI_NiAVObject; +extern const void * RTTI_NiTexture; +extern const void * RTTI_NiTimeController; +extern const void * RTTI_NiSwitchNode; +extern const void * RTTI_NiPointLight; +extern const void * RTTI_NiStringExtraData; +extern const void * RTTI_BSPositionData; +extern const void * RTTI_NiCamera; +extern const void * RTTI_NiGeometryData; +extern const void * RTTI_BSGeometry; +extern const void * RTTI_BSSkin__BoneData; +extern const void * RTTI_BSSkin__Instance; +extern const void * RTTI_NiIntegerExtraData; +extern const void * RTTI_NiLight; +extern const void * RTTI_BSTriShape; +extern const void * RTTI_BSDynPosData; +extern const void * RTTI_NiParticles; +extern const void * RTTI_NiStream; +extern const void * RTTI_NiAVObjectPalette; +extern const void * RTTI_NiDefaultAVObjectPalette; +extern const void * RTTI_NiDirectionalLight; +extern const void * RTTI_BSDynamicTriShape; +extern const void * RTTI_NiIntegersExtraData; +extern const void * RTTI_NiCullingProcess; +extern const void * RTTI_BSSegmentedTriShape; +extern const void * RTTI_NiGeometry; +extern const void * RTTI_NiTriBasedGeom; +extern const void * RTTI_NiTriShape; +extern const void * RTTI_NiSkinPartition; +extern const void * RTTI_NiSkinData; +extern const void * RTTI_NiSkinInstance; +extern const void * RTTI_NiShadeProperty; +extern const void * RTTI_NiAlphaAccumulator; +extern const void * RTTI_NiAmbientLight; +extern const void * RTTI_NiBinaryExtraData; +extern const void * RTTI_NiBooleanExtraData; +extern const void * RTTI_NiBSPNode; +extern const void * RTTI_NiColorExtraData; +extern const void * RTTI_NiFloatExtraData; +extern const void * RTTI_NiFloatsExtraData; +extern const void * RTTI_NiFogProperty; +extern const void * RTTI_NiParticlesData; +extern const void * RTTI_NiParticleMeshesData; +extern const void * RTTI_NiParticleMeshes; +extern const void * RTTI_NiSpotLight; +extern const void * RTTI_NiStringsExtraData; +extern const void * RTTI_NiSwitchStringExtraData; +extern const void * RTTI_NiTriShapeData; +extern const void * RTTI_NiTriStripsData; +extern const void * RTTI_NiTriStrips; +extern const void * RTTI_NiVectorExtraData; +extern const void * RTTI_BSLODTriShape; +extern const void * RTTI_NiAdditionalGeometryData; +extern const void * RTTI_NiAccumulator; +extern const void * RTTI_NiBackToFrontAccumulator; +extern const void * RTTI_NiTriBasedGeomData; +extern const void * RTTI_NiPick__Results; +extern const void * RTTI_NiCollisionTraversals__IFindIntersections; +extern const void * RTTI_NiCollisionData; +extern const void * RTTI_NiBoundingVolume; +extern const void * RTTI_NiBoxBV; +extern const void * RTTI_NiCapsuleBV; +extern const void * RTTI_NiHalfSpaceBV; +extern const void * RTTI_NiSphereBV; +extern const void * RTTI_NiUnionBV; +extern const void * RTTI_NiOBBRoot; +extern const void * RTTI_NiIntersector; +extern const void * RTTI_NiBoxSphereIntersector; +extern const void * RTTI_NiBoxCapsuleIntersector; +extern const void * RTTI_NiCapsuleCapsuleIntersector; +extern const void * RTTI_NiCapsuleSphereIntersector; +extern const void * RTTI_NiCapsuleTriIntersector; +extern const void * RTTI_NiSphereTriIntersector; +extern const void * RTTI_NiOBBNode; +extern const void * RTTI_NiOBBLeaf; +extern const void * RTTI_NiControllerManager; +extern const void * RTTI_NiControllerSequence; +extern const void * RTTI_NiTextKeyMatch; +extern const void * RTTI_NiMultiTargetTransformController; +extern const void * RTTI_BSMultiTargetTreadTransfController; +extern const void * RTTI_NiInterpController; +extern const void * RTTI_NiFloatInterpolator; +extern const void * RTTI_NiColorInterpolator; +extern const void * RTTI_NiSingleInterpController; +extern const void * RTTI_NiTransformInterpolator; +extern const void * RTTI_NiPosData; +extern const void * RTTI_NiPathInterpolator; +extern const void * RTTI_NiBlendTransformInterpolator; +extern const void * RTTI_NiBlendFloatInterpolator; +extern const void * RTTI_NiFloatExtraDataController; +extern const void * RTTI_NiVisController; +extern const void * RTTI_NiTransformController; +extern const void * RTTI_NiBlendAccumTransformInterpolator; +extern const void * RTTI_NiBlendInterpolator; +extern const void * RTTI_BSBlendTreadTransfInterpolator; +extern const void * RTTI_NiInterpolator; +extern const void * RTTI_NiKeyBasedInterpolator; +extern const void * RTTI_NiTransformData; +extern const void * RTTI_NiBlendBoolInterpolator; +extern const void * RTTI_NiBlendColorInterpolator; +extern const void * RTTI_NiBlendPoint3Interpolator; +extern const void * RTTI_NiBlendQuaternionInterpolator; +extern const void * RTTI_NiBoolData; +extern const void * RTTI_NiBoolInterpolator; +extern const void * RTTI_NiBoolTimelineInterpolator; +extern const void * RTTI_NiBSplineBasisData; +extern const void * RTTI_NiBSplineData; +extern const void * RTTI_NiBSplineColorInterpolator; +extern const void * RTTI_NiBSplineCompColorInterpolator; +extern const void * RTTI_NiBSplineCompFloatInterpolator; +extern const void * RTTI_NiBSplineCompPoint3Interpolator; +extern const void * RTTI_NiBSplineCompTransformInterpolator; +extern const void * RTTI_NiBSplineFloatInterpolator; +extern const void * RTTI_NiBSplinePoint3Interpolator; +extern const void * RTTI_NiBSplineTransformInterpolator; +extern const void * RTTI_NiColorExtraDataController; +extern const void * RTTI_NiFloatsExtraDataController; +extern const void * RTTI_NiFloatsExtraDataPoint3Controller; +extern const void * RTTI_NiKeyframeManager; +extern const void * RTTI_NiLightColorController; +extern const void * RTTI_NiLightDimmerController; +extern const void * RTTI_NiLightRadiusController; +extern const void * RTTI_NiLookAtController; +extern const void * RTTI_NiLookAtInterpolator; +extern const void * RTTI_NiMorphData; +extern const void * RTTI_NiPathController; +extern const void * RTTI_NiPoint3Interpolator; +extern const void * RTTI_NiQuaternionInterpolator; +extern const void * RTTI_NiFloatController; +extern const void * RTTI_NiRollController; +extern const void * RTTI_NiRotData; +extern const void * RTTI_NiSequence; +extern const void * RTTI_NiSequenceStreamHelper; +extern const void * RTTI_NiStringPalette; +extern const void * RTTI_NiTextKeyExtraData; +extern const void * RTTI_NiUVData; +extern const void * RTTI_BSAnimNotes; +extern const void * RTTI_BSAnimNote; +extern const void * RTTI_BSGrabIKNote; +extern const void * RTTI_BSLookIKNote; +extern const void * RTTI_BSRotAccumTransfInterpolator; +extern const void * RTTI_BSTreadTransfInterpolator; +extern const void * RTTI_BSFrustumFOVController; +extern const void * RTTI_NiExtraDataController; +extern const void * RTTI_NiBoolInterpController; +extern const void * RTTI_NiBSplineInterpolator; +extern const void * RTTI_NiPoint3InterpController; +extern const void * RTTI_NiFloatInterpController; +extern const void * RTTI_NiParticleSystem; +extern const void * RTTI_NiPSysMeshEmitter; +extern const void * RTTI_BSPSysInheritVelocityModifier; +extern const void * RTTI_NiPSysEmitterCtlr; +extern const void * RTTI_NiPSysGravityModifier; +extern const void * RTTI_BSPSysHavokUpdateModifier; +extern const void * RTTI_NiMeshParticleSystem; +extern const void * RTTI_NiPSysCylinderEmitter; +extern const void * RTTI_BSStripParticleSystem; +extern const void * RTTI_NiPSysEmitter; +extern const void * RTTI_NiPSysData; +extern const void * RTTI_NiPSysModifierCtlr; +extern const void * RTTI_NiPSysModifier; +extern const void * RTTI_NiPSysMeshUpdateModifier; +extern const void * RTTI_NiMeshPSysData; +extern const void * RTTI_NiPSysUpdateCtlr; +extern const void * RTTI_NiPSysAirFieldAirFrictionCtlr; +extern const void * RTTI_NiPSysAirFieldInheritVelocityCtlr; +extern const void * RTTI_NiPSysAirFieldModifier; +extern const void * RTTI_NiPSysAirFieldSpreadCtlr; +extern const void * RTTI_NiPSysAgeDeathModifier; +extern const void * RTTI_NiPSysBombModifier; +extern const void * RTTI_NiPSysBoundUpdateModifier; +extern const void * RTTI_NiPSysBoxEmitter; +extern const void * RTTI_NiPSysColliderManager; +extern const void * RTTI_NiPSysColorModifier; +extern const void * RTTI_NiPSysDragFieldModifier; +extern const void * RTTI_NiPSysDragModifier; +extern const void * RTTI_NiPSysEmitterCtlrData; +extern const void * RTTI_NiPSysEmitterDeclinationCtlr; +extern const void * RTTI_NiPSysEmitterDeclinationVarCtlr; +extern const void * RTTI_NiPSysEmitterInitialRadiusCtlr; +extern const void * RTTI_NiPSysEmitterLifeSpanCtlr; +extern const void * RTTI_NiPSysEmitterPlanarAngleCtlr; +extern const void * RTTI_NiPSysEmitterPlanarAngleVarCtlr; +extern const void * RTTI_NiPSysEmitterSpeedCtlr; +extern const void * RTTI_NiPSysFieldAttenuationCtlr; +extern const void * RTTI_NiPSysFieldMagnitudeCtlr; +extern const void * RTTI_NiPSysFieldMaxDistanceCtlr; +extern const void * RTTI_NiPSysGravityFieldModifier; +extern const void * RTTI_NiPSysGravityStrengthCtlr; +extern const void * RTTI_NiPSysGrowFadeModifier; +extern const void * RTTI_NiPSysInitialRotAngleCtlr; +extern const void * RTTI_NiPSysInitialRotAngleVarCtlr; +extern const void * RTTI_NiPSysInitialRotSpeedCtlr; +extern const void * RTTI_NiPSysInitialRotSpeedVarCtlr; +extern const void * RTTI_NiPSysModifierActiveCtlr; +extern const void * RTTI_NiPSysPlanarCollider; +extern const void * RTTI_NiPSysPositionModifier; +extern const void * RTTI_NiPSysRadialFieldModifier; +extern const void * RTTI_NiPSysResetOnLoopCtlr; +extern const void * RTTI_NiPSysRotationModifier; +extern const void * RTTI_NiPSysSpawnModifier; +extern const void * RTTI_NiPSysSphereEmitter; +extern const void * RTTI_NiPSysSphericalCollider; +extern const void * RTTI_NiPSysTurbulenceFieldModifier; +extern const void * RTTI_NiPSysVortexFieldModifier; +extern const void * RTTI_BSStripPSysData; +extern const void * RTTI_BSPSysRecycleBoundModifier; +extern const void * RTTI_NiPSysVolumeEmitter; +extern const void * RTTI_NiPSysModifierFloatCtlr; +extern const void * RTTI_NiPSysFieldModifier; +extern const void * RTTI_NiPSysModifierBoolCtlr; +extern const void * RTTI_NiPSysCollider; +extern const void * RTTI_BSStaticTriShapeDB__QueuedHandles; +extern const void * RTTI_BSStaticTriShapeDB__Builder; +extern const void * RTTI_BSStaticTriShapeDB__Manager; +extern const void * RTTI_BSStaticTriShapeDB__CsgStream; +extern const void * RTTI_ITextureDB; +extern const void * RTTI_BSTextureDB__QueuedHandles; +extern const void * RTTI_BSMaterialDB__QueuedHandles; +extern const void * RTTI_BSTaskThread; +extern const void * RTTI_IOManager; +extern const void * RTTI_IOManagerThread; +extern const void * RTTI_IOTask; +extern const void * RTTI_IOSystemTask; +extern const void * RTTI_QueuedFile; +extern const void * RTTI_QueuedNamedFile; +extern const void * RTTI_BSQueuedResourceCollectionBase; +extern const void * RTTI_BSMultiBound; +extern const void * RTTI_BSMultiBoundRoom; +extern const void * RTTI_BSXFlags; +extern const void * RTTI_BSValueNode; +extern const void * RTTI_BSTextureStreamer__TextureModifyBatch; +extern const void * RTTI_BSTextureStreamer__Manager; +extern const void * RTTI_BSTextureStreamer__StreamDetailI; +extern const void * RTTI_BSTextureStreamer__zlibStreamDetail; +extern const void * RTTI_BSTextureStreamer__TextureModifyBatchNoLimit; +extern const void * RTTI_BSWindModifier; +extern const void * RTTI_BSPortalGraph; +extern const void * RTTI_BSPortalGraphEntry; +extern const void * RTTI_BSOcclusionShape; +extern const void * RTTI_BSRangeNode; +extern const void * RTTI_BSBlastNode; +extern const void * RTTI_BSDebrisNode; +extern const void * RTTI_BSDamageStage; +extern const void * RTTI_PArrayPoint; +extern const void * RTTI_BSPSysArrayEmitter; +extern const void * RTTI_BSMultiBoundShape; +extern const void * RTTI_BSInstanceTriShape; +extern const void * RTTI_BSMultiStreamInstanceTriShape; +extern const void * RTTI_BSIStream; +extern const void * RTTI_BSOStream; +extern const void * RTTI_BSFurnitureMarkerNode; +extern const void * RTTI_BSOcclusionBox; +extern const void * RTTI_BSOcclusionPlane; +extern const void * RTTI_BSPortalSharedNode; +extern const void * RTTI_BSBehaviorGraphExtraData; +extern const void * RTTI_BSCullingProcess; +extern const void * RTTI_BSNiNode; +extern const void * RTTI_BSSubIndexTriShape; +extern const void * RTTI_BSSegmentDataStorage; +extern const void * RTTI_BSParticleSystemManager; +extern const void * RTTI_BSBound; +extern const void * RTTI_BSMergeInstancedTriShape; +extern const void * RTTI_BSMultiBoundNode; +extern const void * RTTI_BSBoneMap; +extern const void * RTTI_BSSceneGraph; +extern const void * RTTI_BSReference; +extern const void * RTTI_BSNodeReferences; +extern const void * RTTI_BSShapeConstructor; +extern const void * RTTI_BSBoxConstructor; +extern const void * RTTI_BSOrientedBoxConstructor; +extern const void * RTTI_BSSphereConstructor; +extern const void * RTTI_BSCylinderConstructor; +extern const void * RTTI_BSCapsuleConstructor; +extern const void * RTTI_BSArrowConstructor; +extern const void * RTTI_BSCircleConstructor; +extern const void * RTTI_BSRingConstructor; +extern const void * RTTI_BSFlatQuadConstructor; +extern const void * RTTI_BSDiskConstructor; +extern const void * RTTI_BSPSysMultiTargetEmitterCtlr; +extern const void * RTTI_BSTextureArray__Texture; +extern const void * RTTI_BSTextureArray__StaticTexture; +extern const void * RTTI_BSTextureArray__StaticTextureIndexed; +extern const void * RTTI_BSTextureArray__MasterTexture; +extern const void * RTTI_BSProceduralLightningTasklet; +extern const void * RTTI_BSProceduralLightningController; +extern const void * RTTI_BSBoneLODExtraData; +extern const void * RTTI_BSMasterParticleSystem; +extern const void * RTTI_BSParentVelocityModifier; +extern const void * RTTI_BSInvMarker; +extern const void * RTTI_BSWArray; +extern const void * RTTI_BSMultiBoundCapsule; +extern const void * RTTI_BSPSysStripUpdateModifier; +extern const void * RTTI_BSPSysSubTexModifier; +extern const void * RTTI_BSPSysScaleModifier; +extern const void * RTTI_BSLagBoneController; +extern const void * RTTI_BSNonUniformScaleExtraData; +extern const void * RTTI_BSCombinedTriShape; +extern const void * RTTI_BSDistantObjectInstanceDataBlock; +extern const void * RTTI_BSTableCurve; +extern const void * RTTI__com_error; +extern const void * RTTI_BSD3DResourceCreator; +extern const void * RTTI_BSConnectPoint__Children; +extern const void * RTTI_BSConnectPoint__Parents; +extern const void * RTTI_hknpBSWorld; +extern const void * RTTI_bhkWorld; +extern const void * RTTI_bhkCharProxyManager; +extern const void * RTTI_bhkCharRigidBodyManager; +extern const void * RTTI_hclBSWorld; +extern const void * RTTI_hknpBackfaceCollisionModifier; +extern const void * RTTI_hknpBSShapeCodec; +extern const void * RTTI_bhkNPCollisionObject; +extern const void * RTTI_bhkNPCollisionObjectBase; +extern const void * RTTI_bhkNPCollisionObjectUnlinked; +extern const void * RTTI_bhkNPCollisionProxyObject; +extern const void * RTTI_bhkNPCollisionObjectUnlinked__LinkExtraData; +extern const void * RTTI_bhkPhysicsSystem; +extern const void * RTTI_bhkRagdollSystem; +extern const void * RTTI_bhkCollisionFilter; +extern const void * RTTI_hknpCompressedMeshShapeCinfo; +extern const void * RTTI_hknpDefaultCompressedMeshShapeCinfo; +extern const void * RTTI_hknpBSMaterial; +extern const void * RTTI_hknpCharacterState; +extern const void * RTTI_bhkCharacterState; +extern const void * RTTI_bhkCharacterController; +extern const void * RTTI_bhkICharOrientationController; +extern const void * RTTI_bhkCharacterControllerCinfo; +extern const void * RTTI_hknpBSMoveLimitModifier; +extern const void * RTTI_bhkCharacterStateClimbing; +extern const void * RTTI_bhkCharacterStateFlying; +extern const void * RTTI_BSClothExtraData; +extern const void * RTTI_hclTransformSet; +extern const void * RTTI_hclInstantiationUtil; +extern const void * RTTI_BSClothUtils__BSInstantiationUtil; +extern const void * RTTI_BSClothUtils__BSTransformSet; +extern const void * RTTI_BSClothUtils__BSTriShapeBuffer; +extern const void * RTTI_hknpClosestUniqueBodyIdHitCollector; +extern const void * RTTI_bhkWorldM; +extern const void * RTTI_hkaNpRagdollRaycastInterface; +extern const void * RTTI_bhkRagdollPenetrationUtil; +extern const void * RTTI_hkThreadPool; +extern const void * RTTI_bhkThreadPool; +extern const void * RTTI_hknpFixedClosestHitCollector; +extern const void * RTTI_hknpBSMouseSpringAction; +extern const void * RTTI_hknpCharacterProxyListener; +extern const void * RTTI_bhkCharProxyController; +extern const void * RTTI_bhkCharacterProxy; +extern const void * RTTI_bhkCharProxyControllerCinfo; +extern const void * RTTI_hknpCharacterRigidBodyListener; +extern const void * RTTI_bhkCharRigidBodyController; +extern const void * RTTI_bhkCharacterRigidBodyCinfo; +extern const void * RTTI_bhkCharacterRigidBody; +extern const void * RTTI_bhkCharRigidBodyControllerCinfo; +extern const void * RTTI_hknpBSBroadPhaseConfig; +extern const void * RTTI_hknpSafeEaseConstraintsAction; +extern const void * RTTI_BSPistonController; +extern const void * RTTI_hclWorld; +extern const void * RTTI_hclWorldListener; +extern const void * RTTI_bhkBipedOrientationController; +extern const void * RTTI_hkThreadMemory; +extern const void * RTTI_bhkMemorySystem; +extern const void * RTTI_hclBSClothParameterizedWindAction; +extern const void * RTTI_hkPackfileWriter__AddObjectListener; +extern const void * RTTI_hknpWorldSnapshot; +extern const void * RTTI_hclSimClothInstance; +extern const void * RTTI_hclCollidable; +extern const void * RTTI_hclClothInstance; +extern const void * RTTI_hkaSplineCompressedAnimation; +extern const void * RTTI_hkpConstraintMotor; +extern const void * RTTI_hkpLimitedForceConstraintMotor; +extern const void * RTTI_hkpPositionConstraintMotor; +extern const void * RTTI_hkbCustomIdSelector; +extern const void * RTTI_hkbStateChooserWrapper; +extern const void * RTTI_hkbAttachmentSetup; +extern const void * RTTI_hkbBehaviorGraphInternalState; +extern const void * RTTI_hkbBlendingTransitionEffectInternalState; +extern const void * RTTI_hkbBoneIndexArray; +extern const void * RTTI_hkbCharacterControllerModifierInternalState; +extern const void * RTTI_hkbCombineTransformsModifierInternalState; +extern const void * RTTI_hkbComputeDirectionModifierInternalState; +extern const void * RTTI_hkbComputeRotationFromAxisAngleModifierInternalState; +extern const void * RTTI_hkbComputeRotationToTargetModifierInternalState; +extern const void * RTTI_hkbDampingModifierInternalState; +extern const void * RTTI_hkbDelayedModifierInternalState; +extern const void * RTTI_hkbDetectCloseToGroundModifierInternalState; +extern const void * RTTI_hkbDockingGeneratorInternalState; +extern const void * RTTI_hkbExpressionDataArray; +extern const void * RTTI_hkbEvaluateExpressionModifierInternalState; +extern const void * RTTI_hkbEventDrivenModifierInternalState; +extern const void * RTTI_hkpSpringDamperConstraintMotor; +extern const void * RTTI_hkbEventRangeDataArray; +extern const void * RTTI_hkbEventsFromRangeModifierInternalState; +extern const void * RTTI_hkbExtrapolatingTransitionEffect; +extern const void * RTTI_hkbExtrapolatingTransitionEffectInternalState; +extern const void * RTTI_hkbGetUpModifierInternalState; +extern const void * RTTI_hkbGetWorldFromModelModifierInternalState; +extern const void * RTTI_hkbJigglerGroup; +extern const void * RTTI_hkbJigglerModifierInternalState; +extern const void * RTTI_hkbLayerGeneratorInternalState; +extern const void * RTTI_hkbLookAtModifierInternalState; +extern const void * RTTI_hkbManualSelectorGeneratorInternalState; +extern const void * RTTI_hkbManualSelectorTransitionEffectInternalState; +extern const void * RTTI_hkbModifierWrapper; +extern const void * RTTI_hkbMoveCharacterModifierInternalState; +extern const void * RTTI_hkStringObject; +extern const void * RTTI_hkbRotateCharacterModifierInternalState; +extern const void * RTTI_hkbScriptCondition; +extern const void * RTTI_hkbStringCondition; +extern const void * RTTI_hkbTimerModifierInternalState; +extern const void * RTTI_hkbTransformVectorModifierInternalState; +extern const void * RTTI_hkpVelocityConstraintMotor; +extern const void * RTTI_hkcdPlanarEntity; +extern const void * RTTI_hkcdPlanarSolid__NodeStorage; +extern const void * RTTI_hkbSimulationControlCommand; +extern const void * RTTI_hkbCharacterControlCommand; +extern const void * RTTI_hkbRaiseEventCommand; +extern const void * RTTI_hkbSetSelectedCharacterCommand; +extern const void * RTTI_hkbSetWordVariableCommand; +extern const void * RTTI_hkbSetLocalTimeOfClipGeneratorCommand; +extern const void * RTTI_hkbSetBehaviorCommand; +extern const void * RTTI_hkbSetNodePropertyCommand; +extern const void * RTTI_hkbRebuildScriptsCommand; +extern const void * RTTI_hkbCameraVariablesChangedCommand; +extern const void * RTTI_hkbAiControlPathToCommand; +extern const void * RTTI_hkbAiControlCancelPathCommand; +extern const void * RTTI_hkbCharacterAddedInfo; +extern const void * RTTI_hkbCharacterInfo; +extern const void * RTTI_hkbCharacterSteppedInfo; +extern const void * RTTI_hkbCharacterSkinInfo; +extern const void * RTTI_hkbBehaviorInfo; +extern const void * RTTI_hkbBehaviorEventsInfo; +extern const void * RTTI_hkbEventRaisedInfo; +extern const void * RTTI_hkbSimulationStateInfo; +extern const void * RTTI_hkbLinkedSymbolInfo; +extern const void * RTTI_hkbAuxiliaryNodeInfo; +extern const void * RTTI_hkbBehaviorGraphInternalStateInfo; +extern const void * RTTI_hkbCharacterSelectedInfo; +extern const void * RTTI_hkbCameraShakeEventPayload; +extern const void * RTTI_hkbHoldFromBlendingTransitionEffect; +extern const void * RTTI_hkbNullPhysicsInterface; +extern const void * RTTI_hkbParticleSystemEventPayload; +extern const void * RTTI_hkbGeneratorOutputListener; +extern const void * RTTI_hkbPoseStoringGeneratorOutputListener; +extern const void * RTTI_hkbPoseStoringGeneratorOutputListener__StoredPose; +extern const void * RTTI_hkbTestIdSelector; +extern const void * RTTI_hkMeshTexture; +extern const void * RTTI_hkMemoryMeshTexture; +extern const void * RTTI_BSIStateManagerModifier__BSIStateManagerStateListener; +extern const void * RTTI_hkpCallbackConstraintMotor; +extern const void * RTTI_hkpCogWheelConstraintData; +extern const void * RTTI_hkpLinearClearanceConstraintData; +extern const void * RTTI_hkpParametricCurve; +extern const void * RTTI_hkpLinearParametricCurve; +extern const void * RTTI_hkpRackAndPinionConstraintData; +extern const void * RTTI_hkpRotationalConstraintData; +extern const void * RTTI_hkpWheelFrictionConstraintData; +extern const void * RTTI_hclConstraintSet; +extern const void * RTTI_hclAntiPinchConstraintSet; +extern const void * RTTI_hclBendLinkConstraintSet; +extern const void * RTTI_hclBendLinkConstraintSetMx; +extern const void * RTTI_hclBendStiffnessConstraintSet; +extern const void * RTTI_hclBendStiffnessConstraintSetMx; +extern const void * RTTI_hclOperator; +extern const void * RTTI_hclBlendSomeVerticesOperator; +extern const void * RTTI_hclBonePlanesConstraintSet; +extern const void * RTTI_hclBoneSpaceMeshMeshDeformOperator; +extern const void * RTTI_hclBoneSpaceMeshMeshDeformPNOperator; +extern const void * RTTI_hclBoneSpaceMeshMeshDeformPNTBOperator; +extern const void * RTTI_hclBoneSpaceMeshMeshDeformPNTOperator; +extern const void * RTTI_hclBoneSpaceMeshMeshDeformPOperator; +extern const void * RTTI_hclBoneSpaceSkinOperator; +extern const void * RTTI_hclBoneSpaceSkinPNOperator; +extern const void * RTTI_hclBoneSpaceSkinPNTBOperator; +extern const void * RTTI_hclBoneSpaceSkinPNTOperator; +extern const void * RTTI_hclBoneSpaceSkinPOperator; +extern const void * RTTI_hclBufferDefinition; +extern const void * RTTI_hclShape; +extern const void * RTTI_hclCapsuleShape; +extern const void * RTTI_hclClothData; +extern const void * RTTI_hclClothState; +extern const void * RTTI_hclCompressibleLinkConstraintSet; +extern const void * RTTI_hclCompressibleLinkConstraintSetMx; +extern const void * RTTI_hclConvexGeometryShape; +extern const void * RTTI_hclConvexHeightFieldShape; +extern const void * RTTI_hclConvexPlanesShape; +extern const void * RTTI_hclCopyVerticesOperator; +extern const void * RTTI_hclGatherAllVerticesOperator; +extern const void * RTTI_hclGatherSomeVerticesOperator; +extern const void * RTTI_hclInputConvertOperator; +extern const void * RTTI_hclLocalRangeConstraintSet; +extern const void * RTTI_hclMeshBoneDeformOperator; +extern const void * RTTI_hclMeshMeshDeformOperator; +extern const void * RTTI_hclMoveParticlesOperator; +extern const void * RTTI_hclObjectSpaceMeshMeshDeformOperator; +extern const void * RTTI_hclObjectSpaceMeshMeshDeformPNOperator; +extern const void * RTTI_hclObjectSpaceMeshMeshDeformPNTBOperator; +extern const void * RTTI_hclObjectSpaceMeshMeshDeformPNTOperator; +extern const void * RTTI_hclObjectSpaceMeshMeshDeformPOperator; +extern const void * RTTI_hclObjectSpaceSkinOperator; +extern const void * RTTI_hclObjectSpaceSkinPNOperator; +extern const void * RTTI_hkpVehicleFrictionDescription; +extern const void * RTTI_hclObjectSpaceSkinPNTBOperator; +extern const void * RTTI_hclObjectSpaceSkinPNTOperator; +extern const void * RTTI_hclObjectSpaceSkinPOperator; +extern const void * RTTI_hclOutputConvertOperator; +extern const void * RTTI_hclPlaneShape; +extern const void * RTTI_hclScratchBufferDefinition; +extern const void * RTTI_hclShadowBufferDefinition; +extern const void * RTTI_hclSimClothData; +extern const void * RTTI_hclSimClothPose; +extern const void * RTTI_hclSimpleMeshBoneDeformOperator; +extern const void * RTTI_hclAction; +extern const void * RTTI_hclSimpleWindAction; +extern const void * RTTI_hclSimulateOperator; +extern const void * RTTI_hclSkinOperator; +extern const void * RTTI_hclSphereShape; +extern const void * RTTI_hclStandardLinkConstraintSet; +extern const void * RTTI_hclStandardLinkConstraintSetMx; +extern const void * RTTI_hclStaticShadowBufferDefinition; +extern const void * RTTI_hclStretchLinkConstraintSet; +extern const void * RTTI_hclStretchLinkConstraintSetMx; +extern const void * RTTI_hclTaperedCapsuleShape; +extern const void * RTTI_hknpBodyReference; +extern const void * RTTI_hclTransformSetDefinition; +extern const void * RTTI_hclTransitionConstraintSet; +extern const void * RTTI_hclUpdateAllVertexFramesOperator; +extern const void * RTTI_hknpCircularSurfaceVelocity; +extern const void * RTTI_hclUpdateSomeVertexFramesOperator; +extern const void * RTTI_hclVolumeConstraint; +extern const void * RTTI_hclVolumeConstraintMx; +extern const void * RTTI_hknpExternMeshShapeGeometry; +extern const void * RTTI_hknpDefaultExternMeshShapeGeometry; +extern const void * RTTI_hknpDisableCollisionFilter; +extern const void * RTTI_hknpMaterialPalette; +extern const void * RTTI_hknpRefWorldCinfo; +extern const void * RTTI_hknpTyremarksWheel; +extern const void * RTTI_hknpTyremarksInfo; +extern const void * RTTI_hknpVehicleData; +extern const void * RTTI_hknpVehicleAerodynamics; +extern const void * RTTI_hknpVehicleDefaultAerodynamics; +extern const void * RTTI_hknpVehicleDriverInputStatus; +extern const void * RTTI_hknpVehicleDriverInput; +extern const void * RTTI_hknpVehicleDriverInputAnalogStatus; +extern const void * RTTI_hknpVehicleDefaultAnalogDriverInput; +extern const void * RTTI_hknpVehicleBrake; +extern const void * RTTI_hknpVehicleDefaultBrake; +extern const void * RTTI_hknpVehicleEngine; +extern const void * RTTI_hknpVehicleDefaultEngine; +extern const void * RTTI_hknpVehicleSteering; +extern const void * RTTI_hknpVehicleDefaultSteering; +extern const void * RTTI_hknpVehicleSuspension; +extern const void * RTTI_hknpVehicleDefaultSuspension; +extern const void * RTTI_hknpVehicleTransmission; +extern const void * RTTI_hknpVehicleDefaultTransmission; +extern const void * RTTI_hknpVehicleVelocityDamper; +extern const void * RTTI_hknpVehicleDefaultVelocityDamper; +extern const void * RTTI_hknpVehicleInstance; +extern const void * RTTI_hknpVehicleWheelCollide; +extern const void * RTTI_hknpVehicleLinearCastWheelCollide; +extern const void * RTTI_hknpVehicleRayCastWheelCollide; +extern const void * RTTI_hknpStaticCompoundShapeInternals; +extern const void * RTTI_hkaAnimationPreviewColorContainer; +extern const void * RTTI_hkaAnimatedReferenceFrame; +extern const void * RTTI_hkaDefaultAnimatedReferenceFrame; +extern const void * RTTI_hkaFootstepAnalysisInfoContainer; +extern const void * RTTI_hkaInterleavedUncompressedAnimation; +extern const void * RTTI_hkaLosslessCompressedAnimation; +extern const void * RTTI_hkaParameterizedAnimationReferenceFrame; +extern const void * RTTI_hkaQuantizedAnimation; +extern const void * RTTI_hknpCompressedMeshShape; +extern const void * RTTI_hknpStaticCompoundShape; +extern const void * RTTI_hknpMaskedShape; +extern const void * RTTI_hknpMaskedShape__MaskWrapper; +extern const void * RTTI_bhkCharacterStateFloating; +extern const void * RTTI_bhkCharacterStateInAir; +extern const void * RTTI_bhkCharacterStateJumping; +extern const void * RTTI_bhkCharacterStateOnGround; +extern const void * RTTI_bhkCharacterStateSwimming; +extern const void * RTTI_bhkQuadOrientationController; +extern const void * RTTI_hclBuffer; +extern const void * RTTI_hkaNpRagdollPenetrationDetector; +extern const void * RTTI_bhkCharacterPointCollector; +extern const void * RTTI_hknpBSCharacterProxy; +extern const void * RTTI_hknpBSCharacterRigidBody; +extern const void * RTTI_hkSolverAllocator; +extern const void * RTTI_bhkThreadMemorySource; +extern const void * RTTI_hknpDummyShape; +extern const void * RTTI_hknpExternMeshShape; +extern const void * RTTI_hclPointContactPlanesShape; +extern const void * RTTI_hkbAttachmentModifier; +extern const void * RTTI_hkbAttributeModifier; +extern const void * RTTI_hkbBlendingTransitionEffect; +extern const void * RTTI_hkbCharacterControllerModifier; +extern const void * RTTI_hkbCombineTransformsModifier; +extern const void * RTTI_hkbComputeDirectionModifier; +extern const void * RTTI_hkbComputeRotationFromAxisAngleModifier; +extern const void * RTTI_hkbComputeRotationToTargetModifier; +extern const void * RTTI_hkbDampingModifier; +extern const void * RTTI_hkbDelayedModifier; +extern const void * RTTI_hkbDetectCloseToGroundModifier; +extern const void * RTTI_hkbDockingGenerator; +extern const void * RTTI_hkbEvaluateExpressionModifier; +extern const void * RTTI_hkbEvaluateHandleModifier; +extern const void * RTTI_hkbEventDrivenModifier; +extern const void * RTTI_hkbEventsFromRangeModifier; +extern const void * RTTI_hkbExtractRagdollPoseModifier; +extern const void * RTTI_hkbFootIkControlsModifier; +extern const void * RTTI_hkbFootIkModifier; +extern const void * RTTI_hkbGetHandleOnBoneModifier; +extern const void * RTTI_hkbGetUpModifier; +extern const void * RTTI_hkbGetWorldFromModelModifier; +extern const void * RTTI_hkbHandIkControlsModifier; +extern const void * RTTI_hkbHandIkModifier; +extern const void * RTTI_hkbJigglerModifier; +extern const void * RTTI_hkbKeyframeBonesModifier; +extern const void * RTTI_hkbLayer; +extern const void * RTTI_hkbLayerGenerator; +extern const void * RTTI_hkbLookAtModifier; +extern const void * RTTI_hkbManualSelectorGenerator; +extern const void * RTTI_hkbManualSelectorTransitionEffect; +extern const void * RTTI_hkbMirrorModifier; +extern const void * RTTI_hkbModifierGenerator; +extern const void * RTTI_hkbModifierList; +extern const void * RTTI_hkbMoveBoneAttachmentModifier; +extern const void * RTTI_hkbMoveCharacterModifier; +extern const void * RTTI_hkbPoweredRagdollControlsModifier; +extern const void * RTTI_hkbRigidBodyRagdollControlsModifier; +extern const void * RTTI_hkbRotateCharacterModifier; +extern const void * RTTI_hkbScriptGenerator; +extern const void * RTTI_hkbSenseHandleModifier; +extern const void * RTTI_hkbSetWorldFromModelModifier; +extern const void * RTTI_hkbTimerModifier; +extern const void * RTTI_hkbTransformVectorModifier; +extern const void * RTTI_hkbTwistModifier; +extern const void * RTTI_hkUuidPseudoRandomGenerator; +extern const void * RTTI_hkResourceContainer; +extern const void * RTTI_hkResourceMap; +extern const void * RTTI_hkResourceBase; +extern const void * RTTI_hkResourceHandle; +extern const void * RTTI_hkMemoryResourceHandle; +extern const void * RTTI_hkMemoryResourceContainer; +extern const void * RTTI_hkContainerResourceMap; +extern const void * RTTI_hkcdPlanarSolid__ArrayMgr; +extern const void * RTTI_hkcdConvexCellsCollection; +extern const void * RTTI_hkcdPlanarCsgOperand; +extern const void * RTTI_hkcdPlanarGeometry; +extern const void * RTTI_hkcdPlanarGeometry__VertexStorage; +extern const void * RTTI_hkcdPlanarGeometryPlanesCollection; +extern const void * RTTI_hkcdPlanarGeometryPolygonCollection; +extern const void * RTTI_hkcdPlanarSolid; +extern const void * RTTI_hkcdShape; +extern const void * RTTI_hkcdDynamicAabbTree; +extern const void * RTTI_hkcdStaticAabbTree; +extern const void * RTTI_hkbAlignBoneModifier; +extern const void * RTTI_hkbAnimatedSkeletonGenerator; +extern const void * RTTI_hkRemoteObjectClientSideListener; +extern const void * RTTI_hkbBehaviorClient; +extern const void * RTTI_hkbClientCharacterState; +extern const void * RTTI_hkbComputeWorldFromModelModifier; +extern const void * RTTI_hkbCustomTestGeneratorHiddenTypes; +extern const void * RTTI_hkbCustomTestGeneratorSimpleTypes; +extern const void * RTTI_hkbCustomTestGeneratorComplexTypes; +extern const void * RTTI_hkbCustomTestGeneratorNestedTypesBase; +extern const void * RTTI_hkbCustomTestGeneratorNestedTypes; +extern const void * RTTI_hkbCustomTestGeneratorBoneTypes; +extern const void * RTTI_hkbCustomTestGeneratorAnnotatedTypes; +extern const void * RTTI_hkbCustomTestGenerator; +extern const void * RTTI_hkbGravityModifier; +extern const void * RTTI_hkbPinBonesGenerator; +extern const void * RTTI_hkbRadialSelectorGenerator; +extern const void * RTTI_hkbStateDependentModifier; +extern const void * RTTI_hkSkinnedMeshShape; +extern const void * RTTI_hkStorageSkinnedMeshShape; +extern const void * RTTI_hkMeshMaterial; +extern const void * RTTI_hkMemoryMeshMaterial; +extern const void * RTTI_BSAlignBoneModifier; +extern const void * RTTI_hkMeshShape; +extern const void * RTTI_hkMemoryMeshShape; +extern const void * RTTI_BSAssignVariablesModifier; +extern const void * RTTI_hkMemoryMeshTexture__Sampler; +extern const void * RTTI_hkMeshVertexBuffer; +extern const void * RTTI_hkMemoryMeshVertexBuffer; +extern const void * RTTI_BSBoneSwitchGenerator; +extern const void * RTTI_hkMultipleVertexBuffer; +extern const void * RTTI_BSBoneSwitchGeneratorBoneData; +extern const void * RTTI_hkSkinBinding; +extern const void * RTTI_BSComputeAddBoneAnimModifier; +extern const void * RTTI_hkSkinnedRefMeshShape; +extern const void * RTTI_BSCyclicBlendTransitionGenerator; +extern const void * RTTI_BSDecomposeVectorModifier; +extern const void * RTTI_BSDirectAtCapturePoseModifier; +extern const void * RTTI_BSDirectAtModifier; +extern const void * RTTI_BSDistTriggerModifier; +extern const void * RTTI_BSEventEveryNEventsModifier; +extern const void * RTTI_BSEventOnDeactivateModifier; +extern const void * RTTI_BSEventOnFalseToTrueModifier; +extern const void * RTTI_BSGetTimeStepModifier; +extern const void * RTTI_BSIStateManagerModifier; +extern const void * RTTI_BSInterpValueModifier; +extern const void * RTTI_BSIsActiveModifier; +extern const void * RTTI_BSLimbCycleModifier; +extern const void * RTTI_BSLimbIKModifier; +extern const void * RTTI_BSLookAtCapturePoseModifier; +extern const void * RTTI_BSLookAtModifier; +extern const void * RTTI_BSModifyOnceModifier; +extern const void * RTTI_BSOffsetAnimationGenerator; +extern const void * RTTI_BSPassByTargetTriggerModifier; +extern const void * RTTI_BSRagdollContactListenerModifier; +extern const void * RTTI_BSRandomAlarmModifier; +extern const void * RTTI_BSRetargetBodyBlend; +extern const void * RTTI_BSReverseSpineTwistModifier; +extern const void * RTTI_BSRootTwistModifier; +extern const void * RTTI_BSSpeedSamplerModifier; +extern const void * RTTI_BSTimerModifier; +extern const void * RTTI_BSTweenerModifier; +extern const void * RTTI_DynamicAnimationTaggingGenerator; +extern const void * RTTI_hclAntiPinchConstraintInstanceData; +extern const void * RTTI_hclClothContainer; +extern const void * RTTI_hknpBallGun; +extern const void * RTTI_hknpFirstPersonGun; +extern const void * RTTI_hknpProjectileGun; +extern const void * RTTI_hknpGunProjectile; +extern const void * RTTI_hclTransitionConstraintInstanceData; +extern const void * RTTI_hclVolumeConstraintInstanceData; +extern const void * RTTI_hclVolumeConstraintMxInstanceData; +extern const void * RTTI_hknpDestructionShapeProperties; +extern const void * RTTI_hknpLodShape; +extern const void * RTTI_hknpMalleableConstraintData; +extern const void * RTTI_hknpPhysicsSceneData; +extern const void * RTTI_hkaParameterizedReferenceFrame; +extern const void * RTTI_hkaAngularReferenceFrame; +extern const void * RTTI_hkaDirectionalReferenceFrame; +extern const void * RTTI_hkaFootstepAnalysisInfo; +extern const void * RTTI_hkaPredictiveCompressedAnimation; +extern const void * RTTI_hclScratchBuffer; +extern const void * RTTI_hclShadowBuffer; +extern const void * RTTI_hclStaticShadowBuffer; +extern const void * RTTI_hkcdConvexCellsTree3D; +extern const void * RTTI_hkcdVertexGeometry; +extern const void * RTTI_hkcdVertexGeometry__VPolygonCollectionBase; +extern const void * RTTI_hkcdVertexGeometry__VPolygonCollection; +extern const void * RTTI_hkMeshTexture__Sampler; +extern const void * RTTI_hkcdConvexCellsTree2D; +extern const void * RTTI_hkImage; +extern const void * RTTI_hkDefaultImage; +extern const void * RTTI_UI; +extern const void * RTTI_Scaleform__Render__MappedTextureBase; +extern const void * RTTI_Scaleform__Render__RenderBuffer__RenderTargetData; +extern const void * RTTI_Scaleform__Render__D3D1x__MappedTexture; +extern const void * RTTI_Scaleform__Render__D3D1x__RenderTargetData; +extern const void * RTTI_BSUIScaleformData; +extern const void * RTTI_UIMessage; +extern const void * RTTI_BSGFxFunctionHandler; +extern const void * RTTI_BSGFxFunctionBase; +extern const void * RTTI_BSGFxShaderFXTarget; +extern const void * RTTI_BSGFxDisplayObject; +extern const void * RTTI_IMenu; +extern const void * RTTI_Scaleform__SysAllocPaged; +extern const void * RTTI_Scaleform__SysMemMapper; +extern const void * RTTI_Scaleform__Log; +extern const void * RTTI_Scaleform__GFx__Translator; +extern const void * RTTI_Scaleform__GFx__ZlibSupportBase; +extern const void * RTTI_Scaleform__GFx__State; +extern const void * RTTI_Scaleform__GFx__ZlibSupport; +extern const void * RTTI_BSScaleformTranslator; +extern const void * RTTI_BSScaleformManager; +extern const void * RTTI_BSStreamParserData; +extern const void * RTTI_BSResourceStreamParser; +extern const void * RTTI_MenuCursor; +extern const void * RTTI_Scaleform__File; +extern const void * RTTI_Scaleform__MemoryFile; +extern const void * RTTI_Scaleform__GFx__FileOpenerBase; +extern const void * RTTI_Scaleform__GFx__FileOpener; +extern const void * RTTI_BSScaleformFileOpener; +extern const void * RTTI_ScaleformFile__MemoryFile; +extern const void * RTTI_IDebugText; +extern const void * RTTI_DebugText; +extern const void * RTTI_Scaleform__RefCountVImpl; +extern const void * RTTI_Scaleform__Render__ImageBase; +extern const void * RTTI_Scaleform__Render__Image; +extern const void * RTTI_Scaleform__Render__TextureImage; +extern const void * RTTI_BSScaleformImageLoader; +extern const void * RTTI_SFRenderBufferManager; +extern const void * RTTI_Movie; +extern const void * RTTI_MoviePlayer; +extern const void * RTTI_ITrianglePathSplitter; +extern const void * RTTI_IPathSmootherRayCast; +extern const void * RTTI_FindTriangleForLocationTraversableFilter; +extern const void * RTTI_FindTriangleForLocationWaterFilter; +extern const void * RTTI_BSPathing; +extern const void * RTTI_BSPathing__EdgeFoundVisitor; +extern const void * RTTI_TrianglePathWaterAndLedgeSplitter; +extern const void * RTTI_BSNavmesh; +extern const void * RTTI_IPathBuilderTracker; +extern const void * RTTI_BSPathingRequest; +extern const void * RTTI_NullPathBuilderTracker; +extern const void * RTTI_BSPathSmootherPOVSearch; +extern const void * RTTI_DynamicNavmesh__BuildThread; +extern const void * RTTI_MovementAgent; +extern const void * RTTI_NullMovementState; +extern const void * RTTI_IMovementHandlerAgent; +extern const void * RTTI_MovementHandlerArbiter; +extern const void * RTTI_MovementHandlerAgent; +extern const void * RTTI_IMovementControllerRegisterInterface; +extern const void * RTTI_IMovementControllerDataTracker; +extern const void * RTTI_MovementControllerAI; +extern const void * RTTI_BSPathingStreamSimpleBufferRead; +extern const void * RTTI_MovementControllerNullDataTracker; +extern const void * RTTI_IMovementPlayIdleResult; +extern const void * RTTI_ISelectIdleFilter; +extern const void * RTTI_MovementSelectIdleUtils__SelectClosestIdleToPath; +extern const void * RTTI_MovementSelectIdleUtils__SelectClosestIdleToMovementSelectionData; +extern const void * RTTI_MovementMessageFreezeDirection; +extern const void * RTTI_IMovementQueryTweener; +extern const void * RTTI_IMovementSetTweener; +extern const void * RTTI_MovementTweenerArbiter; +extern const void * RTTI_MovementTweenerAgent; +extern const void * RTTI_IMovementSetCorrectionData; +extern const void * RTTI_IMovementQueryTweenerState; +extern const void * RTTI_MovementTweenerAgentFixedDelta; +extern const void * RTTI_BSNavmeshObstacleCoverData; +extern const void * RTTI_BSNavmeshObstacleData; +extern const void * RTTI_BSNavmeshInfoSearch; +extern const void * RTTI_BSPrecomputedNavmeshInfoSearch; +extern const void * RTTI_IMovementPathManagerAgent; +extern const void * RTTI_MovementPathManagerAgent; +extern const void * RTTI_IMovementSetGoal; +extern const void * RTTI_IMovementQueryPathingState; +extern const void * RTTI_IMovementPathManagerDataTracker; +extern const void * RTTI_MovementPathManagerArbiter; +extern const void * RTTI_MovementMessageNewPath; +extern const void * RTTI_MovementMessageUpdateRequestImmediate; +extern const void * RTTI_MovementPathManagerNullTracker; +extern const void * RTTI_IMovementSetState; +extern const void * RTTI_MovementAgentActorState; +extern const void * RTTI_IPathFollowerAction; +extern const void * RTTI_IMovementPlannerAgent; +extern const void * RTTI_IMovementQueryPathFollowing; +extern const void * RTTI_IMovementQueryPathFollowing__INodeVisitor; +extern const void * RTTI_IMovementSetPathFollowing; +extern const void * RTTI_ICheckEndReachedFunctorMapper; +extern const void * RTTI_CheckParameterReachedFunctor; +extern const void * RTTI_CheckGoalReachedFunctor; +extern const void * RTTI_CheckStoppedMovingFunctor; +extern const void * RTTI_MovementAgentPathFollowerStandard; +extern const void * RTTI_BSNavmeshInfoMap; +extern const void * RTTI_BSNavmeshInfoMap__IRecursiveVisitor; +extern const void * RTTI_BSPrecomputedNavmeshInfoPathMap__INavmeshInfoVisitor; +extern const void * RTTI_MovementArbitrationAverageFloat; +extern const void * RTTI_MovementArbitrationMaxWeightPoint; +extern const void * RTTI_MovementArbitrationMaxWeightParameters; +extern const void * RTTI_NullMovementSelectIdleResult; +extern const void * RTTI_INavmeshSearchFilterSet; +extern const void * RTTI_BSNavmeshSearchFilters; +extern const void * RTTI_BSNavmeshSearch; +extern const void * RTTI_BSPathingLOSGridCell; +extern const void * RTTI_BSPathingLOSGrid; +extern const void * RTTI_BSPathingLOSGridMap; +extern const void * RTTI_BSPathBuilder; +extern const void * RTTI_PathSmootherRayCastUsePreferredTris; +extern const void * RTTI_PathSmootherRayCastUseTrianglePath; +extern const void * RTTI_IMovementSetStaticAvoider; +extern const void * RTTI_IMovementQueryStaticAvoider; +extern const void * RTTI_MovementPathManagerAgentStaticAvoider; +extern const void * RTTI_BSPathEventManager; +extern const void * RTTI_MovementMessageDoor; +extern const void * RTTI_MovementMessageApproachingDoor; +extern const void * RTTI_MovementMessageActivateDoor; +extern const void * RTTI_MovementMessageWarpToLocation; +extern const void * RTTI_MovementMessageWarpToMultiple; +extern const void * RTTI_MovementMessageBlocked; +extern const void * RTTI_MovementMessagePlayIdle; +extern const void * RTTI_MovementMessageSetStaticPath; +extern const void * RTTI_MovementMessageJump; +extern const void * RTTI_IMovementQueryDeltas; +extern const void * RTTI_IMovementDeltaStore; +extern const void * RTTI_MovementPostUpdateArbiter; +extern const void * RTTI_MovementArbiter; +extern const void * RTTI_IMovementPlannerSetArbitration; +extern const void * RTTI_MovementPlannerArbiter; +extern const void * RTTI_MovementPlannerAgent; +extern const void * RTTI_MovementPathManagerAgentLoadedAreaMonitor; +extern const void * RTTI_IMovementQueryFlight; +extern const void * RTTI_MovementAgentPathFollowerFlight; +extern const void * RTTI_MovementAgentPathFollowerVirtual; +extern const void * RTTI_IMovementQueryActorAvoidance; +extern const void * RTTI_MovementAgentActorAvoider; +extern const void * RTTI_MovementHandlerAgentAngleGain; +extern const void * RTTI_MovementHandlerAgentSpeedPassThrough; +extern const void * RTTI_MovementHandlerAgentAnglePassThrough; +extern const void * RTTI_MovementHandlerAgentDirectionPassThrough; +extern const void * RTTI_MovementHandlerAgentStrafing; +extern const void * RTTI_MovementPlannerAgentNavmeshBounds; +extern const void * RTTI_MovementPathManagerAgentAvoidBox; +extern const void * RTTI_MovementStateTransformOverride; +extern const void * RTTI_MovementStateTweener; +extern const void * RTTI_MovementStateFollowPath; +extern const void * RTTI_MovementTweenerAgentTargetPosAndVel; +extern const void * RTTI_MovementHandlerAgentStorePlannerOutput; +extern const void * RTTI_MovementHandlerAgentStandard; +extern const void * RTTI_MovementMessagePathEvent; +extern const void * RTTI_MovementMessagePathComplete; +extern const void * RTTI_MovementMessagePathFailed; +extern const void * RTTI_MovementMessagePathCleared; +extern const void * RTTI_BSPathingSearchRayCast; +extern const void * RTTI_ISmoothingRayValidator; +extern const void * RTTI_GroundPathRayValidator; +extern const void * RTTI_WaterPathRayValidator; +extern const void * RTTI_IPathingNodeGenerator; +extern const void * RTTI_GroundPathPathingNodeGenerator; +extern const void * RTTI_WaterPathPathingNodeGenerator; +extern const void * RTTI_BSNavmeshObstacleUndoData; +extern const void * RTTI_VelocityObstacle__Utilities__BuildOpenEdgeListsVisitor; +extern const void * RTTI_IPathFollowerState; +extern const void * RTTI_ICheckEndReachedFunctor; +extern const void * RTTI_PathFollowerStatePlayIdle; +extern const void * RTTI_PathFollowerStateFollowPathToParameter; +extern const void * RTTI_PathFollowerStateKeepLastDirection; +extern const void * RTTI_PathFollowerStateShortcut; +extern const void * RTTI_PathFollowerStateTurnToAngle; +extern const void * RTTI_MovementArbitrationVectorAdd; +extern const void * RTTI_bnet__BaseCallback; +extern const void * RTTI_BSPlatform__BSTerminatedCallback; +extern const void * RTTI_BSPlatform__BSBethesdaPlatform; +extern const void * RTTI_BSPlatform__BSAsyncRequestBase; +extern const void * RTTI_bnet__EmptyTerminationCB; +extern const void * RTTI_bnet__ExternalAuthInfo; +extern const void * RTTI_bnet__SteamExternalAuthInfo; +extern const void * RTTI_BSPlatform__BSUploadRequest; +extern const void * RTTI_BSPlatform__BSDownloadRequest; +extern const void * RTTI_bnet___impl__RefCount; +extern const void * RTTI_bnet___impl__SharedMutex; +extern const void * RTTI_bnet__IDynamicVariableDeleter; +extern const void * RTTI_bnet__ConfigurationEntry; +extern const void * RTTI_bnet___impl__IPool; +extern const void * RTTI_bnet__ConfigurationEntryBoolean; +extern const void * RTTI_bnet__ConfigurationEntryInteger; +extern const void * RTTI_bnet__ConfigurationEntryFloat; +extern const void * RTTI_bnet__ConfigurationEntryLong; +extern const void * RTTI_bnet__ConfigurationEntryString; +extern const void * RTTI_bnet__IJobCollection; +extern const void * RTTI_bnet___detail__Composite; +extern const void * RTTI_std__bad_weak_ptr; +extern const void * RTTI_std___Ref_count_base; +extern const void * RTTI_bnet__IHeap; +extern const void * RTTI_bnet__IHttpConnectionProvider; +extern const void * RTTI_bnet__Network; +extern const void * RTTI_bnet__UserQueue; +extern const void * RTTI_bnet__UserProcessorCollection; +extern const void * RTTI_bnet__JobProcessorCollection; +extern const void * RTTI_bnet___impl__AsyncOperation; +extern const void * RTTI_bnet__SuspendFlow; +extern const void * RTTI_bnet__ResumeFlow; +extern const void * RTTI_bnet__BaseHttpRequest; +extern const void * RTTI_bnet__LogoutFlow; +extern const void * RTTI_bnet__IUgcUploader; +extern const void * RTTI_bnet__ChunkedUploadFlow; +extern const void * RTTI_bnet__IDownloader; +extern const void * RTTI_bnet__DownloadFlow; +extern const void * RTTI_bnet__FileDownloader; +extern const void * RTTI_bnet__UgcBrowseRequest; +extern const void * RTTI_bnet__UgcArgLessRequest; +extern const void * RTTI_bnet__UgcCategoryCountRequest; +extern const void * RTTI_bnet__UgcContentUploadRequest; +extern const void * RTTI_bnet__UgcImageUploadRequest; +extern const void * RTTI_bnet__UgcAddVideoRequest; +extern const void * RTTI_bnet__UgcContentRequest; +extern const void * RTTI_bnet__UgcContentUploadDetailsRequest; +extern const void * RTTI_bnet__UgcCreationRequest; +extern const void * RTTI_bnet__UgcEditRequest; +extern const void * RTTI_bnet__UgcFlagRequest; +extern const void * RTTI_bnet__UgcListContentRequest; +extern const void * RTTI_bnet__UgcListCategoriesRequest; +extern const void * RTTI_bnet__UgcListDlcRequest; +extern const void * RTTI_bnet__Ugc__ListPlatformsRequest; +extern const void * RTTI_bnet__Ugc__ListProductsRequest; +extern const void * RTTI_bnet__UgcListFollowedAuthorRequest; +extern const void * RTTI_bnet__UgcListNotificationRequest; +extern const void * RTTI_bnet__UgcListBlacklistedRequest; +extern const void * RTTI_bnet__UgcListFeaturedContentRequest; +extern const void * RTTI_bnet__UgcListByEntitlementIdsRequest; +extern const void * RTTI_bnet__UgcNotificationAcknowledgeRequest; +extern const void * RTTI_bnet__UgcPreviewUploadRequest; +extern const void * RTTI_bnet__UgcQueryRequest; +extern const void * RTTI_bnet__UgcRateRequest; +extern const void * RTTI_bnet__UgcAddReleaseNoteRequest; +extern const void * RTTI_bnet__UgcUpdateReleaseNoteRequest; +extern const void * RTTI_bnet__UgcRemoveReleaseNoteRequest; +extern const void * RTTI_bnet__Ugc__RefreshEntitlementRequest; +extern const void * RTTI_bnet__Job; +extern const void * RTTI_bnet__FreeLessHeap; +extern const void * RTTI_bnet__AcquirableFreeLessHeap; +extern const void * RTTI_bnet__NetworkLoop; +extern const void * RTTI_bnet__UserProcessor; +extern const void * RTTI_bnet__AcceptLegalDocumentsRequest; +extern const void * RTTI_bnet__ListLegalDocumentsFlow; +extern const void * RTTI_bnet__AccountLinkingRequest; +extern const void * RTTI_bnet__AccountQuickCreateFlow; +extern const void * RTTI_bnet__AnonymousAuthenticationFlow; +extern const void * RTTI_bnet__SessionAnonymousCreateFlow; +extern const void * RTTI_bnet__SessionQuickCreateFlow; +extern const void * RTTI_bnet__AccountDeletionRequest; +extern const void * RTTI_bnet__AddFingerprintRequest; +extern const void * RTTI_bnet__IsAccountLinkableRequest; +extern const void * RTTI_bnet__UserCredentialsAuthenticationFlow; +extern const void * RTTI_bnet__ExternalAuthenticationFlow; +extern const void * RTTI_bnet__GameCodeAuthenticationFlow; +extern const void * RTTI_bnet__UnlinkAccountFlow; +extern const void * RTTI_bnet__UsernameValidationRequest; +extern const void * RTTI_bnet__VerifyFingerprintRequest; +extern const void * RTTI_bnet__VerifyLinkRequest; +extern const void * RTTI_bnet__TwitchAccountInfoRequest; +extern const void * RTTI_bnet__CheckEmailRequest; +extern const void * RTTI_bnet__RecoverPasswordRequest; +extern const void * RTTI_bnet__RecoverUsernameRequest; +extern const void * RTTI_bnet__ResendVerificationRequest; +extern const void * RTTI_bnet__SessionValidateRequest; +extern const void * RTTI_bnet__AccountInfoRequest; +extern const void * RTTI_bnet__RetrieveExternalAccountJob; +extern const void * RTTI_bnet__ExternalServiceRetrieveAccountRequest; +extern const void * RTTI_bnet__AccountUpgradeAnonymousToQuickRequest; +extern const void * RTTI_bnet__GameAccountLinkFlow; +extern const void * RTTI_bnet__GameAccountLinkConflictResolveFlow; +extern const void * RTTI_bnet__GameAccountExternalLinkConflictResolveFlow; +extern const void * RTTI_bnet__GameAccountUserCredentialsAuthenticationFlow; +extern const void * RTTI_bnet__GameAccountExternalAuthenticationFlow; +extern const void * RTTI_bnet__GameAccountExternalLinkFlow; +extern const void * RTTI_bnet__GameAccountGameCodeAuthenticationFlow; +extern const void * RTTI_bnet__SessionResumeFlow; +extern const void * RTTI_bnet__NewGameSessionFlow; +extern const void * RTTI_bnet__Entitlements__ListRequest; +extern const void * RTTI_bnet__Entitlements__ConsumeRequest; +extern const void * RTTI_bnet__Entitlements__SearchRequest; +extern const void * RTTI_bnet__EventLog__LogEventRequest; +extern const void * RTTI_bnet__VCCS__Wallet__BalanceRequest; +extern const void * RTTI_bnet__VCCS__Wallet__PurchaseRequest; +extern const void * RTTI_bnet__UgcMtxPurchaseFlow; +extern const void * RTTI_bnet__VCCS__Fulfillment__UpdateFirstPartyEntitlementRequest; +extern const void * RTTI_bnet__VCCS__Catalog__GetItemRequest; +extern const void * RTTI_bnet__VCCS__Catalog__ListItemsRequest; +extern const void * RTTI_bnet__CMS__ListMessagesRequest; +extern const void * RTTI_bnet__Status__GetExtServerStatusRequest; +extern const void * RTTI_bnet__SessionLogoutRequest; +extern const void * RTTI_bnet__UgcCancelUploadRequest; +extern const void * RTTI_bnet__UgcChunkedUploadCompletionRequest; +extern const void * RTTI_bnet__UgcChunkedUploadInitiationRequest; +extern const void * RTTI_bnet__UgcChunkUploadRequest; +extern const void * RTTI_bnet__CdpFileInfoRequest; +extern const void * RTTI_bnet__CdpKeyRequest; +extern const void * RTTI_bnet__BaseFileDownloader; +extern const void * RTTI_bnet__HttpTransport; +extern const void * RTTI_bnet__WinHttpTransport; +extern const void * RTTI_bnet__SessionRefreshRequest; +extern const void * RTTI_bnet__SessionResumeRequest; +extern const void * RTTI_bnet__ListRequiredLegalDocumentsRequest; +extern const void * RTTI_bnet__AuthenticationFlow; +extern const void * RTTI_bnet__BeamQuickCreationRequest; +extern const void * RTTI_bnet__AnonymousAuthRequest; +extern const void * RTTI_bnet__SessionAnonymousAccountCreateRequest; +extern const void * RTTI_bnet__SessionQuickCreateRequest; +extern const void * RTTI_bnet__SessionAuthRequest; +extern const void * RTTI_bnet__BeamUserCredentialsAuthenticationRequest; +extern const void * RTTI_bnet__BeamExternalAuthenticationRequest; +extern const void * RTTI_bnet__SessionExternalAuthRequest; +extern const void * RTTI_bnet__Session__GameCodeAuthRequest; +extern const void * RTTI_bnet__TokenUpgradeRequest; +extern const void * RTTI_bnet__AccountUnlinkingRequest; +extern const void * RTTI_bnet__RetrieveExternalAccountRequest; +extern const void * RTTI_bnet__GameAccountLinkRequest; +extern const void * RTTI_bnet__GameAccountLinkConflictResolveRequest; +extern const void * RTTI_bnet__GameAccountExternalLinkConflictResolveRequest; +extern const void * RTTI_bnet__GameAccountAuthenticationRequest; +extern const void * RTTI_bnet__GameAccountExternalAuthRequest; +extern const void * RTTI_bnet__GameAccountExternalLinkRequest; +extern const void * RTTI_bnet__Session__GameAccountGameCodeAuthRequest; +extern const void * RTTI_bnet__SessionTokenRequest; +extern const void * RTTI_bnet__CdpAuthenticationRequest; +extern const void * RTTI_bnet__SessionResumeTokenRequest; +extern const void * RTTI_bnet__BeamAuthenticationRequest; +extern const void * RTTI_BSScript__IFindBoundObjectFunctor; +extern const void * RTTI_BSScript__IVMObjectBindInterface; +extern const void * RTTI_BSScript__ObjectTypeInfo; +extern const void * RTTI_BSScript__IComplexType; +extern const void * RTTI_BSScript__IVMSaveLoadInterface; +extern const void * RTTI_BSScript__IVirtualMachine; +extern const void * RTTI_BSScript__ITypeLinkedCallback; +extern const void * RTTI_BSScript__IVMDebugInterface; +extern const void * RTTI_BSScript__ICachedErrorMessage; +extern const void * RTTI_BSScript__ErrorLogger; +extern const void * RTTI_BSScript__IFunction; +extern const void * RTTI_BSScript__NF_util__NativeFunctionBase; +extern const void * RTTI_BSScript__StructTypeInfo; +extern const void * RTTI_BSScript__IMemoryPagePolicy; +extern const void * RTTI_BSScript__SimpleAllocMemoryPagePolicy; +extern const void * RTTI_BSScript__CompiledScriptLoader; +extern const void * RTTI_BSScript__Internal__VirtualMachine; +extern const void * RTTI_BSScript__Internal__IFuncCallQuery; +extern const void * RTTI_BSScript__Internal__RawFuncCallQuery; +extern const void * RTTI_BSScript__IObjectProcessor; +extern const void * RTTI_BSScript__Internal__AutoPropGetFunction; +extern const void * RTTI_BSScript__Internal__AutoPropSetFunction; +extern const void * RTTI_BSScript__Internal__CodeTasklet; +extern const void * RTTI_BSScript__ByteCode__PackedInstructionStream__InstructionFunctor; +extern const void * RTTI_BSScript__Internal__NativeFunctionStub; +extern const void * RTTI_BSScript__UnlinkedTypes__InstructionStream__InstructionFunctor; +extern const void * RTTI_BSScript__LinkerProcessor; +extern const void * RTTI_BSScript__Internal__ScriptFunction; +extern const void * RTTI_BSPrecomputedVisibility__MultiCellVisibilityData__TomeCollectionBuildTask; +extern const void * RTTI_BSShaderProperty; +extern const void * RTTI_BSLightingShaderMaterial; +extern const void * RTTI_BSEffectShaderProperty; +extern const void * RTTI_BSLightingShaderProperty; +extern const void * RTTI_BSFadeNode; +extern const void * RTTI_BSLeafAnimNode; +extern const void * RTTI_BSTreeNode; +extern const void * RTTI_BSImagespaceShaderVatsTargetDebug; +extern const void * RTTI_BSImagespaceShaderVatsTarget; +extern const void * RTTI_BSImagespaceShaderAlphaBlend; +extern const void * RTTI_BSImagespaceShaderBlur3; +extern const void * RTTI_BSImagespaceShaderBlur5; +extern const void * RTTI_BSImagespaceShaderBlur7; +extern const void * RTTI_BSImagespaceShaderBlur9; +extern const void * RTTI_BSImagespaceShaderBlur11; +extern const void * RTTI_BSImagespaceShaderBlur13; +extern const void * RTTI_BSImagespaceShaderBlur15; +extern const void * RTTI_BSImagespaceShaderNonHDRBlur3; +extern const void * RTTI_BSImagespaceShaderNonHDRBlur5; +extern const void * RTTI_BSImagespaceShaderNonHDRBlur7; +extern const void * RTTI_BSImagespaceShaderNonHDRBlur9; +extern const void * RTTI_BSImagespaceShaderNonHDRBlur11; +extern const void * RTTI_BSImagespaceShaderNonHDRBlur13; +extern const void * RTTI_BSImagespaceShaderNonHDRBlur15; +extern const void * RTTI_BSImagespaceShaderBrightPassBlur3; +extern const void * RTTI_BSImagespaceShaderBrightPassBlur5; +extern const void * RTTI_BSImagespaceShaderBrightPassBlur7; +extern const void * RTTI_BSImagespaceShaderBrightPassBlur9; +extern const void * RTTI_BSImagespaceShaderBrightPassBlur11; +extern const void * RTTI_BSImagespaceShaderBrightPassBlur13; +extern const void * RTTI_BSImagespaceShaderBrightPassBlur15; +extern const void * RTTI_BSImagespaceShaderHDRBlurX15_320CS; +extern const void * RTTI_BSImagespaceShaderHDRBlurX15_480CS; +extern const void * RTTI_BSImagespaceShaderHDRBlurX15_1024CS; +extern const void * RTTI_BSImagespaceShaderBlurX15_480CS; +extern const void * RTTI_BSImagespaceShaderBlurX13_480CS; +extern const void * RTTI_BSImagespaceShaderBlurX11_480CS; +extern const void * RTTI_BSImagespaceShaderBlurX9_480CS; +extern const void * RTTI_BSImagespaceShaderBlurX7_480CS; +extern const void * RTTI_BSImagespaceShaderBlurX5_480CS; +extern const void * RTTI_BSImagespaceShaderBlurX3_480CS; +extern const void * RTTI_BSImagespaceShaderBlurY15_270CS; +extern const void * RTTI_BSImagespaceShaderBlurY13_270CS; +extern const void * RTTI_BSImagespaceShaderBlurY11_270CS; +extern const void * RTTI_BSImagespaceShaderBlurY9_270CS; +extern const void * RTTI_BSImagespaceShaderBlurY7_270CS; +extern const void * RTTI_BSImagespaceShaderBlurY5_270CS; +extern const void * RTTI_BSImagespaceShaderBlurY3_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassHDRBlurY15_180CS; +extern const void * RTTI_BSImagespaceShaderBrightPassHDRBlurY15_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassHDRBlurY15_1024CS; +extern const void * RTTI_BSImagespaceShaderBrightPassBlurY15_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassBlurY13_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassBlurY11_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassBlurY9_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassBlurY7_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassBlurY5_270CS; +extern const void * RTTI_BSImagespaceShaderBrightPassBlurY3_270CS; +extern const void * RTTI_BSImagespaceShaderCopy; +extern const void * RTTI_BSImagespaceShaderCopyScaleBias; +extern const void * RTTI_BSImagespaceShaderCopyVisAlpha; +extern const void * RTTI_BSImagespaceShaderTextureMask; +extern const void * RTTI_BSImagespaceShaderGreyScale; +extern const void * RTTI_BSImagespaceShaderDownsampleDepth; +extern const void * RTTI_BSImagespaceShaderCopyStencil; +extern const void * RTTI_BSImagespaceShaderCopyWaterMask; +extern const void * RTTI_BSImagespaceShaderCopyShadowMapToArray; +extern const void * RTTI_BSImagespaceShaderDepthOfField; +extern const void * RTTI_BSImagespaceShaderDepthOfFieldFogged; +extern const void * RTTI_BSImagespaceShaderDepthOfFieldSplitScreen; +extern const void * RTTI_BSImagespaceShaderDistantBlur; +extern const void * RTTI_BSImagespaceShaderDistantBlurFogged; +extern const void * RTTI_BSImagespaceShaderDoubleVision; +extern const void * RTTI_BSImagespaceShaderFullScreenColor; +extern const void * RTTI_BSImagespaceShaderFXAA; +extern const void * RTTI_BSImagespaceShaderTemporalAA; +extern const void * RTTI_BSImagespaceShaderTemporalAAPipboy; +extern const void * RTTI_BSImagespaceShaderTemporalAAPowerArmorPipboy; +extern const void * RTTI_BSImagespaceShaderGammaCorrect; +extern const void * RTTI_BSImagespaceShaderGammaCorrectResize; +extern const void * RTTI_BSImagespaceShaderGammaLinearize; +extern const void * RTTI_BSImagespaceShaderGammaCorrectLUT; +extern const void * RTTI_BSImagespaceShaderHDRDownSample4; +extern const void * RTTI_BSImagespaceShaderHDRDownSample16Lum; +extern const void * RTTI_BSImagespaceShaderHDRDownSample4RGB2Lum; +extern const void * RTTI_BSImagespaceShaderHDRDownSample16; +extern const void * RTTI_BSImagespaceShaderHDRDownSample4LumClamp; +extern const void * RTTI_BSImagespaceShaderHDRDownSample16LumClamp; +extern const void * RTTI_BSImagespaceShaderHDRDownSample4LightAdapt; +extern const void * RTTI_BSImagespaceShaderHDRDownSample16LightAdapt; +extern const void * RTTI_BSImagespaceShaderHDRTonemapBlendCinematic; +extern const void * RTTI_BSImagespaceShaderHDRTonemapBlendCinematicFade; +extern const void * RTTI_BSImagespaceShaderHDRDownSample4CS; +extern const void * RTTI_BSImagespaceShaderHDRDownSample4LumCS; +extern const void * RTTI_BSImagespaceShaderHDRDownSample16LumCS; +extern const void * RTTI_BSImagespaceShaderHDRDownSample64RGB2LumCS; +extern const void * RTTI_BSImagespaceShaderHDRDownSample2LightAdaptCS; +extern const void * RTTI_BSImagespaceShaderLocalMap; +extern const void * RTTI_BSImagespaceShaderLocalMapCompanion; +extern const void * RTTI_BSImagespaceShaderMap; +extern const void * RTTI_BSImagespaceShaderNoiseScrollAndBlend; +extern const void * RTTI_BSImagespaceShaderNoiseNormalmap; +extern const void * RTTI_BSImagespaceShaderRadialBlur; +extern const void * RTTI_BSImagespaceShaderRadialBlurMedium; +extern const void * RTTI_BSImagespaceShaderRadialBlurHigh; +extern const void * RTTI_BSImagespaceShaderRefraction; +extern const void * RTTI_BSImagespaceShaderWaterDisplacementClearSimulation; +extern const void * RTTI_BSImagespaceShaderWaterDisplacementTexOffset; +extern const void * RTTI_BSImagespaceShaderWaterDisplacementWadingRipple; +extern const void * RTTI_BSImagespaceShaderWaterDisplacementRainRipple; +extern const void * RTTI_BSImagespaceShaderWaterWadingHeightmap; +extern const void * RTTI_BSImagespaceShaderWaterRainHeightmap; +extern const void * RTTI_BSImagespaceShaderWaterBlendHeightmaps; +extern const void * RTTI_BSImagespaceShaderWaterSmoothHeightmap; +extern const void * RTTI_BSImagespaceShaderWaterDisplacementNormals; +extern const void * RTTI_BSImagespaceShaderPipboyScreen; +extern const void * RTTI_BSImagespaceShaderHUDGlass; +extern const void * RTTI_BSImagespaceShaderHUDGlassDropShadow; +extern const void * RTTI_BSImagespaceShaderHUDGlassBlurY; +extern const void * RTTI_BSImagespaceShaderHUDGlassBlurX; +extern const void * RTTI_BSImagespaceShaderHUDGlassMarkers; +extern const void * RTTI_BSImagespaceShaderHUDGlassClear; +extern const void * RTTI_BSImagespaceShaderHUDGlassCopy; +extern const void * RTTI_BSImagespaceShaderVLSSliceCoord; +extern const void * RTTI_BSImagespaceShaderVLSSliceInterp; +extern const void * RTTI_BSImagespaceShaderVLSSliceStencil; +extern const void * RTTI_BSImagespaceShaderVLSSliceScatterRay; +extern const void * RTTI_BSImagespaceShaderVLSSliceScatterInterp; +extern const void * RTTI_BSImagespaceShaderVLSScatterAccum; +extern const void * RTTI_BSImagespaceShaderVLSSpotLight; +extern const void * RTTI_BSImagespaceShaderVLSApplication; +extern const void * RTTI_BSImagespaceShaderVLSComposite; +extern const void * RTTI_BSImagespaceShaderModMenuEffect; +extern const void * RTTI_BSImagespaceShaderModMenuGlowComposite; +extern const void * RTTI_BSImagespaceShaderAmbientOcclusion; +extern const void * RTTI_BSImagespaceShaderAmbientOcclusionBlur; +extern const void * RTTI_BSImagespaceShaderMotionBlur; +extern const void * RTTI_BSImagespaceShaderSAOCameraZ; +extern const void * RTTI_BSImagespaceShaderSAOMinify; +extern const void * RTTI_BSImagespaceShaderSAORawAO; +extern const void * RTTI_BSImagespaceShaderSAOBlurH; +extern const void * RTTI_BSImagespaceShaderSAOBlurV; +extern const void * RTTI_BSImagespaceShaderSAOCameraZCS; +extern const void * RTTI_BSImagespaceShaderSAOCameraAndMipsZCS; +extern const void * RTTI_BSImagespaceShaderSAOMipsZCS; +extern const void * RTTI_BSImagespaceShaderSAOMinifyCS; +extern const void * RTTI_BSImagespaceShaderSAORawAOCS; +extern const void * RTTI_BSImagespaceShaderSAOBlurHCS; +extern const void * RTTI_BSImagespaceShaderSAOBlurVCS; +extern const void * RTTI_BSImagespaceShaderSAORawAOEditor; +extern const void * RTTI_BSImagespaceShaderSSLRBlurH; +extern const void * RTTI_BSImagespaceShaderSSLRBlurV; +extern const void * RTTI_BSImagespaceShaderSSLRRaytracing; +extern const void * RTTI_BSImagespaceShaderSSLRPrepass; +extern const void * RTTI_BSImagespaceShaderSunbeams; +extern const void * RTTI_BSImagespaceShaderUpsampleDynamicResolution; +extern const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass1; +extern const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass2; +extern const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass3; +extern const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass4; +extern const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass4Fogged; +extern const void * RTTI_BSImagespaceShaderRainSplash; +extern const void * RTTI_BSImagespaceShaderRainSplashUpdate; +extern const void * RTTI_BSImagespaceShaderRainSplashDraw; +extern const void * RTTI_BSDeferredDecal__BSDFDecal; +extern const void * RTTI_ShadowSceneNode; +extern const void * RTTI_BSImagespaceShaderLensFlare; +extern const void * RTTI_BSImagespaceShaderLensFlareVisibility; +extern const void * RTTI_BSGrassShaderProperty; +extern const void * RTTI_BSLightingShaderMaterialEnvmap; +extern const void * RTTI_BSLightingShaderMaterialBase; +extern const void * RTTI_BSLightingShaderMaterialEye; +extern const void * RTTI_BSLightingShaderMaterialGlowmap; +extern const void * RTTI_BSLightingShaderMaterialParallax; +extern const void * RTTI_BSLightingShaderMaterialParallaxOcc; +extern const void * RTTI_BSLightingShaderMaterialDismemberment; +extern const void * RTTI_BSLightingShaderMaterialFace; +extern const void * RTTI_BSLightingShaderMaterialSkinTint; +extern const void * RTTI_BSLightingShaderMaterialHairTint; +extern const void * RTTI_BSLightingShaderMaterialLandscape; +extern const void * RTTI_BSLightingShaderMaterialLODLandscape; +extern const void * RTTI_BSLightingShaderMaterialSnow; +extern const void * RTTI_BSLightingShaderMaterialMultiLayerParallax; +extern const void * RTTI_ImageSpaceEffectParam; +extern const void * RTTI_ImageSpaceShaderParam; +extern const void * RTTI_BSShaderAccumulator; +extern const void * RTTI_BSWaterShaderProperty; +extern const void * RTTI_BSBloodSplatterShaderProperty; +extern const void * RTTI_BSBloodSplatterShader; +extern const void * RTTI_ImageSpaceEffectVatsTarget; +extern const void * RTTI_NVFlex__DebrisInstanceNode; +extern const void * RTTI_NVFlex__DebrisInstanceGroup; +extern const void * RTTI_NVFlex__DebrisInstanceGroupPass; +extern const void * RTTI_BSDismember__BSShaderDismembermentExtraData; +extern const void * RTTI_BSShaderMaterial; +extern const void * RTTI_BSPackedCombinedGeomDataExtra; +extern const void * RTTI_BSPackedSharedGeometryBuilder; +extern const void * RTTI_BSPackedCombinedSharedGeomDataExtra; +extern const void * RTTI_BSGeometryListCullingProcess; +extern const void * RTTI_BSImagespaceShaderCopyParam; +extern const void * RTTI_BSLight; +extern const void * RTTI_BSDFPrePassShader; +extern const void * RTTI_ImageSpaceEffectHDR; +extern const void * RTTI_ImageSpaceEffectAmbientOcclusion; +extern const void * RTTI_ImageSpaceEffectScalableAmbientObscuranceCS; +extern const void * RTTI_ImageSpaceEffectSunbeams; +extern const void * RTTI_ImageSpaceEffectMotionBlur; +extern const void * RTTI_ImageSpaceEffectScalableAmbientObscurance; +extern const void * RTTI_ImageSpaceEffectTemporalAA; +extern const void * RTTI_ImageSpaceEffectOption; +extern const void * RTTI_ImageSpaceEffectBokehDepthOfField; +extern const void * RTTI_BSDFCompositeShader; +extern const void * RTTI_BSSkyShader; +extern const void * RTTI_BSSkyShaderProperty; +extern const void * RTTI_ImageSpaceEffectRainSplash; +extern const void * RTTI_BSBatchRenderer; +extern const void * RTTI_BSFaceCustomizationShader; +extern const void * RTTI_BSEffectShaderMaterial; +extern const void * RTTI_BSEffectShader; +extern const void * RTTI_BSDistantTreeShaderProperty; +extern const void * RTTI_BSDistantTreeShader; +extern const void * RTTI_BSWaterShaderMaterial; +extern const void * RTTI_BSCubeMapCamera; +extern const void * RTTI_BSWaterShader; +extern const void * RTTI_ImageSpaceEffectWaterDisplacement; +extern const void * RTTI_ImageSpaceEffectModMenu; +extern const void * RTTI_BSShader; +extern const void * RTTI_BSReloadShaderI; +extern const void * RTTI_ImageSpaceEffectPipboyScreen; +extern const void * RTTI_ImageSpaceEffectBlur; +extern const void * RTTI_ImageSpaceEffectFullScreenBlur; +extern const void * RTTI_ImageSpaceEffectGetHit; +extern const void * RTTI_ImageSpaceEffectRadialBlur; +extern const void * RTTI_BSClearZNode; +extern const void * RTTI_BSFogProperty; +extern const void * RTTI_BSLightingShader; +extern const void * RTTI_ImageSpaceEffectFullScreenColor; +extern const void * RTTI_BSInstanceGroup; +extern const void * RTTI_BSCombinedNode; +extern const void * RTTI_BSEyeCenterExtraData; +extern const void * RTTI_BSUtilityShader; +extern const void * RTTI_BSMeshLODTriShape; +extern const void * RTTI_BSMultiIndexTriShape; +extern const void * RTTI_BSOrderedNode; +extern const void * RTTI_BSParticleShader; +extern const void * RTTI_BSDFLightShader; +extern const void * RTTI_BSLightingShaderPropertyFloatController; +extern const void * RTTI_BSLightingShaderPropertyUShortController; +extern const void * RTTI_BSLightingShaderPropertyColorController; +extern const void * RTTI_BSEffectShaderPropertyFloatController; +extern const void * RTTI_BSEffectShaderPropertyColorController; +extern const void * RTTI_BSNiAlphaPropertyTestRefController; +extern const void * RTTI_BSPSysSimpleColorModifier; +extern const void * RTTI_BSPSysLODModifier; +extern const void * RTTI_BSImagespaceShader; +extern const void * RTTI_BSShadowLight; +extern const void * RTTI_BSShadowDirectionalLight; +extern const void * RTTI_ImageSpaceEffect; +extern const void * RTTI_ImageSpaceEffectRefraction; +extern const void * RTTI_ImageSpaceEffectDepthOfField; +extern const void * RTTI_ImageSpaceEffectDepthOfFieldSplitScreen; +extern const void * RTTI_ImageSpaceEffectBlurCS; +extern const void * RTTI_ImageSpaceEffectHDRCS; +extern const void * RTTI_ImageSpaceEffectMap; +extern const void * RTTI_ImageSpaceEffectNoise; +extern const void * RTTI_ImageSpaceEffectHUDGlass; +extern const void * RTTI_ImageSpaceEffectVLSLight; +extern const void * RTTI_ImageSpaceEffectVLS; +extern const void * RTTI_ImageSpaceEffectUpsampleDynamicResolution; +extern const void * RTTI_BSImagespaceComputeShader; +extern const void * RTTI_BSShadowFrustumLight; +extern const void * RTTI_BSShadowParabolicLight; +extern const void * RTTI_NVFlex__DebrisInstanceData; +extern const void * RTTI_NVFlex__DebrisInstanceDataOwner; +extern const void * RTTI_BSComputeShader; +extern const void * RTTI_BSLODMultiIndexTriShape; +extern const void * RTTI_BSDirectoryMonitorThread; +extern const void * RTTI_BSShaderResourceManager; +extern const void * RTTI_BSSystemMonitor__SocketThread; +extern const void * RTTI_BSSystemMonitor__MemOpsThread; +extern const void * RTTI_BSGameDataSystemUtility; +extern const void * RTTI_BSSaveDataSystemUtility; +extern const void * RTTI_BSSystemUtility; +extern const void * RTTI_BSWin32GameDataSystemUtility; +extern const void * RTTI_BSSysInfoSystemUtility; +extern const void * RTTI_BSGameStreamUtility; +extern const void * RTTI_BSAwardsSystemUtility; +extern const void * RTTI_BSCacheDriveSystemUtility; +extern const void * RTTI_BSSystemUtilitiesStrings; +extern const void * RTTI_BSMsgDialogSystemUtility; +extern const void * RTTI_BSDiscBootSystemUtility; +extern const void * RTTI_type_info; +extern const void * RTTI_BSSocketServer; +extern const void * RTTI_NiCollisionObject; +extern const void * RTTI_BSLines; +extern const void * RTTI_BSDynamicLines; +extern const void * RTTI_BSParabolicCullingProcess; diff --git a/f4se/f4se/GameRTTI.inl b/f4se/f4se/GameRTTI.inl new file mode 100644 index 0000000..3f0e54e --- /dev/null +++ b/f4se/f4se/GameRTTI.inl @@ -0,0 +1,3975 @@ +const void * RTTI_IAIWorldLocation = (void *)0x036CB118; +const void * RTTI_BGSAIWorldLocation = (void *)0x036CB188; +const void * RTTI_BGSAIWorldLocationInteriorCell = (void *)0x036CB1B8; +const void * RTTI_BGSAIWorldLocationPointRadius = (void *)0x036CB278; +const void * RTTI_BGSAIWorldLocationPrimitive = (void *)0x036CB350; +const void * RTTI_FindTriangleForLocationFilter = (void *)0x036CB3F0; +const void * RTTI_FindTriangleForLocationFilterCheckDeltaZ = (void *)0x036CB428; +const void * RTTI_BGSAIWorldLocationRefRadius = (void *)0x036CB470; +const void * RTTI_BSTArrayBase__IAllocatorFunctor = (void *)0x036CB678; +const void * RTTI_IPackageDataTypeCheck = (void *)0x036CB760; +const void * RTTI_IProcedureTreeExecState = (void *)0x036CB790; +const void * RTTI_IProcedure = (void *)0x036CB7C0; +const void * RTTI_BGSProcedureBase = (void *)0x036CB7E8; +const void * RTTI_BGSProcedureHeadtrack = (void *)0x036CB810; +const void * RTTI_BGSProcedureHeadtrackExecState = (void *)0x036CB8C0; +const void * RTTI_BGSProcedurePlayIdle = (void *)0x036CBCC0; +const void * RTTI_BGSProcedurePlayIdleExecState = (void *)0x036CBD70; +const void * RTTI_TESActionData = (void *)0x036CBE50; +const void * RTTI_BGSActionData = (void *)0x036CBE78; +const void * RTTI_Setting = (void *)0x036CC510; +const void * RTTI_INISettingCollection = (void *)0x036CC5A8; +const void * RTTI_hkBaseObject = (void *)0x036CC618; +const void * RTTI_hknpCollisionQueryCollector = (void *)0x036CC640; +const void * RTTI_hknpClosestHitCollector = (void *)0x036CC678; +const void * RTTI_GameSettingCollection = (void *)0x036CC6A8; +const void * RTTI_IMovementParameters = (void *)0x036CC7A0; +const void * RTTI_MovementParameters = (void *)0x036CC7D0; +const void * RTTI_BGSProcedureRange = (void *)0x036CC800; +const void * RTTI_BGSProcedureRangeExecState = (void *)0x036CC8A8; +const void * RTTI_BGSProcedureStayAway = (void *)0x036CCC40; +const void * RTTI_BGSProcedureStayAwayExecState = (void *)0x036CCCF0; +const void * RTTI_IAnimationEventDataForEachFunctor = (void *)0x036CCFC0; +const void * RTTI_BGSRetargetOnDeleteExtraData = (void *)0x036CDB48; +const void * RTTI_IForEachDynamicIdleInDir = (void *)0x036CDB80; +const void * RTTI_DynamicIdleDataSingletonHelper = (void *)0x036CDBB0; +const void * RTTI_BGSObjectInstanceExtra = (void *)0x036CDE50; +const void * RTTI_BSExtraData = (void *)0x036CDE80; +const void * RTTI_ExtraInitActions = (void *)0x036CDEA8; +const void * RTTI_BaseFormComponent = (void *)0x036CDEE8; +const void * RTTI_BGSAttachParentArray = (void *)0x036CDF70; +const void * RTTI_BGSCraftingUseSound = (void *)0x036CDFD8; +const void * RTTI_BGSFeaturedItemMessage = (void *)0x036CE008; +const void * RTTI_BGSForcedLocRefType = (void *)0x036CE090; +const void * RTTI_BGSInstanceNamingRulesForm = (void *)0x036CE0C0; +const void * RTTI_BGSMod__Template__Items = (void *)0x036CE1F0; +const void * RTTI_TESFullName = (void *)0x036CE220; +const void * RTTI_BGSMod__Template__Item = (void *)0x036CE248; +const void * RTTI_BGSModelMaterialSwap = (void *)0x036CE300; +const void * RTTI_BGSNativeTerminalForm = (void *)0x036CE380; +const void * RTTI_BGSPreviewTransform = (void *)0x036CE400; +const void * RTTI_BGSPropertySheet = (void *)0x036CE430; +const void * RTTI_BGSSoundTagComponent = (void *)0x036CE4B8; +const void * RTTI_InitActionI = (void *)0x036CE500; +const void * RTTI_BGSConveyorBelt__ConveyorBelt = (void *)0x036CE528; +const void * RTTI_hkReferencedObject = (void *)0x036CE720; +const void * RTTI_hknpAction = (void *)0x036CE750; +const void * RTTI_hknpUnaryAction = (void *)0x036CE778; +const void * RTTI_BGSMotorAction__Action = (void *)0x036CE7B8; +const void * RTTI_BGSMotorAction__FanMotor = (void *)0x036CE7E8; +const void * RTTI_NiRefObject = (void *)0x036CDAD8; +const void * RTTI_hknpPhysicsSystemData = (void *)0x036CEA28; +const void * RTTI_CellHfCollision = (void *)0x036CEA58; +const void * RTTI_hknpCollisionFilter = (void *)0x036CEAC0; +const void * RTTI_hknpAllHitsCollector = (void *)0x036CEAF0; +const void * RTTI_hknpBSCustomCollisionFilter = (void *)0x036CEB70; +const void * RTTI_hknpFlippedGetClosestPointsQueryCollector = (void *)0x036CEBB0; +const void * RTTI_IFormFactory = (void *)0x036CEBF0; +const void * RTTI_AlchemyItem = (void *)0x036CEC70; +const void * RTTI_BGSDualCastData = (void *)0x036CEFD8; +const void * RTTI_EffectArchetypes__MainStatusHandler = (void *)0x036CF8C8; +const void * RTTI_EffectSetting = (void *)0x036CFAF0; +const void * RTTI_EnchantmentItem = (void *)0x036CFCD8; +const void * RTTI_IngredientItem = (void *)0x036CFE88; +const void * RTTI_BSModelDB__QueuedHandles = (void *)0x036D0038; +const void * RTTI_AnimationSystemUtils__TESModelAndAnimationHandles = (void *)0x036D00E0; +const void * RTTI_TESModelDB__TESQueuedHandles = (void *)0x036D0128; +const void * RTTI_MagicItem = (void *)0x036CEC98; +const void * RTTI_MagicItem__PreloadableVisitor = (void *)0x036D01D0; +const void * RTTI_MagicItemTraversalFunctor = (void *)0x036D0208; +const void * RTTI_MagicItemFindFunctor = (void *)0x036D0238; +const void * RTTI_MagicItemFindKeywordFunctor = (void *)0x036D0268; +const void * RTTI_FindNonExcludedDeliveryFunctor = (void *)0x036D02D8; +const void * RTTI_FindEqualsFunctor = (void *)0x036D0380; +const void * RTTI_GetCostliestEffectFunctor = (void *)0x036D03A8; +const void * RTTI_LongestDurationFunctor = (void *)0x036D03D8; +const void * RTTI_LargestAreaFunctor = (void *)0x036D0408; +const void * RTTI_GetMagicItemDescriptionFunctor = (void *)0x036D0AE8; +const void * RTTI_ScrollItem = (void *)0x036D0B30; +const void * RTTI_SpellItem = (void *)0x036CEC50; +const void * RTTI_ExtraCell3D = (void *)0x036D0D00; +const void * RTTI_ExtraHavok = (void *)0x036D0D28; +const void * RTTI_ExtraRegionList = (void *)0x036D0D50; +const void * RTTI_ExtraCellSkyRegion = (void *)0x036D0D78; +const void * RTTI_ExtraCellImageSpace = (void *)0x036D0DA8; +const void * RTTI_ExtraCellGodRays = (void *)0x036D0DD8; +const void * RTTI_ExtraSeenData = (void *)0x036D0E00; +const void * RTTI_ExtraNorthRotation = (void *)0x036D0E28; +const void * RTTI_ExtraDetachTime = (void *)0x036D0E58; +const void * RTTI_ExtraProcessMiddleLow = (void *)0x036D0E80; +const void * RTTI_ExtraCellMusicType = (void *)0x036D0EB0; +const void * RTTI_ExtraCellAcousticSpace = (void *)0x036D0EE0; +const void * RTTI_ExtraCellWaterType = (void *)0x036D0F10; +const void * RTTI_TESObjectREFRDef__IAliasFunctor = (void *)0x036D15E0; +const void * RTTI_TESObjectREFRDef__ILinkedReferenceFunctor = (void *)0x036D1618; +const void * RTTI_ExtraCulledBone = (void *)0x036D1658; +const void * RTTI_ExtraLock = (void *)0x036D1680; +const void * RTTI_ExtraTeleport = (void *)0x036D16A0; +const void * RTTI_ExtraStartingPosition = (void *)0x036D16C8; +const void * RTTI_ExtraOwnership = (void *)0x036D16F8; +const void * RTTI_ExtraGlobal = (void *)0x036D1720; +const void * RTTI_ExtraRank = (void *)0x036D1748; +const void * RTTI_ExtraCount = (void *)0x036D1768; +const void * RTTI_ExtraLeveledItem = (void *)0x036D1790; +const void * RTTI_ExtraOutfitItem = (void *)0x036D17B8; +const void * RTTI_ExtraHealth = (void *)0x036D17E0; +const void * RTTI_ExtraTimeLeft = (void *)0x036D1808; +const void * RTTI_ExtraCharge = (void *)0x036D1830; +const void * RTTI_ExtraScale = (void *)0x036D1858; +const void * RTTI_ExtraFollower = (void *)0x036D1880; +const void * RTTI_ExtraOriginalReference = (void *)0x036D18A8; +const void * RTTI_ExtraPoison = (void *)0x036D18D8; +const void * RTTI_ExtraHeadingTarget = (void *)0x036D1900; +const void * RTTI_ExtraCreatureAwakeSound = (void *)0x036D1930; +const void * RTTI_ExtraObjectHealth = (void *)0x036D1960; +const void * RTTI_ExtraActorCause = (void *)0x036D1988; +const void * RTTI_ExtraRadioData = (void *)0x036D19B0; +const void * RTTI_ExtraPatrolRefData = (void *)0x036D19D8; +const void * RTTI_ExtraNavMeshPortal = (void *)0x036D1A08; +const void * RTTI_ExtraOcclusionPlaneRefData = (void *)0x036D1A38; +const void * RTTI_ExtraPortalRefData = (void *)0x036D1A70; +const void * RTTI_ExtraSceneData = (void *)0x036D1AA0; +const void * RTTI_ExtraBadPosition = (void *)0x036D1AC8; +const void * RTTI_ExtraHeadTrackingWeight = (void *)0x036D1AF0; +const void * RTTI_ExtraFavorCost = (void *)0x036D1B20; +const void * RTTI_ExtraTextDisplayData = (void *)0x036D1B48; +const void * RTTI_ExtraHorse = (void *)0x036D1B78; +const void * RTTI_ExtraEnchantment = (void *)0x036D1BA0; +const void * RTTI_ExtraForcedTarget = (void *)0x036D1BC8; +const void * RTTI_ExtraUniqueID = (void *)0x036D1BF0; +const void * RTTI_ExtraFlags = (void *)0x036D1C18; +const void * RTTI_ExtraWaterCurrentZoneData = (void *)0x036D1C40; +const void * RTTI_ExtraMissingRefIDs = (void *)0x036D1C70; +const void * RTTI_ExtraRangedDistOverride = (void *)0x036D1CA0; +const void * RTTI_ExtraSoundOutputOverride = (void *)0x036D1CD0; +const void * RTTI_ExtraEditorID = (void *)0x036D1D00; +const void * RTTI_ExtraFavorite = (void *)0x036D1D28; +const void * RTTI_ExtraPrimitive = (void *)0x036D1D50; +const void * RTTI_ExtraAmmo = (void *)0x036D1D78; +const void * RTTI_ExtraCombinedRefs = (void *)0x036D1D98; +const void * RTTI_ExtraPreVisRefs = (void *)0x036D1DC0; +const void * RTTI_ExtraTransitionCellCount = (void *)0x036D1DE8; +const void * RTTI_ExtraGIDBuffer = (void *)0x036D1E18; +const void * RTTI_ExtraFromAlias = (void *)0x036D1E98; +const void * RTTI_ExtraOpenCloseActivateRef = (void *)0x036D1EC0; +const void * RTTI_ExtraTeleportName = (void *)0x036D1EF0; +const void * RTTI_ExtraCachedScale = (void *)0x036D1F18; +const void * RTTI_ExtraReferenceHandles = (void *)0x036D1F40; +const void * RTTI_ExtraLocation = (void *)0x036D1F70; +const void * RTTI_ExtraTresPassPackage = (void *)0x036D1F98; +const void * RTTI_ExtraAttachRef = (void *)0x036D1FC8; +const void * RTTI_ExtraAttachRefChildren = (void *)0x036D1FF0; +const void * RTTI_ExtraPowerLinks = (void *)0x036D2020; +const void * RTTI_ExtraReflectedRefs = (void *)0x036D2048; +const void * RTTI_ExtraReflectorRefs = (void *)0x036D2078; +const void * RTTI_ExtraWaterLightRefs = (void *)0x036D20A8; +const void * RTTI_ExtraLitWaterRefs = (void *)0x036D20D8; +const void * RTTI_ExtraHasNoRumors = (void *)0x036D2100; +const void * RTTI_ExtraModelSwap = (void *)0x036D2128; +const void * RTTI_ExtraRadius = (void *)0x036D2150; +const void * RTTI_ExtraCombatStyle = (void *)0x036D2178; +const void * RTTI_ExtraPackageData = (void *)0x036D21A0; +const void * RTTI_ExtraCollisionData = (void *)0x036D21C8; +const void * RTTI_ExtraLargeRefOwnerCells = (void *)0x036D21F8; +const void * RTTI_ExtraLightData = (void *)0x036D2228; +const void * RTTI_ExtraModRank = (void *)0x036D2250; +const void * RTTI_ExtraAttachedArrows3D = (void *)0x036D2278; +const void * RTTI_ExtraAlphaCutoff = (void *)0x036D22A8; +const void * RTTI_ExtraForcedLandingMarker = (void *)0x036D22D0; +const void * RTTI_ExtraCellWaterEnvMap = (void *)0x036D2300; +const void * RTTI_ExtraKeywords = (void *)0x036D2330; +const void * RTTI_ExtraMaterialSwap = (void *)0x036D2358; +const void * RTTI_ExtraProjectedDecalData = (void *)0x036D2380; +const void * RTTI_ExtraActivateText = (void *)0x036D23B0; +const void * RTTI_ExtraRadioReceiver = (void *)0x036D23D8; +const void * RTTI_ExtraRadioRepeater = (void *)0x036D2408; +const void * RTTI_ExtraAnimSounds = (void *)0x036D2438; +const void * RTTI_ExtraActorValueStorage = (void *)0x036D2460; +const void * RTTI_ExtraObjectBreakable = (void *)0x036D2490; +const void * RTTI_ExtraObjectSavedDynamicIdles = (void *)0x036D24C0; +const void * RTTI_ExtraObjectSavedUnrecoverableSubgraphs = (void *)0x036D24F8; +const void * RTTI_ExtraIgnoredAttractKeywords = (void *)0x036D2538; +const void * RTTI_ExtraInteriorLODWorldspace = (void *)0x036D2570; +const void * RTTI_ExtraBoneScaleMap = (void *)0x036D25A8; +const void * RTTI_ExtraFXPickNodes = (void *)0x036D25D0; +const void * RTTI_ExtraEssentialProtected = (void *)0x036D25F8; +const void * RTTI_ExtraInvestedGold = (void *)0x036D2628; +const void * RTTI_ExtraVoiceType = (void *)0x036D2650; +const void * RTTI_Workshop__ExtraData = (void *)0x036D2678; +const void * RTTI_NonActorMagicCaster = (void *)0x036D26A8; +const void * RTTI_ExtraAnimGraphManager = (void *)0x036D2F40; +const void * RTTI_ExtraDismemberedLimbs = (void *)0x036D2F70; +const void * RTTI_ExtraBiped = (void *)0x036D2FA0; +const void * RTTI_ExtraLight = (void *)0x036D2FC8; +const void * RTTI_ExtraLeveledCreature = (void *)0x036D2FF0; +const void * RTTI_ExtraMapMarker = (void *)0x036D3020; +const void * RTTI_ExtraAction = (void *)0x036D3048; +const void * RTTI_ExtraShouldWear = (void *)0x036D3070; +const void * RTTI_ExtraSeed = (void *)0x036D3098; +const void * RTTI_ExtraPackage = (void *)0x036D30B8; +const void * RTTI_ExtraPlayerCrimeList = (void *)0x036D30E0; +const void * RTTI_ExtraRunOncePacks = (void *)0x036D3110; +const void * RTTI_ExtraDistantData = (void *)0x036D3138; +const void * RTTI_ExtraEnableStateParent = (void *)0x036D3160; +const void * RTTI_ExtraEnableStateChildren = (void *)0x036D3190; +const void * RTTI_ExtraLinkedRef = (void *)0x036D31C0; +const void * RTTI_ExtraLinkedRefChildren = (void *)0x036D31E8; +const void * RTTI_ExtraLocationRefType = (void *)0x036D3218; +const void * RTTI_ExtraActivateRef = (void *)0x036D3248; +const void * RTTI_ExtraActivateRefChildren = (void *)0x036D3270; +const void * RTTI_ExtraRandomTeleportMarker = (void *)0x036D32A0; +const void * RTTI_ExtraCannotWear = (void *)0x036D32D0; +const void * RTTI_ExtraLastFinishedSequence = (void *)0x036D32F8; +const void * RTTI_ExtraGhost = (void *)0x036D3328; +const void * RTTI_ExtraItemDropper = (void *)0x036D3350; +const void * RTTI_ExtraDroppedItemList = (void *)0x036D3378; +const void * RTTI_ExtraSavedAnimation = (void *)0x036D33A8; +const void * RTTI_ExtraSavedHavokData = (void *)0x036D33D8; +const void * RTTI_ExtraRefractionProperty = (void *)0x036D3408; +const void * RTTI_ExtraSound = (void *)0x036D3438; +const void * RTTI_ExtraCreatureMovementSound = (void *)0x036D3460; +const void * RTTI_ExtraActivateLoopSound = (void *)0x036D3498; +const void * RTTI_ExtraFactionChanges = (void *)0x036D34C8; +const void * RTTI_ExtraSayTopicInfoOnceADay = (void *)0x036D34F8; +const void * RTTI_ExtraEncounterZone = (void *)0x036D3528; +const void * RTTI_ExtraRoomRefData = (void *)0x036D3558; +const void * RTTI_ExtraPatrolRefInUseData = (void *)0x036D3580; +const void * RTTI_ExtraFollowerSwimBreadcrumbs = (void *)0x036D35B0; +const void * RTTI_ExtraAliasInstanceArray = (void *)0x036D35E8; +const void * RTTI_ExtraDecalGroup = (void *)0x036D3618; +const void * RTTI_ExtraCanTalkToPlayer = (void *)0x036D3640; +const void * RTTI_ExtraSayToTopicInfo = (void *)0x036D3670; +const void * RTTI_ExtraInteraction = (void *)0x036D36A0; +const void * RTTI_ExtraLockList = (void *)0x036D36C8; +const void * RTTI_ExtraSoul = (void *)0x036D36F0; +const void * RTTI_ExtraPackageStartLocation = (void *)0x036D3710; +const void * RTTI_ExtraMasterFileCell = (void *)0x036D3740; +const void * RTTI_ExtraMasterLocation = (void *)0x036D3770; +const void * RTTI_ExtraPersistentCell = (void *)0x036D37A0; +const void * RTTI_ExtraRagDollData = (void *)0x036D37D0; +const void * RTTI_ExtraUsedMarkers = (void *)0x036D37F8; +const void * RTTI_ExtraReservedMarkers = (void *)0x036D3820; +const void * RTTI_ExtraAshPileRef = (void *)0x036D3850; +const void * RTTI_ExtraLeveledItemBase = (void *)0x036D3878; +const void * RTTI_ExtraLevCreaModifier = (void *)0x036D38A8; +const void * RTTI_ExtraSpawnContainer = (void *)0x036D38D8; +const void * RTTI_ExtraAcousticParent = (void *)0x036D3908; +const void * RTTI_ExtraEmittanceSource = (void *)0x036D3938; +const void * RTTI_ExtraMultiBoundData = (void *)0x036D3968; +const void * RTTI_ExtraMultiBoundRef = (void *)0x036D3998; +const void * RTTI_ExtraMultiBound = (void *)0x036D39C8; +const void * RTTI_ExtraPortal = (void *)0x036D39F0; +const void * RTTI_ExtraRoom = (void *)0x036D3A18; +const void * RTTI_ExtraOcclusionShape = (void *)0x036D3A38; +const void * RTTI_ExtraFriendHits = (void *)0x036D3A68; +const void * RTTI_ExtraStartingWorldOrCell = (void *)0x036D3A90; +const void * RTTI_ExtraEditorRef3DData = (void *)0x036D3AC0; +const void * RTTI_ExtraEditorRefMoveData = (void *)0x036D3AF0; +const void * RTTI_ExtraGuardedRefData = (void *)0x036D3B20; +const void * RTTI_ExtraIgnoredBySandbox = (void *)0x036D3B50; +const void * RTTI_ExtraPromotedRef = (void *)0x036D3B80; +const void * RTTI_ExtraAnimationSequencer = (void *)0x036D3BA8; +const void * RTTI_ExtraRefrPath = (void *)0x036D3BD8; +const void * RTTI_ExtraCellGrassData = (void *)0x036D3C00; +const void * RTTI_ExtraWaterData = (void *)0x036D3C30; +const void * RTTI_ExtraGroupConstraint = (void *)0x036D3C58; +const void * RTTI_ExtraScriptedAnimDependence = (void *)0x036D3C88; +const void * RTTI_ExtraRaceData = (void *)0x036D3CC0; +const void * RTTI_ExtraMagicCaster = (void *)0x036D26D8; +const void * RTTI_ExtraBendableSplineParams = (void *)0x036D3CE8; +const void * RTTI_ExtraInstanceData = (void *)0x036D3D18; +const void * RTTI_ExtraPowerArmor = (void *)0x036D3D40; +const void * RTTI_ExtraPowerArmorPreload = (void *)0x036D3D68; +const void * RTTI_ExtraInputEnableLayer = (void *)0x036D3D98; +const void * RTTI_ExtraDirectAtTarget = (void *)0x036D3DC8; +const void * RTTI_ExtraRefWeaponSounds = (void *)0x036D3DF8; +const void * RTTI_ExtraAnimGraphPreload = (void *)0x036D3E88; +const void * RTTI_ExtraFurnitureEntryData = (void *)0x036D3F28; +const void * RTTI_BSTextureDB__QueuedHandlesWithCallback = (void *)0x036D4108; +const void * RTTI_QueuedActor = (void *)0x036D4478; +const void * RTTI_QueuedCharacter = (void *)0x036D44F0; +const void * RTTI_QueuedHead = (void *)0x036D4518; +const void * RTTI_AttachDistant3DTask = (void *)0x036D4570; +const void * RTTI_QueuedModel = (void *)0x036D44C8; +const void * RTTI_QueuedReference = (void *)0x036D44A0; +const void * RTTI_QueuedAnimationObject = (void *)0x036D45A0; +const void * RTTI_BackgroundProcessThread = (void *)0x036D45D0; +const void * RTTI_Scaleform__SysAllocBase = (void *)0x036D4648; +const void * RTTI_Scaleform__SysAlloc = (void *)0x036D4678; +const void * RTTI_Scaleform__SysAllocMalloc = (void *)0x036D46A8; +const void * RTTI_Scaleform__AcquireInterface = (void *)0x036D4748; +const void * RTTI_QueuedTree = (void *)0x036D4780; +const void * RTTI_QueuedPlayer = (void *)0x036D47A8; +const void * RTTI_INIPrefSettingCollection = (void *)0x036D4E80; +const void * RTTI_TES = (void *)0x036D5020; +const void * RTTI_AddCellGrassTask = (void *)0x036D5080; +const void * RTTI_BGSLoadFormBuffer = (void *)0x036D5510; +const void * RTTI_BGSStoryManagerBranchNode = (void *)0x036D5680; +const void * RTTI_NEW_REFR_DATA = (void *)0x036D5850; +const void * RTTI_TESDataHandler__ArchiveRegistrationListener = (void *)0x036D5880; +const void * RTTI_TESDataHandler__PSGRegistrationListener = (void *)0x036D58C8; +const void * RTTI_BGSStoryManagerTreeForm = (void *)0x036D56E0; +const void * RTTI_BGSStoryManagerNodeBase = (void *)0x036D56B0; +const void * RTTI_BSModelDB__BSModelProcessor = (void *)0x036D5AE0; +const void * RTTI_BGSAttackDataForm = (void *)0x036D5C40; +const void * RTTI_BGSBipedObjectForm = (void *)0x036D5CA0; +const void * RTTI_BGSBlockBashData = (void *)0x036D5CD0; +const void * RTTI_BGSDestructibleObjectForm = (void *)0x036CEDE8; +const void * RTTI_BGSEquipType = (void *)0x036CEDC0; +const void * RTTI_BGSIdleCollection = (void *)0x036D5EF8; +const void * RTTI_IKeywordFormBase = (void *)0x036CEC18; +const void * RTTI_BGSKeywordForm = (void *)0x036CED00; +const void * RTTI_BGSMenuDisplayObject = (void *)0x036CFBF8; +const void * RTTI_BGSMessageIcon = (void *)0x036CED70; +const void * RTTI_BGSOverridePackCollection = (void *)0x036D6070; +const void * RTTI_PerkRankVisitor = (void *)0x036D60C8; +const void * RTTI_BGSPerkRankArray = (void *)0x036D60A0; +const void * RTTI_BGSPickupPutdownSounds = (void *)0x036CEE18; +const void * RTTI_BGSSkinForm = (void *)0x036D6130; +const void * RTTI_TESActorBaseData = (void *)0x036D61A0; +const void * RTTI_TESAIForm = (void *)0x036D6180; +const void * RTTI_TESAttackDamageForm = (void *)0x036D61C8; +const void * RTTI_TESModelRDT = (void *)0x036D66E0; +const void * RTTI_TESBipedModelForm = (void *)0x036D4320; +const void * RTTI_TESContainer = (void *)0x036D6758; +const void * RTTI_TESDescription = (void *)0x036CEE48; +const void * RTTI_TESEnchantableForm = (void *)0x036D61F8; +const void * RTTI_TESForm = (void *)0x036CB140; +const void * RTTI_IFormFactory__IFactoryVisitor = (void *)0x036D7770; +const void * RTTI_BSStorage = (void *)0x036D77A8; +const void * RTTI_BSMemStorage = (void *)0x036D77C8; +const void * RTTI_BSVMLoadTask = (void *)0x036D7860; +const void * RTTI_TESHealthForm = (void *)0x036D6228; +const void * RTTI_TESIcon = (void *)0x036CED28; +const void * RTTI_TESImageSpaceModifiableForm = (void *)0x036D6318; +const void * RTTI_TESLeveledList = (void *)0x036D6250; +const void * RTTI_TESModel = (void *)0x036CE358; +const void * RTTI_TESProduceForm = (void *)0x036D6278; +const void * RTTI_TESRaceForm = (void *)0x036D62A0; +const void * RTTI_TESReactionForm = (void *)0x036D62F0; +const void * RTTI_TESSpellList = (void *)0x036D62C8; +const void * RTTI_TESTexture = (void *)0x036CED48; +const void * RTTI_TESValueForm = (void *)0x036CFEB0; +const void * RTTI_TESWeightForm = (void *)0x036CED98; +const void * RTTI_ActionInput = (void *)0x036CBEA0; +const void * RTTI_BSAttachTechniques__BSAttachTechnique = (void *)0x036D8058; +const void * RTTI_BGSAttachTechniquesUtil__ProcessTechniquesFunctor = (void *)0x036D80A0; +const void * RTTI_BGSAttachTechniquesUtil__AttachTechniquesFunctor = (void *)0x036D80F0; +const void * RTTI_BGSAttachTechniquesUtil__DetachTechniquesFunctor = (void *)0x036D8140; +const void * RTTI_BGSParticleArrayAttach = (void *)0x036D8188; +const void * RTTI_BGSParticleArrayAttach__EmitterPolicy = (void *)0x036D81B8; +const void * RTTI_BGSParticleArrayAttach__CollectEmitterPolicy = (void *)0x036D8200; +const void * RTTI_BGSParticleArrayAttach__ClearEmitterPolicy = (void *)0x036D8250; +const void * RTTI_BGSHavokGeometryAttach = (void *)0x036D8290; +const void * RTTI_BGSHavokGeometryAttach__ActionPolicy = (void *)0x036D82C0; +const void * RTTI_BGSHavokGeometryAttach__AttachPolicy = (void *)0x036D8300; +const void * RTTI_BGSHavokGeometryAttach__DetachPolicy = (void *)0x036D8340; +const void * RTTI_BGSNamedNodeAttach = (void *)0x036D8380; +const void * RTTI_BGSNamedNodeAttach__ActionPolicy = (void *)0x036D83B0; +const void * RTTI_BGSNamedNodeAttach__AttachPolicy = (void *)0x036D83E8; +const void * RTTI_BGSNamedNodeAttach__DetachPolicy = (void *)0x036D8420; +const void * RTTI_BGSMultiTechniqueAttach = (void *)0x036D8458; +const void * RTTI_BGSMultiTechniqueAttach__ActionPolicy = (void *)0x036D8488; +const void * RTTI_BGSMultiTechniqueAttach__AttachPolicy = (void *)0x036D84C8; +const void * RTTI_BGSMultiTechniqueAttach__DetachPolicy = (void *)0x036D8508; +const void * RTTI_ITempEffectFactory = (void *)0x036D8788; +const void * RTTI_BSMultiBoundAABB = (void *)0x036D8870; +const void * RTTI_BSMultiBoundOBB = (void *)0x036D88C0; +const void * RTTI_BGSDecalNode = (void *)0x036D8A18; +const void * RTTI_QueuedPromoteReferencesTask = (void *)0x036D8A98; +const void * RTTI_BSResource__Location = (void *)0x036D8C40; +const void * RTTI_BSResource__LooseFileLocation = (void *)0x036D8C70; +const void * RTTI_BGSQueuedGrassModelHandles = (void *)0x036D8D08; +const void * RTTI_hknpShape = (void *)0x036CE9A8; +const void * RTTI_hknpConvexShape = (void *)0x036D8DC0; +const void * RTTI_hknpSphereShape = (void *)0x036D8DE8; +const void * RTTI_IRaceSubGraphDataFunctor = (void *)0x036D8E10; +const void * RTTI_InteractionCombatDataCollectFunctor = (void *)0x036D8E88; +const void * RTTI_BGSInventoryItem__StackDataCompareFunctor = (void *)0x036D8ED0; +const void * RTTI_BGSInventoryItem__CheckStackIDFunctor = (void *)0x036D8F10; +const void * RTTI_BGSInventoryItem__CheckExtraDataFunctor = (void *)0x036D8F50; +const void * RTTI_BGSInventoryItem__StackDataWriteFunctor = (void *)0x036D8F90; +const void * RTTI_BGSInventoryItem__ModCountFunctor = (void *)0x036D8FD0; +const void * RTTI_SetPlayerHasTakenStackFunctor = (void *)0x036D9008; +const void * RTTI_RemoveExtraFunctor = (void *)0x036D9040; +const void * RTTI_BGSInventoryItem__Stack = (void *)0x036D90B0; +const void * RTTI_BGSInventoryItem__FindEquippedStackFunctor = (void *)0x036D90E0; +const void * RTTI_BGSInventoryItem__IsUIEquivalentStackFunctor = (void *)0x036D9120; +const void * RTTI_BGSInventoryItem__SetFlagFunctor = (void *)0x036D9168; +const void * RTTI_MatchEquippedAndFavoriteDataFunctor = (void *)0x036D91A0; +const void * RTTI_IsFavoriteFunctor = (void *)0x036D91E0; +const void * RTTI_SetFavoriteFunctor = (void *)0x036D9208; +const void * RTTI_RemoveFavoriteFunctor = (void *)0x036D9238; +const void * RTTI_SetPoisonFunctor = (void *)0x036D9268; +const void * RTTI_SetEnchantmentFunctor = (void *)0x036D9290; +const void * RTTI_BGSInventoryItem__HasExtraDataFunctor = (void *)0x036D9300; +const void * RTTI_RefMatchFunctor = (void *)0x036D9340; +const void * RTTI_RemoveQuestAliasFunctor = (void *)0x036D9368; +const void * RTTI_SetTimeLeftFunctor = (void *)0x036D9398; +const void * RTTI_ModTimeLeftFunctor = (void *)0x036D93C8; +const void * RTTI_ProcessWeaponStrikeMagicFunctor = (void *)0x036D93F8; +const void * RTTI_SetItemOwnershipFunctor = (void *)0x036D9430; +const void * RTTI_SetItemOriginalReferenceFunctor = (void *)0x036D9460; +const void * RTTI_BSConnectPoint__ChildOrigin = (void *)0x036D94D0; +const void * RTTI_BGSObjectVisibilityManager = (void *)0x036D9620; +const void * RTTI_BSPortal = (void *)0x036D96C8; +const void * RTTI_BGSPrimitiveLine = (void *)0x036D9738; +const void * RTTI_BGSPrimitivePlane = (void *)0x036D97B0; +const void * RTTI_BGSPrimitiveBox = (void *)0x036D9760; +const void * RTTI_BGSPrimitiveSphere = (void *)0x036D97D8; +const void * RTTI_BGSPrimitiveEllipsoid = (void *)0x036D9808; +const void * RTTI_BGSPrimitive = (void *)0x036D9788; +const void * RTTI_BSMultiBoundSphere = (void *)0x036D9838; +const void * RTTI_ReferenceEffectController = (void *)0x036D9930; +const void * RTTI_OwnedController = (void *)0x036D9960; +const void * RTTI_BSAttachRefController = (void *)0x036D99A0; +const void * RTTI_BSCloneReserver = (void *)0x036D99D0; +const void * RTTI_NiProperty = (void *)0x036D9AC0; +const void * RTTI_NiAlphaProperty = (void *)0x036D9AE8; +const void * RTTI_GridArray = (void *)0x036D9B28; +const void * RTTI_GridCellArray = (void *)0x036D9BB8; +const void * RTTI_BSExternalAudioIO__ExternalIOInterface = (void *)0x036DA158; +const void * RTTI_LipSynchAnimDB__LipAudioInterface = (void *)0x036DA530; +const void * RTTI_BSInputEventUser = (void *)0x036DA608; +const void * RTTI_TESCameraState = (void *)0x036DA630; +const void * RTTI_LocalMapCamera = (void *)0x036DA658; +const void * RTTI_LocalMapCamera__DefaultState = (void *)0x036DA6A0; +const void * RTTI_BSSplatterExtraData = (void *)0x036DA760; +const void * RTTI_hknpConvexPolytopeShape = (void *)0x036DC348; +const void * RTTI_hknpCapsuleShape = (void *)0x036DC378; +const void * RTTI_IMessageBoxCallback = (void *)0x036DC800; +const void * RTTI_ExamineConfirmMenu__ICallback = (void *)0x036DC830; +const void * RTTI_hknpUniqueBodyIdHitCollector = (void *)0x036DC868; +const void * RTTI_hknpAnyHitCollector = (void *)0x036DC8A0; +const void * RTTI_hknpFlippedShapeCastQueryCollector = (void *)0x036DC8D0; +const void * RTTI_Workshop__hknpWorkshopCastCollector = (void *)0x036DCA30; +const void * RTTI_Workshop__CheckBuildRadiusCollector = (void *)0x036DCA70; +const void * RTTI_Workshop__hknpCheckShelterHitsCollector = (void *)0x036DCAB0; +const void * RTTI_Workshop__RequestScrapCallback = (void *)0x036DCAF0; +const void * RTTI_Workshop__RequestStoreCallback = (void *)0x036DCB28; +const void * RTTI_Workshop__RequestRepairCallback = (void *)0x036DCB60; +const void * RTTI_Workshop__hknpWorkshopIntersectCollector = (void *)0x036DCB98; +const void * RTTI_Workshop__hknpClosestConnectPointCollector = (void *)0x036DCBE0; +const void * RTTI_Workshop__hknpCountIntersectingRefsCollector = (void *)0x036DCC20; +const void * RTTI_Workshop__ForwardCastCollector = (void *)0x036DCC68; +const void * RTTI_Workshop__GroundIntersectCollector = (void *)0x036DCCA0; +const void * RTTI_Workshop__MainStatusHandler = (void *)0x036DCCD8; +const void * RTTI_Workshop__WorkshopReferenceEventSink = (void *)0x036DCD10; +const void * RTTI_std__error_category = (void *)0x036DDE98; +const void * RTTI_std___Generic_error_category = (void *)0x036DDEC8; +const void * RTTI_std___Iostream_error_category = (void *)0x036DDF00; +const void * RTTI_NVFlex__DebrisInstanceManager = (void *)0x036DE028; +const void * RTTI_NVFlex__DebrisNode = (void *)0x036DE238; +const void * RTTI_NVFlex__DebrisRenderer = (void *)0x036DE2C0; +const void * RTTI_NVFlex__FlexThread = (void *)0x036DE378; +const void * RTTI_hkGeometry = (void *)0x036DE590; +const void * RTTI_MovementHandlerAgentAnimationDriven = (void *)0x036DE620; +const void * RTTI_MovementHandlerAgentGraphDrivenAnimationDriven = (void *)0x036DE990; +const void * RTTI_IPipelineStageInterface = (void *)0x036DE5F0; +const void * RTTI_IMovementTweenerAgent = (void *)0x036DED58; +const void * RTTI_IMovementInterface = (void *)0x036DED28; +const void * RTTI_IMovementQueryDesiredDeltas = (void *)0x036DED88; +const void * RTTI_IMovementSetPlayerControls = (void *)0x036DEDC0; +const void * RTTI_MovementHandlerAgentPlayerControls = (void *)0x036DEDF8; +const void * RTTI_NavMeshObstacleCoverManager = (void *)0x036DF0F8; +const void * RTTI_BSPathingStreamRead = (void *)0x036DF180; +const void * RTTI_PathingStreamMasterFileRead = (void *)0x036DF1B0; +const void * RTTI_QuestPathingRequest = (void *)0x036DF228; +const void * RTTI_IPathBuilderFactoryBase = (void *)0x036DF280; +const void * RTTI_TESRegion = (void *)0x036DF600; +const void * RTTI_TESRegionData = (void *)0x036DF5A8; +const void * RTTI_TESRegionDataGrass = (void *)0x036DF660; +const void * RTTI_TESRegionDataLandscape = (void *)0x036DF690; +const void * RTTI_TESRegionDataManager = (void *)0x036DF6C0; +const void * RTTI_TESRegionDataMap = (void *)0x036DF6F0; +const void * RTTI_TESRegionDataObjects = (void *)0x036DF720; +const void * RTTI_TESRegionDataSound = (void *)0x036DF750; +const void * RTTI_TESRegionDataWeather = (void *)0x036DF5D0; +const void * RTTI_TESRegionObjectBase = (void *)0x036DF788; +const void * RTTI_TESRegionGrassObject = (void *)0x036DF7B8; +const void * RTTI_TESRegionGrassObjectList = (void *)0x036DF7E8; +const void * RTTI_TESRegionList = (void *)0x036DF860; +const void * RTTI_TESRegionNoiseFunction = (void *)0x036DF8C0; +const void * RTTI_TESRegionObject = (void *)0x036DF8F8; +const void * RTTI_TESRegionObjectList = (void *)0x036DF950; +const void * RTTI_BGSAttractionRule = (void *)0x036DFC08; +const void * RTTI_BGSCharacterTint__Entry = (void *)0x036DFE50; +const void * RTTI_BGSCharacterTint__MaskEntry = (void *)0x036DFE80; +const void * RTTI_BGSCharacterTint__PaletteEntry = (void *)0x036DFEB8; +const void * RTTI_BGSCharacterTint__Template__Entry = (void *)0x036DFEF0; +const void * RTTI_BGSCharacterTint__Template__Mask = (void *)0x036DFF28; +const void * RTTI_BGSCharacterTint__Template__Palette = (void *)0x036DFF60; +const void * RTTI_BGSCharacterTint__TextureSetEntry = (void *)0x036DFF98; +const void * RTTI_BGSCharacterTint__Template__TextureSet = (void *)0x036DFFD0; +const void * RTTI_BGSRefCollectionAlias = (void *)0x036E0038; +const void * RTTI_BSShaderMaterialExtraData = (void *)0x036E0128; +const void * RTTI_BSDismembermentLimbExtraData = (void *)0x036E01B0; +const void * RTTI_BSDismembermentExtraData = (void *)0x036E01E8; +const void * RTTI_BGSSceneAction = (void *)0x036E0298; +const void * RTTI_BGSSceneActionConversationBase = (void *)0x036E02C0; +const void * RTTI_BGSSceneActionPlayerDialogue = (void *)0x036E03B0; +const void * RTTI_BGSSceneActionStartScene = (void *)0x036E0490; +const void * RTTI_BSIAudioEffectChain = (void *)0x036E0558; +const void * RTTI_BGSAudioEffectChain = (void *)0x036E0588; +const void * RTTI_BSISoundDescriptor = (void *)0x036E06C0; +const void * RTTI_BGSSoundDescriptor = (void *)0x036E06F0; +const void * RTTI_BGSAutoWeaponSoundDef = (void *)0x036E0720; +const void * RTTI_BGSComponent = (void *)0x036E0960; +const void * RTTI_BGSCompoundSoundDef = (void *)0x036E0A88; +const void * RTTI_BGSDamageType = (void *)0x036E0C58; +const void * RTTI_BGSDefaultObject = (void *)0x036E0D20; +const void * RTTI_BGSInstanceNamingRules = (void *)0x036E0DC0; +const void * RTTI_BGSMaterialSwap = (void *)0x036CE330; +const void * RTTI_BGSSoundCategorySnapshot = (void *)0x036E0F80; +const void * RTTI_BGSSoundKeywordMapping = (void *)0x036E1090; +const void * RTTI_BGSSoundTagSet = (void *)0x036CE490; +const void * RTTI_BGSTransform = (void *)0x036CE3D8; +const void * RTTI_BGSAddonNode = (void *)0x036D53D0; +const void * RTTI_BGSAddonNodeSoundHandleExtra = (void *)0x036E1328; +const void * RTTI_BGSAimModel = (void *)0x036E1408; +const void * RTTI_BGSArtObject = (void *)0x036CEF88; +const void * RTTI_BGSArtObjectCloneTask = (void *)0x036E14D0; +const void * RTTI_BGSBendableSpline = (void *)0x036E16B8; +const void * RTTI_BGSConstructibleObject = (void *)0x036E1858; +const void * RTTI_BGSDebris = (void *)0x036D5D40; +const void * RTTI_BGSPreloadable = (void *)0x036E1950; +const void * RTTI_BGSExplosion = (void *)0x036CEF38; +const void * RTTI_BGSHazard = (void *)0x036E1AF8; +const void * RTTI_BGSLensFlare = (void *)0x036E1BC0; +const void * RTTI_BSLensFlareSpriteRenderData = (void *)0x036E1CB8; +const void * RTTI_BGSLensFlareSprite = (void *)0x036E1CF0; +const void * RTTI_BGSMovableStatic = (void *)0x036E1E18; +const void * RTTI_BGSMod__Attachment__Mod = (void *)0x036CE110; +const void * RTTI_BGSOutfit = (void *)0x036E2268; +const void * RTTI_BGSPackIn = (void *)0x036E2330; +const void * RTTI_BGSProjectile = (void *)0x036CEF10; +const void * RTTI_BGSStaticCollection = (void *)0x036E25E8; +const void * RTTI_BGSStaticCollection__RootFacade = (void *)0x036E2618; +const void * RTTI_BGSTalkingActivator = (void *)0x036E2888; +const void * RTTI_BGSTerminal = (void *)0x036CE3B0; +const void * RTTI_BGSZoomData = (void *)0x036E2B50; +const void * RTTI_TESAmmo = (void *)0x036E2C80; +const void * RTTI_TESCombatStyle = (void *)0x036D1528; +const void * RTTI_TESEffectShader = (void *)0x036CEF60; +const void * RTTI_TESFlora = (void *)0x036E3238; +const void * RTTI_BGSMod__Property__BridgeI = (void *)0x036E3420; +const void * RTTI_TESFurniture = (void *)0x036E2A88; +const void * RTTI_TESGrass = (void *)0x036E3608; +const void * RTTI_TESKey = (void *)0x036D12F8; +const void * RTTI_TESLevCharacter = (void *)0x036E37C0; +const void * RTTI_TESLevItem = (void *)0x036D64D0; +const void * RTTI_TESLevSpell = (void *)0x036D7ED8; +const void * RTTI_BGSOpenCloseForm = (void *)0x036E2860; +const void * RTTI_TESObjectLIGH = (void *)0x036CFB58; +const void * RTTI_TESBoundObject = (void *)0x036CECB8; +const void * RTTI_TBO_InstanceData = (void *)0x036E3D08; +const void * RTTI_TESObject = (void *)0x036CECE0; +const void * RTTI_TESBoundAnimObject = (void *)0x036E28B8; +const void * RTTI_TESObjectACTI = (void *)0x036D5440; +const void * RTTI_TESObjectANIO = (void *)0x036E3F28; +const void * RTTI_TESObjectARMA = (void *)0x036E4068; +const void * RTTI_TESObjectARMO = (void *)0x036D6158; +const void * RTTI_TESObjectARMO__InstanceData = (void *)0x036E4560; +const void * RTTI_TESObjectBOOK = (void *)0x036E4750; +const void * RTTI_TESObjectCONT = (void *)0x036E48B0; +const void * RTTI_TESObjectDOOR = (void *)0x036D54B8; +const void * RTTI_IFadeDoneCallback = (void *)0x036E4A90; +const void * RTTI_TESObjectMISC = (void *)0x036D5490; +const void * RTTI_TESObjectSTAT = (void *)0x036D5468; +const void * RTTI_TESObjectTREE = (void *)0x036E5420; +const void * RTTI_TESObjectWEAP = (void *)0x036D53F8; +const void * RTTI_TESObjectWEAP__InstanceData = (void *)0x036E76C0; +const void * RTTI_TESObjectWEAP__Data = (void *)0x036E76F8; +const void * RTTI_TESSoulGem = (void *)0x036E79D0; +const void * RTTI_BSStaticTriShapeDB__IBatchRequestIter = (void *)0x036E7AB8; +const void * RTTI_BGSCombinedCellGeometryDB__LoadTask = (void *)0x036E7AF8; +const void * RTTI_BGSCombinedCellGeometryDB__ModelDB__QueuedHandles = (void *)0x036E7B40; +const void * RTTI_BGSCombinedCellGeometryDB__CDX = (void *)0x036E8468; +const void * RTTI_BGSCombinedCellGeometryDB__CDX__PsgBatchIter = (void *)0x036E86B0; +const void * RTTI_BGSCombinedCellGeometryDB__PreloadTask = (void *)0x036E86F8; +const void * RTTI_BGSCombinedCellGeometryDB__Unpacker__Thread = (void *)0x036E8930; +const void * RTTI_BGSEncounterZone = (void *)0x036D13B8; +const void * RTTI_BGSGodRays = (void *)0x036D15B8; +const void * RTTI_QueuedPromoteLargeReferencesTask = (void *)0x036E8B48; +const void * RTTI_BGSLightingTemplate = (void *)0x036E8B90; +const void * RTTI_BGSLocation = (void *)0x036D13F0; +const void * RTTI_QueuedPromoteLocationReferencesTask = (void *)0x036E8DC8; +const void * RTTI_BGSLocationRefType = (void *)0x036CE060; +const void * RTTI_BGSReferenceEffect = (void *)0x036CFB80; +const void * RTTI_BGSReferenceGroup = (void *)0x036E9028; +const void * RTTI_BSParticleShaderEmitter = (void *)0x036E9110; +const void * RTTI_BSParticleShaderCubeEmitter = (void *)0x036E9140; +const void * RTTI_BSParticleShaderSnowEmitter = (void *)0x036E9178; +const void * RTTI_BSParticleShaderRainEmitter = (void *)0x036E91B0; +const void * RTTI_BGSShaderParticleGeometryData = (void *)0x036E91E8; +const void * RTTI_CellLoaderTask = (void *)0x036E92F8; +const void * RTTI_ImageSpaceModifierInstanceForm = (void *)0x036E9320; +const void * RTTI_ImageSpaceModifierInstance = (void *)0x036E9358; +const void * RTTI_ImageSpaceModifierInstanceTemp = (void *)0x036E9390; +const void * RTTI_ImageSpaceModifierInstanceDOF = (void *)0x036E93C8; +const void * RTTI_ImageSpaceModifierInstanceRB = (void *)0x036E9400; +const void * RTTI_TESClimate = (void *)0x036E9470; +const void * RTTI_TESImageSpace = (void *)0x036D1318; +const void * RTTI_NiFloatData = (void *)0x036E9618; +const void * RTTI_NiColorData = (void *)0x036E9640; +const void * RTTI_TESImageSpaceModifier = (void *)0x036CFB18; +const void * RTTI_TESChildCell = (void *)0x036E9968; +const void * RTTI_TESObjectLAND = (void *)0x036E9940; +const void * RTTI_hknpBSMaterialProperties = (void *)0x036E9990; +const void * RTTI_TESLandTexture = (void *)0x036DF928; +const void * RTTI_hkSlot = (void *)0x036E9D48; +const void * RTTI_TESObjectCELL = (void *)0x036CB160; +const void * RTTI_TESObjectCELL__IDecalRefFunctor = (void *)0x036E9E28; +const void * RTTI_BSHandleRefObject = (void *)0x036EA430; +const void * RTTI_TESObjectREFR = (void *)0x036CB328; +const void * RTTI_ActorValueOwner = (void *)0x036EA568; +const void * RTTI_BGSInventoryItem__ClearEquipFlagsFunctor = (void *)0x036EA5E8; +const void * RTTI_BGSInventoryList__InventoryWeightFunctor = (void *)0x036EA628; +const void * RTTI_TESObjectREFR__HandleOutfitItemsFunctor = (void *)0x036EA668; +const void * RTTI_OwnedCameraEffectController = (void *)0x036EA6A8; +const void * RTTI_BSInputEnableManager__IDebugNameFunctor = (void *)0x036EA6E0; +const void * RTTI_RemoveOutfitItemsFunctor = (void *)0x036EA978; +const void * RTTI_CalcContainerWeight = (void *)0x036EA9A8; +const void * RTTI_InventoryScoringFunctor = (void *)0x036EA9D8; +const void * RTTI_GetBestWeaponFunctor = (void *)0x036EAA08; +const void * RTTI_GetBestAmmoFunctor = (void *)0x036EAA38; +const void * RTTI_GetBestArmorFunctor = (void *)0x036EAA68; +const void * RTTI_GetBestLightFunctor = (void *)0x036EAA98; +const void * RTTI_IAnimationStreamWrite = (void *)0x036EAC08; +const void * RTTI_IAnimationStreamRead = (void *)0x036EAC38; +const void * RTTI_BSAnimGraphVisit__BShkbVisitor = (void *)0x036EAC68; +const void * RTTI_AnimationStreamLoadGame = (void *)0x036EACA0; +const void * RTTI_AnimationStreamSaveGame = (void *)0x036EACD0; +const void * RTTI_REFREventCallbacks__IEventCallback = (void *)0x036EAD80; +const void * RTTI_REFREventCallbacks__IEventCallbackFactory = (void *)0x036EADB8; +const void * RTTI_TESObjectREFRSync__REFRSyncController = (void *)0x036EAEB8; +const void * RTTI_TESWaterForm = (void *)0x036D1368; +const void * RTTI_TESTexture1024 = (void *)0x036EB080; +const void * RTTI_TESWeather = (void *)0x036E9448; +const void * RTTI_TESWorldSpace = (void *)0x036CB250; +const void * RTTI_BGSAcousticSpace = (void *)0x036D1340; +const void * RTTI_NiFormArray = (void *)0x036EB710; +const void * RTTI_BGSCameraPath = (void *)0x036EB6C0; +const void * RTTI_BGSCameraShot = (void *)0x036EB6E8; +const void * RTTI_BGSCollisionLayer = (void *)0x036E24B8; +const void * RTTI_BGSColorForm = (void *)0x036DFE28; +const void * RTTI_BGSDefaultObjectManager = (void *)0x036EED90; +const void * RTTI_BGSFootstep = (void *)0x036EEE30; +const void * RTTI_BGSFootstepSet = (void *)0x036EEF00; +const void * RTTI_BGSImpactData = (void *)0x036EF000; +const void * RTTI_BGSImpactDataSet = (void *)0x036CEFB0; +const void * RTTI_BGSListForm = (void *)0x036CFD00; +const void * RTTI_BSMaterialObject = (void *)0x036EF240; +const void * RTTI_BGSMaterialObject = (void *)0x036EF268; +const void * RTTI_BGSMaterialType = (void *)0x036EAFA0; +const void * RTTI_BGSMenuIcon = (void *)0x036EF438; +const void * RTTI_BGSMessage = (void *)0x036CE038; +const void * RTTI_BGSMusicPaletteTrack = (void *)0x036EF5F8; +const void * RTTI_BGSMusicSilenceTrack = (void *)0x036EF800; +const void * RTTI_BGSMusicSingleTrack = (void *)0x036EFA18; +const void * RTTI_BSIMusicTrack = (void *)0x036EF5D0; +const void * RTTI_BGSMusicTrack = (void *)0x036EF628; +const void * RTTI_BGSMusicTrackFormWrapper = (void *)0x036EFC10; +const void * RTTI_BSIMusicType = (void *)0x036EFD00; +const void * RTTI_BGSMusicType = (void *)0x036D1390; +const void * RTTI_BSIReverbType = (void *)0x036EFE08; +const void * RTTI_BGSReverbParameters = (void *)0x036EB5B8; +const void * RTTI_BSISoundCategory = (void *)0x036EFEE8; +const void * RTTI_BGSSoundCategory = (void *)0x036D14D8; +const void * RTTI_BGSSoundDescriptorForm = (void *)0x036E0428; +const void * RTTI_BSISoundOutputModel = (void *)0x036F0100; +const void * RTTI_BSISoundOutputModel__BSIAttenuationCharacteristics = (void *)0x036F0130; +const void * RTTI_BGSSoundOutput = (void *)0x036D14B0; +const void * RTTI_BGSSoundOutput__DynamicAttenuationCharacteristics = (void *)0x036F0180; +const void * RTTI_BSISoundDescriptor__BSIPlaybackCharacteristics = (void *)0x036F02B0; +const void * RTTI_BGSStandardSoundDef = (void *)0x036F02F8; +const void * RTTI_BGSStandardSoundDef__SoundPlaybackCharacteristics = (void *)0x036F0330; +const void * RTTI_BSShaderTextureSet = (void *)0x036F0538; +const void * RTTI_BSTextureSet = (void *)0x036F0568; +const void * RTTI_BGSTextureSet = (void *)0x036E1690; +const void * RTTI_IVisitProcedures = (void *)0x036F07E0; +const void * RTTI_MiscStatManager__IMiscStatVisitor = (void *)0x036F0808; +const void * RTTI_MiscStatManager__FindStatByCRC = (void *)0x036F0840; +const void * RTTI_TESGlobal = (void *)0x036D0CD0; +const void * RTTI_TESLoadScreen = (void *)0x036F0B00; +const void * RTTI_Script = (void *)0x036F0C50; +const void * RTTI_MovementLargeDelta__IEnumVisitor = (void *)0x036F0D38; +const void * RTTI_DefaultMessageBoxCallback = (void *)0x036F0EC0; +const void * RTTI_BSStream = (void *)0x036F11C8; +const void * RTTI_IUIMessageData = (void *)0x036F1878; +const void * RTTI_MobIterOperator = (void *)0x036F18C0; +const void * RTTI_ConsoleData = (void *)0x036F18E8; +const void * RTTI_TESShout = (void *)0x036D7F00; +const void * RTTI_TESSound = (void *)0x03711570; +const void * RTTI_TESWordOfPower = (void *)0x03711630; +const void * RTTI_BGSAction = (void *)0x03711718; +const void * RTTI_BGSAssociationType = (void *)0x037117E0; +const void * RTTI_BGSBodyPartData = (void *)0x03711A08; +const void * RTTI_BGSDialogueBranch = (void *)0x036D1488; +const void * RTTI_PerkEntryVisitor = (void *)0x037133B0; +const void * RTTI_BGSEntryPointFunctionData = (void *)0x037135F8; +const void * RTTI_BGSEntryPointFunctionDataOneValue = (void *)0x03713628; +const void * RTTI_BGSEntryPointFunctionDataTwoValue = (void *)0x03713660; +const void * RTTI_BGSEntryPointFunctionDataAVAndValue = (void *)0x03713698; +const void * RTTI_BGSEntryPointFunctionDataLeveledList = (void *)0x037136D8; +const void * RTTI_BGSEntryPointFunctionDataSpellItem = (void *)0x03713718; +const void * RTTI_BGSEntryPointFunctionDataBooleanGraphVariable = (void *)0x03713760; +const void * RTTI_BGSEntryPointFunctionDataText = (void *)0x037137A8; +const void * RTTI_BGSEntryPointFunctionDataActivateChoice = (void *)0x037137E0; +const void * RTTI_BGSEquipSlot = (void *)0x03713830; +const void * RTTI_TESModelTri = (void *)0x037139C0; +const void * RTTI_BGSHeadPart = (void *)0x03713940; +const void * RTTI_BGSIdleMarker = (void *)0x03713A90; +const void * RTTI_BGSKeyword = (void *)0x036CDF48; +const void * RTTI_BGSBaseAlias = (void *)0x036E0010; +const void * RTTI_BGSLocAlias = (void *)0x03713C30; +const void * RTTI_BGSMovementType = (void *)0x03713D00; +const void * RTTI_BGSNote = (void *)0x036E2A68; +const void * RTTI_BGSPerkEntry = (void *)0x03713F18; +const void * RTTI_BGSPerk = (void *)0x036CFBB0; +const void * RTTI_BGSQuestPerkEntry = (void *)0x03713F40; +const void * RTTI_BGSAbilityPerkEntry = (void *)0x03713F68; +const void * RTTI_BGSEntryPointPerkEntry = (void *)0x03713F98; +const void * RTTI_BGSRefAlias = (void *)0x036E0068; +const void * RTTI_BGSRelationship = (void *)0x03714568; +const void * RTTI_BGSScene = (void *)0x036D1578; +const void * RTTI_BGSSceneActionDialogue = (void *)0x03714890; +const void * RTTI_BGSSceneActionPackage = (void *)0x037148C0; +const void * RTTI_BGSSceneActionNPCResponseDialogue = (void *)0x037148F0; +const void * RTTI_BGSSceneActionRadio = (void *)0x03714928; +const void * RTTI_BGSSceneActionTimer = (void *)0x03714AD8; +const void * RTTI_BGSVoiceType = (void *)0x036D2F18; +const void * RTTI_Reset3DMobIterator = (void *)0x03714DE8; +const void * RTTI_TESClass = (void *)0x03714E28; +const void * RTTI_TESEyes = (void *)0x036D5420; +const void * RTTI_TESFaction = (void *)0x036D1500; +const void * RTTI_TESIdleForm = (void *)0x036CBC98; +const void * RTTI_TESActorBase = (void *)0x036D1550; +const void * RTTI_TESNPC = (void *)0x036D54E0; +const void * RTTI_TESNPC__InstanceData = (void *)0x03715878; +const void * RTTI_BSTransformExtra = (void *)0x037158A8; +const void * RTTI_TESQuest = (void *)0x036D1598; +const void * RTTI_QueuedPromoteQuestTask = (void *)0x03715CA8; +const void * RTTI_BGSTextureModel = (void *)0x03716090; +const void * RTTI_BGSBehaviorGraphModel = (void *)0x037160B8; +const void * RTTI_TESRace = (void *)0x036D7E80; +const void * RTTI_TESTopic = (void *)0x036D1468; +const void * RTTI_TESTopicInfo = (void *)0x036D1440; +const void * RTTI_ZoneEntry = (void *)0x03717E50; +const void * RTTI_TargetEntry = (void *)0x03717EE0; +const void * RTTI_BGSZoneTargetListener = (void *)0x03717F08; +const void * RTTI_TrapTargetEntry = (void *)0x03717F38; +const void * RTTI_FOCollisionListener = (void *)0x03717FB0; +const void * RTTI_hkStreamWriter = (void *)0x037182E8; +const void * RTTI_hknpModifier = (void *)0x03718310; +const void * RTTI_hkError = (void *)0x03718338; +const void * RTTI_hkStreamReader = (void *)0x03718388; +const void * RTTI_hkFileSystem = (void *)0x037183B0; +const void * RTTI_HavokFileStreambufReader = (void *)0x03718410; +const void * RTTI_HavokFileStreambufWriter = (void *)0x03718440; +const void * RTTI_RagdollFurnitureModifier = (void *)0x03718470; +const void * RTTI_hkWin32FileSystem = (void *)0x03718688; +const void * RTTI_HavokStreambufFactory = (void *)0x037186B0; +const void * RTTI_HavokError = (void *)0x037186E0; +const void * RTTI_LoadedAreaBound = (void *)0x037187C0; +const void * RTTI_SpecificItemCollector = (void *)0x03718820; +const void * RTTI_bhkTrapListener = (void *)0x037189B8; +const void * RTTI_TrapEntry = (void *)0x037189E0; +const void * RTTI_TESTrapListener = (void *)0x03718A00; +const void * RTTI_Atmosphere = (void *)0x03718AB8; +const void * RTTI_Clouds = (void *)0x03718B18; +const void * RTTI_NiBillboardNode = (void *)0x03718B38; +const void * RTTI_Moon = (void *)0x03718B60; +const void * RTTI_Precipitation = (void *)0x03718BA8; +const void * RTTI_Sky = (void *)0x03719018; +const void * RTTI_TESWeather__SkyStaticFunctor = (void *)0x03719038; +const void * RTTI_SkyEffectController = (void *)0x03719070; +const void * RTTI_SkyStaticFindFunctor = (void *)0x037190A0; +const void * RTTI_TempLoadGameBuffer = (void *)0x037190D0; +const void * RTTI_SkyObject = (void *)0x03718AE0; +const void * RTTI_Stars = (void *)0x03719168; +const void * RTTI_Sun = (void *)0x03719298; +const void * RTTI_BShkNonTransformController = (void *)0x03732D40; +const void * RTTI_BShkFloatController = (void *)0x03732D78; +const void * RTTI_BGShkPhonemeController = (void *)0x03732DA8; +const void * RTTI_BSFaceGenAnimationData = (void *)0x03733510; +const void * RTTI_FutBinaryFileC = (void *)0x03733540; +const void * RTTI_BSResourceFaceGenBinaryFile = (void *)0x03733568; +const void * RTTI_BSFaceGenPendingHeadData = (void *)0x036D4540; +const void * RTTI_BSFaceGenDB__TRI__QueuedHandles = (void *)0x03733F58; +const void * RTTI_BSFaceGenModel = (void *)0x03734058; +const void * RTTI_BSFaceGenModelExtraData = (void *)0x03734080; +const void * RTTI_BSFaceGenModelMap__Entry = (void *)0x037340B8; +const void * RTTI_BSFaceGenMorphData = (void *)0x037340E8; +const void * RTTI_BSFaceGenMorphDataHead = (void *)0x03734118; +const void * RTTI_BSFaceGenMorphDataHair = (void *)0x03734148; +const void * RTTI_BSFaceGenBaseMorphExtraData = (void *)0x03734178; +const void * RTTI_BSFaceGenNiNode = (void *)0x037341E8; +const void * RTTI_BSFadeNodeCuller = (void *)0x03734470; +const void * RTTI_ActorValueInfo = (void *)0x036CFBD0; +const void * RTTI_BGSAttackData = (void *)0x03734DC8; +const void * RTTI_BGSAttackDataMap = (void *)0x03734DF0; +const void * RTTI_IPackageData = (void *)0x03734E60; +const void * RTTI_IAIWorldLocationHandle = (void *)0x037352D0; +const void * RTTI_IPackageDataAIWorldLocationHandle = (void *)0x03735300; +const void * RTTI_IAITarget = (void *)0x03735338; +const void * RTTI_BGSPackageDataBool = (void *)0x03734E88; +const void * RTTI_BGSPackageDataFloat = (void *)0x03735358; +const void * RTTI_BGSPackageDataInt = (void *)0x03735428; +const void * RTTI_BGSPackageDataRefOLD = (void *)0x037354E8; +const void * RTTI_BGSPackageDataLocation = (void *)0x03735518; +const void * RTTI_BGSPackageDataLocationWrapper = (void *)0x03735548; +const void * RTTI_BGSPackageDataTargetSelector = (void *)0x03735580; +const void * RTTI_BGSPackageDataObjectList = (void *)0x03736150; +const void * RTTI_ObjectListItem = (void *)0x03736180; +const void * RTTI_BGSPackageDataRef = (void *)0x037363E8; +const void * RTTI_BGSPackageDataTopic = (void *)0x037365A0; +const void * RTTI_IProcedureTreeItem = (void *)0x037367E0; +const void * RTTI_BGSProcedureDoneExecState = (void *)0x03736850; +const void * RTTI_BGSProcedureTreeConditionalItem = (void *)0x037368E0; +const void * RTTI_BGSProcedureTreeBranch = (void *)0x03736918; +const void * RTTI_BGSProcedureTreeOneChildExecState = (void *)0x03736948; +const void * RTTI_BGSProcedureTreeSequence = (void *)0x03736980; +const void * RTTI_BGSProcedureTreeSequenceExecState = (void *)0x03736A10; +const void * RTTI_BGSProcedureTreeStacked = (void *)0x03736AC0; +const void * RTTI_BGSProcedureTreeStackedExecState = (void *)0x03736B50; +const void * RTTI_BGSProcedureTreeSimultaneous = (void *)0x03736C00; +const void * RTTI_BGSProcedureTreeSimultaneousExecState = (void *)0x03736CA0; +const void * RTTI_BGSProcedureTreeRandom = (void *)0x03736D48; +const void * RTTI_BGSProcedureTreeRandomExecState = (void *)0x03736DD8; +const void * RTTI_BGSProcedureDialogueExecState = (void *)0x03736E80; +const void * RTTI_BGSProcedureEatExecState = (void *)0x03736F20; +const void * RTTI_BGSProcedureFindExecState = (void *)0x03736FB0; +const void * RTTI_BGSProcedureHoldPositionExecState = (void *)0x03737040; +const void * RTTI_BGSProcedureKeepAnEyeOnExecState = (void *)0x037370E8; +const void * RTTI_BGSProcedureSayExecState = (void *)0x03737188; +const void * RTTI_BGSProcedureWaitExecState = (void *)0x03737220; +const void * RTTI_BGSProcedureDone = (void *)0x0373B7A0; +const void * RTTI_BGSProcedureAcquire = (void *)0x0373BA88; +const void * RTTI_BGSProcedureAcquireExecState = (void *)0x0373BB40; +const void * RTTI_BGSProcedureActivate = (void *)0x0373BE28; +const void * RTTI_BGSProcedureActivateExecState = (void *)0x0373BEE0; +const void * RTTI_BGSProcedureDialogue = (void *)0x0373C208; +const void * RTTI_BGSProcedureDialogueActivate = (void *)0x0373C528; +const void * RTTI_BGSProcedureDialogueActivateExecState = (void *)0x0373C5E8; +const void * RTTI_BGSProcedureEat = (void *)0x0373C910; +const void * RTTI_BGSProcedureEscort = (void *)0x0373CD20; +const void * RTTI_BGSProcedureEscortExecState = (void *)0x0373CDD0; +const void * RTTI_BGSProcedureFind = (void *)0x0373D0E8; +const void * RTTI_BGSProcedureFlee = (void *)0x0373D4B0; +const void * RTTI_BGSProcedureFleeExecState = (void *)0x0373D558; +const void * RTTI_BGSProcedureFlightGrab = (void *)0x0373D7E0; +const void * RTTI_BGSProcedureFlightGrabExecState = (void *)0x0373D890; +const void * RTTI_BGSProcedureFollow = (void *)0x0373DD90; +const void * RTTI_BGSProcedureFollowTo = (void *)0x0373DE40; +const void * RTTI_BGSProcedureFollowExecState = (void *)0x0373DEF0; +const void * RTTI_BGSProcedureForceGreet = (void *)0x0373E3A8; +const void * RTTI_BGSProcedureGuard = (void *)0x0373E738; +const void * RTTI_BGSProcedureGuardArea = (void *)0x0373E7D8; +const void * RTTI_BGSProcedureGuardExecState = (void *)0x0373E890; +const void * RTTI_BGSProcedureHoldPosition = (void *)0x0373ECD8; +const void * RTTI_BGSProcedureHover = (void *)0x0373EF98; +const void * RTTI_BGSProcedureHoverExecState = (void *)0x0373F038; +const void * RTTI_BGSProcedureKeepAnEyeOn = (void *)0x0373F2E8; +const void * RTTI_BGSProcedureLock = (void *)0x0373F5B8; +const void * RTTI_BGSProcedureUnlock = (void *)0x0373F658; +const void * RTTI_BGSProcedureLockUnlockExecState = (void *)0x0373F710; +const void * RTTI_BGSProcedureOrbit = (void *)0x0373FB88; +const void * RTTI_BGSProcedureOrbitExecState = (void *)0x0373FC28; +const void * RTTI_BGSProcedurePatrolExecState = (void *)0x0373FF70; +const void * RTTI_BGSProcedurePatrol = (void *)0x03740010; +const void * RTTI_BGSProcedurePursue = (void *)0x037402F0; +const void * RTTI_BGSProcedureSandboxExecState = (void *)0x03740780; +const void * RTTI_BGSProcedureSandbox = (void *)0x03740820; +const void * RTTI_BGSProcedureSay = (void *)0x03740B18; +const void * RTTI_BGSProcedureSit = (void *)0x03740DB0; +const void * RTTI_BGSProcedureSleep = (void *)0x03740E58; +const void * RTTI_BGSProcedureSitSleepExecState = (void *)0x03740EF8; +const void * RTTI_BGSProcedureTravel = (void *)0x037413A0; +const void * RTTI_BGSProcedureTravelExecState = (void *)0x03741450; +const void * RTTI_BGSProcedureTreeProcedure = (void *)0x037416B8; +const void * RTTI_BGSProcedureUseIdleMarker = (void *)0x037417B0; +const void * RTTI_BGSProcedureUseIdleMarkerExecState = (void *)0x03741860; +const void * RTTI_BGSProcedureUseMagic = (void *)0x03741CA8; +const void * RTTI_BGSProcedureUseMagicExecState = (void *)0x03741D60; +const void * RTTI_BGSProcedureUseWeapon = (void *)0x03742370; +const void * RTTI_BGSProcedureUseWeaponExecState = (void *)0x03742420; +const void * RTTI_BGSProcedureWait = (void *)0x037426C8; +const void * RTTI_BGSProcedureWander = (void *)0x037429A8; +const void * RTTI_BGSProcedureWanderExecState = (void *)0x03742A60; +const void * RTTI_BGSVisitProceduresInitActorLocation = (void *)0x03742CC8; +const void * RTTI_BGSVisitProceduresCheckGuardWarnTarget = (void *)0x03742D08; +const void * RTTI_TESPackage = (void *)0x036D1418; +const void * RTTI_PackageCreator = (void *)0x037430D8; +const void * RTTI_DialoguePackage = (void *)0x03743100; +const void * RTTI_TESPackageData = (void *)0x03743128; +const void * RTTI_TESAmbushPackageData = (void *)0x03743150; +const void * RTTI_TESCustomPackageData = (void *)0x037431B8; +const void * RTTI_IProcedureTreeVisitor = (void *)0x037431E8; +const void * RTTI_CustomUtils__HasForceGreetVisitor = (void *)0x03743218; +const void * RTTI_TESDialoguePackageData = (void *)0x03743250; +const void * RTTI_TESEatPackageData = (void *)0x03743280; +const void * RTTI_TESEscortPackageData = (void *)0x037432A8; +const void * RTTI_TESFollowPackageData = (void *)0x037432D8; +const void * RTTI_TESPatrolPackageData = (void *)0x03743308; +const void * RTTI_TESUseItemPackageData = (void *)0x03743338; +const void * RTTI_TESUseWeaponPackageData = (void *)0x03743368; +const void * RTTI_ActorPackageData = (void *)0x03743398; +const void * RTTI_UseWeaponActorPackageData = (void *)0x037433C0; +const void * RTTI_PackageLocation = (void *)0x03743490; +const void * RTTI_BSTempEffect = (void *)0x036D8930; +const void * RTTI_BSTempEffectDebris = (void *)0x03743978; +const void * RTTI_BSTempEffectGeometryDecal = (void *)0x03743AB0; +const void * RTTI_BGSParticleObjectCloneTask = (void *)0x03743B38; +const void * RTTI_BSTempEffectParticle = (void *)0x03743B70; +const void * RTTI_BSTempEffectSPG = (void *)0x03743C18; +const void * RTTI_BSTempEffectWeaponBlood = (void *)0x03743D20; +const void * RTTI_BSTerrainEffect = (void *)0x03743DA0; +const void * RTTI_BSPSysHavokUpdateModifier__ICollisionObjectHandler = (void *)0x03743DD0; +const void * RTTI_NavMesh = (void *)0x03743F18; +const void * RTTI_BSNavmeshInfoMap__IVisitor = (void *)0x03744068; +const void * RTTI_NavMeshInfoMap = (void *)0x03744098; +const void * RTTI_BSNavmeshReferenceObstacleArray = (void *)0x03744330; +const void * RTTI_ObstacleTaskData = (void *)0x037444C0; +const void * RTTI_PathingCoverLocation = (void *)0x03744558; +const void * RTTI_BSPathingCellManager = (void *)0x037445B0; +const void * RTTI_Pathing = (void *)0x03744628; +const void * RTTI_BSPathingCell = (void *)0x03744AE8; +const void * RTTI_PathingCell = (void *)0x03744B10; +const void * RTTI_BSPathingDoor = (void *)0x03744D28; +const void * RTTI_PathingDoor = (void *)0x03744D50; +const void * RTTI_BSPathingLockData = (void *)0x03744F80; +const void * RTTI_PathingLockData = (void *)0x03744FA8; +const void * RTTI_BSPathingNumericIDVisitor = (void *)0x037451A8; +const void * RTTI_PathingNumericIDVisitor = (void *)0x037451D8; +const void * RTTI_PathingRequest = (void *)0x037449A0; +const void * RTTI_BSPathingSpace = (void *)0x03745400; +const void * RTTI_PathingSpace = (void *)0x03745428; +const void * RTTI_TeleportDoorSearch = (void *)0x03745458; +const void * RTTI_NavMeshSearchClosePoint = (void *)0x03745588; +const void * RTTI_NavMeshSearchFitSphere = (void *)0x03745688; +const void * RTTI_NavMeshSearchFlee = (void *)0x03745748; +const void * RTTI_NavMeshSearchHide = (void *)0x037457E0; +const void * RTTI_BSNavmeshSearchHideFilter = (void *)0x03745830; +const void * RTTI_NavMeshSearchLOS = (void *)0x037458D0; +const void * RTTI_NavMeshSearchMaxCost = (void *)0x03745908; +const void * RTTI_BSNavmeshSearchMaxCostFilters = (void *)0x03745938; +const void * RTTI_NavMeshSearchMultipleGoals = (void *)0x03745978; +const void * RTTI_NavMeshSearchSLPoint = (void *)0x037459B8; +const void * RTTI_PathingRequestClosePoint = (void *)0x03744970; +const void * RTTI_PathingRequestClosestGoal = (void *)0x03745C60; +const void * RTTI_PathingRequestCover = (void *)0x03745F78; +const void * RTTI_PathingRequestFlee = (void *)0x037461B8; +const void * RTTI_PathingRequestFly = (void *)0x037463F8; +const void * RTTI_PathingRequestFlyAction = (void *)0x03746628; +const void * RTTI_PathingRequestFlyHover = (void *)0x03746888; +const void * RTTI_PathingRequestFlyLand = (void *)0x03746AE8; +const void * RTTI_PathingRequestFlyOrbit = (void *)0x03746D38; +const void * RTTI_PathingRequestFlyTakeOff = (void *)0x03746F98; +const void * RTTI_PathingRequestHide = (void *)0x03747200; +const void * RTTI_PathingRequestLOS = (void *)0x03747438; +const void * RTTI_PathingRequestOptimalLocation = (void *)0x03747668; +const void * RTTI_PathingRequestRotate = (void *)0x037478D0; +const void * RTTI_PathingRequestSafeStraightLine = (void *)0x03744938; +const void * RTTI_PathingRequestStopMoving = (void *)0x03747D40; +const void * RTTI_BGSQueuedTerrainUpdate = (void *)0x037499A0; +const void * RTTI_BGSQueuedTerrainUpgrade = (void *)0x037499D0; +const void * RTTI_BGSQueuedTerrainDowngrade = (void *)0x03749A00; +const void * RTTI_BGSQueuedTerrainInitialLoad = (void *)0x03749A30; +const void * RTTI_BGSQueuedObjectUpgrade = (void *)0x03749A68; +const void * RTTI_BGSQueuedObjectDowngrade = (void *)0x03749A98; +const void * RTTI_BGSQueuedObjectInitialLoad = (void *)0x03749AC8; +const void * RTTI_BGSWaterCollisionManager__BGSWaterUpdateI = (void *)0x0374A218; +const void * RTTI_BGSWaterCollisionManager__bhkPlaceableWater = (void *)0x0374A260; +const void * RTTI_BGSWaterCollisionManager__bhkWaterfall = (void *)0x0374A2A8; +const void * RTTI_BGSWaterCollisionManager__AutoWater = (void *)0x0374A2E8; +const void * RTTI_TESWaterObject = (void *)0x0374A940; +const void * RTTI_TESWaterReflections = (void *)0x0374A968; +const void * RTTI_TESWaterNormals = (void *)0x0374A998; +const void * RTTI_TESWaterDisplacement = (void *)0x0374A9C0; +const void * RTTI_BGSStoryManagerTreeVisitor = (void *)0x0374AC38; +const void * RTTI_BGSStoryManagerQuestFinder = (void *)0x0374AC70; +const void * RTTI_BGSStoryManager = (void *)0x0374ACA8; +const void * RTTI_BGSStoryManagerEventNode = (void *)0x0374AEF0; +const void * RTTI_BGSStoryManagerQuestNode = (void *)0x0374B020; +const void * RTTI_BGSStoryTeller = (void *)0x0374B0F8; +const void * RTTI_BGSLOSData = (void *)0x0374B318; +const void * RTTI_AnimationSystemUtils__QueuedReferenceAnimationTask = (void *)0x0374B3B0; +const void * RTTI_hkaRaycastInterface = (void *)0x0374B508; +const void * RTTI_hkbSpatialQueryInterface = (void *)0x0374B538; +const void * RTTI_BSIFootIkRaycastInterfaceDB = (void *)0x0374B568; +const void * RTTI_CachedRaycastData = (void *)0x0374B5A0; +const void * RTTI_BGSFootIkRaycastInterfaceDB = (void *)0x0374B5C8; +const void * RTTI_IGamebryoSequenceGeneratorHolderSingleton = (void *)0x0374B840; +const void * RTTI_BGSGamebryoSequenceGeneratorHolderSingleton = (void *)0x0374B880; +const void * RTTI_ISynchronizedAnimationSingleton = (void *)0x0374BC10; +const void * RTTI_BGSSynchronizedAnimationInstance = (void *)0x0374BC48; +const void * RTTI_BGSSynchronizedAnimationManager = (void *)0x0374BCC0; +const void * RTTI_SynchronizedAnimationSingleton = (void *)0x0374BF10; +const void * RTTI_BSLimbIKModifierUtilityCastInfo = (void *)0x0374C1D0; +const void * RTTI_BSLimbIKModifierUtility = (void *)0x0374C238; +const void * RTTI_IAnimationGraphManagerHolder = (void *)0x036D3EF0; +const void * RTTI_IAnimationGraphManagerLoadingTask = (void *)0x036F1C88; +const void * RTTI_SimpleAnimationGraphManagerLoadingTask = (void *)0x0374C488; +const void * RTTI_SimpleAnimationGraphManagerHolder = (void *)0x036D3EB8; +const void * RTTI_WeaponAnimationGraphManagerHolder = (void *)0x0374C4C8; +const void * RTTI_TESCamera = (void *)0x036DA680; +const void * RTTI_BSAlignBoneModifierInterface = (void *)0x0374CAD0; +const void * RTTI_BSLookAtModifierInterface = (void *)0x0374CD70; +const void * RTTI_AttractionObjectLOSUtils__CacheEntry = (void *)0x0374D018; +const void * RTTI_ThirdPersonState = (void *)0x0374D3A0; +const void * RTTI_DialogueCameraState = (void *)0x0374D3C8; +const void * RTTI_VATSCamera__BaseState = (void *)0x0374D500; +const void * RTTI_VATSCamera__ModifierBase = (void *)0x0374D530; +const void * RTTI_VATSCamera__StationaryCameraState = (void *)0x0374D560; +const void * RTTI_VATSCamera__LookAtCameraState = (void *)0x0374D600; +const void * RTTI_VATSCamera__ActorLookCameraState = (void *)0x0374D7F8; +const void * RTTI_AimModel__Component = (void *)0x0374DB88; +const void * RTTI_AimModelPlayerComponent = (void *)0x0374DC70; +const void * RTTI_CombatPath = (void *)0x0374DE48; +const void * RTTI_CombatObject = (void *)0x0374DD78; +const void * RTTI_CombatObjectBase = (void *)0x0374DE20; +const void * RTTI_CombatPathDestinationRef = (void *)0x0374DEA0; +const void * RTTI_CombatPathDestinationActor = (void *)0x0374DF38; +const void * RTTI_CombatChargingSearchData = (void *)0x0374DF70; +const void * RTTI_CombatBehaviorTreeWrapperNode = (void *)0x0374DFA0; +const void * RTTI_CombatBehaviorTreeCreateContextNodeBase = (void *)0x0374E008; +const void * RTTI_CombatBehaviorTreeChargingMovement = (void *)0x0374E170; +const void * RTTI_CombatBehaviorTreeChargingCloseMovement = (void *)0x0374E1E0; +const void * RTTI_CombatSharedObject = (void *)0x03753C88; +const void * RTTI_CombatMovementRequest = (void *)0x03753D48; +const void * RTTI_CombatMovementRequestFollowActor = (void *)0x03753D78; +const void * RTTI_CombatBehaviorTreeMovementRequest = (void *)0x03753FC8; +const void * RTTI_CombatBehaviorTreeGrenade = (void *)0x03754570; +const void * RTTI_CombatBehaviorTreeGun = (void *)0x03754B08; +const void * RTTI_CombatBehaviorTreeThrow = (void *)0x037556E8; +const void * RTTI_CombatArea = (void *)0x03755AD8; +const void * RTTI_CombatAreaShape = (void *)0x03755B00; +const void * RTTI_CombatPathingSearchArea = (void *)0x03755C68; +const void * RTTI_CombatPathingRequestCovered = (void *)0x03755CD8; +const void * RTTI_CombatCoveredPathDebugData = (void *)0x03755D10; +const void * RTTI_CombatCoveredPath = (void *)0x0374E148; +const void * RTTI_CombatPathBuilderCovered = (void *)0x03755D48; +const void * RTTI_CombatPositionTracker = (void *)0x03756E18; +const void * RTTI_CombatSuppressiveFireBehavior = (void *)0x03757128; +const void * RTTI_CombatPathController = (void *)0x03757468; +const void * RTTI_CombatTunnelPathController = (void *)0x03757498; +const void * RTTI_CombatViewController = (void *)0x03757798; +const void * RTTI_CombatViewControllerStandard = (void *)0x037577C8; +const void * RTTI_CombatViewControllerGlance = (void *)0x03757800; +const void * RTTI_CombatViewControllerPath = (void *)0x03757838; +const void * RTTI_MovementParametersFixed = (void *)0x037584E0; +const void * RTTI_PassengerInteraction = (void *)0x03758580; +const void * RTTI_BGSPerk__FindPerkInRanksVisitor = (void *)0x03758818; +const void * RTTI_BGSPerk__AddPerkVisitor = (void *)0x03758850; +const void * RTTI_BGSPerk__ApplyPerksVisitor = (void *)0x03758880; +const void * RTTI_BGSInventoryItem__SetHealthFunctor = (void *)0x03758A18; +const void * RTTI_BGSInventoryItem__ModifyModDataFunctor = (void *)0x03758A50; +const void * RTTI_PowerArmor__EventHandler = (void *)0x03758B78; +const void * RTTI_BSInputEventReceiver = (void *)0x03759750; +const void * RTTI_BSIdleInputWatcher = (void *)0x03759780; +const void * RTTI_BSInputEventSingleUser = (void *)0x03759B78; +const void * RTTI_BuildABot__InputHandler = (void *)0x03759BA8; +const void * RTTI_CheckFastTravelCommand = (void *)0x03759C10; +const void * RTTI_ClearIdleCommand = (void *)0x03759C68; +const void * RTTI_DropItemCommand = (void *)0x03759CE0; +const void * RTTI_FastTravelCommand = (void *)0x03759D40; +const void * RTTI_CheckHardcoreFastTravelCommand = (void *)0x03759D68; +const void * RTTI_PipboyCommandResult = (void *)0x03759DA0; +const void * RTTI_FastTravelCommandResult = (void *)0x03759DD0; +const void * RTTI_LocalMapSnapshotCommand = (void *)0x03759E38; +const void * RTTI_PipboyCommand = (void *)0x03759C40; +const void * RTTI_RemoveCustomMapMarkerCommand = (void *)0x03759ED8; +const void * RTTI_SetCustomMapMarkerCommand = (void *)0x03759F48; +const void * RTTI_SetQuickkeyCommand = (void *)0x03759FC8; +const void * RTTI_SortInventoryCommand = (void *)0x0375A048; +const void * RTTI_ToggleComponentFavoriteCommand = (void *)0x0375A0C8; +const void * RTTI_ToggleQuestActiveStatusCommand = (void *)0x0375A100; +const void * RTTI_ToggleRadioStationCommand = (void *)0x0375A188; +const void * RTTI_UseItemCommand = (void *)0x0375A208; +const void * RTTI_Json__FastWriter = (void *)0x0375A280; +const void * RTTI_PipboyCompanionService = (void *)0x0375A318; +const void * RTTI_ButtonHintBar = (void *)0x0375A9C8; +const void * RTTI_FlatScreenModel = (void *)0x0375AC28; +const void * RTTI_HUDScreenModel = (void *)0x0375B1F8; +const void * RTTI_HUDActionPointMeter = (void *)0x0375B418; +const void * RTTI_HUDActiveEffectsDisplay = (void *)0x0375C100; +const void * RTTI_HUDActiveEffectsModel = (void *)0x0375C9E0; +const void * RTTI_HUDActorValueMeterBase = (void *)0x0375B448; +const void * RTTI_HUDAmmoCounter = (void *)0x0375CF40; +const void * RTTI_HUDExplosiveAmmoCounter = (void *)0x0375D1C8; +const void * RTTI_HUDCompassBase = (void *)0x0375F3D0; +const void * RTTI_HUDCompass = (void *)0x0375F3F8; +const void * RTTI_PowerArmorHUDCompass = (void *)0x0375F4B8; +const void * RTTI_HUDCompassMarkerBase = (void *)0x0375FCD0; +const void * RTTI_HUDCompassDirectionMarker = (void *)0x0375FD00; +const void * RTTI_HUDCompassMarker = (void *)0x0375FD30; +const void * RTTI_HUDComponentBase = (void *)0x0375B4C0; +const void * RTTI_HUDCriticalMeter = (void *)0x0375FE10; +const void * RTTI_HUDCrosshair = (void *)0x0375FEA8; +const void * RTTI_HUDCrosshair__FXCrosshairBase = (void *)0x0375FED0; +const void * RTTI_HUDCrosshair__FXCrosshairBase__FXCrosshairTicks = (void *)0x0375FF10; +const void * RTTI_HUDDirectionalHitIndicatorDataModel = (void *)0x03760748; +const void * RTTI_HUDEnemyHealthMeter = (void *)0x037613A0; +const void * RTTI_HUDExperienceMeter = (void *)0x03761C88; +const void * RTTI_HUDExplosivesDataModel = (void *)0x037625B0; +const void * RTTI_HUDFatigueWarning = (void *)0x03762E88; +const void * RTTI_HUDFloatingQuestMarkers = (void *)0x03763720; +const void * RTTI_HUDFloatingQuestMarkers__QuestMarker = (void *)0x03763750; +const void * RTTI_HUDMessages = (void *)0x03763860; +const void * RTTI_HUDMessagesModel = (void *)0x037664D8; +const void * RTTI_HUDMeterBase = (void *)0x0375B498; +const void * RTTI_HUDMeter = (void *)0x0375B478; +const void * RTTI_GameUIModel = (void *)0x03767288; +const void * RTTI_HUDDataModel = (void *)0x03767708; +const void * RTTI_HUDNotificationsModel = (void *)0x0376BF98; +const void * RTTI_HUDPerkVaultBoy = (void *)0x0376F060; +const void * RTTI_HUDPerkVaultBoyModel = (void *)0x03770150; +const void * RTTI_HUDPlayerHealthMeter = (void *)0x037716B8; +const void * RTTI_HUDQuestVaultBoy = (void *)0x03771F20; +const void * RTTI_HUDObjectiveUpdates = (void *)0x03771F48; +const void * RTTI_HUDQuestUpdates = (void *)0x03772208; +const void * RTTI_HUDQuickContainer = (void *)0x03772A48; +const void * RTTI_HUDQuickContainerDataModel = (void *)0x03773B98; +const void * RTTI_HUDRadiationMeter = (void *)0x03775D00; +const void * RTTI_HUDRadiationModel = (void *)0x03776650; +const void * RTTI_HUDRollover = (void *)0x03777100; +const void * RTTI_HUDRolloverModel = (void *)0x03778240; +const void * RTTI_HUDSubtitleText = (void *)0x0377BB70; +const void * RTTI_HUDTutorialText = (void *)0x0377C590; +const void * RTTI_HUDData = (void *)0x0377C698; +const void * RTTI_HUDDataBase = (void *)0x0377C6B8; +const void * RTTI_ContainerMenuBase__IValidFunc = (void *)0x0377CA98; +const void * RTTI_OpenBarterMenuMessage = (void *)0x0377CAD0; +const void * RTTI_BarterMenu = (void *)0x0377CB00; +const void * RTTI_InventoryUpdateData = (void *)0x0377CB98; +const void * RTTI_nsBarterMenu__ValidFunc = (void *)0x0377CBC8; +const void * RTTI_nsBarterMenu__VendorCapsTooLowCallback = (void *)0x0377CBF8; +const void * RTTI_nsBarterMenu__CancelTradeInProgressCallback = (void *)0x0377CC40; +const void * RTTI_nsBarterMenu__ConfirmTradeCallback = (void *)0x0377CC88; +const void * RTTI_nsBarterMenu__HideMenuCallback = (void *)0x0377CCC0; +const void * RTTI_nsBarterMenu__ConfirmInvestmentCallback = (void *)0x0377CCF8; +const void * RTTI_ButtonBarMenu = (void *)0x0377CD60; +const void * RTTI_ContainerMenuBase = (void *)0x0377CB28; +const void * RTTI_ContainerMenuBase__FXQuantityMenu = (void *)0x0377D3C8; +const void * RTTI_CookingMenu = (void *)0x0377D468; +const void * RTTI_ExamineConfirmMenu = (void *)0x0377D510; +const void * RTTI_ExamineConfirmMenu__InitData = (void *)0x0377D540; +const void * RTTI_ExamineConfirmMenu__InitDataBuild = (void *)0x0377D578; +const void * RTTI_ExamineConfirmMenu__InitDataScrap = (void *)0x0377D5B0; +const void * RTTI_ExamineConfirmMenu__InitDataRepairFailure = (void *)0x0377D5E8; +const void * RTTI_Scaleform__GFx__Value__ObjectInterface__ArrVisitor = (void *)0x0377D7D0; +const void * RTTI_BGSInventoryItem__WriteTextExtra = (void *)0x0377D818; +const void * RTTI_ExamineMenu = (void *)0x0377D490; +const void * RTTI_ExamineMenu__FXExamineMenu = (void *)0x0377D850; +const void * RTTI_ExamineMenu__FXExamineMenu__FXExamineSubMenu = (void *)0x0377D880; +const void * RTTI_GameMenuBase = (void *)0x0377CB50; +const void * RTTI_GenericMenu = (void *)0x0377DC58; +const void * RTTI_PipboyOpeningSequenceMenu = (void *)0x0377DC80; +const void * RTTI_FlashHitIndicator = (void *)0x0377DD30; +const void * RTTI_FlashStealth = (void *)0x0377DD58; +const void * RTTI_FlashLocationText = (void *)0x0377DD80; +const void * RTTI_FlashVaultBoyCondition = (void *)0x0377DDA8; +const void * RTTI_FlashExplosiveIndicators = (void *)0x0377DE58; +const void * RTTI_FlashExplosiveIndicators__SingleExplosiveIndicator = (void *)0x0377DE90; +const void * RTTI_FlashDirectionalHitIndicators = (void *)0x0377DED8; +const void * RTTI_FlashDirectionalHitIndicators__SingleDirectionalHitIndicator = (void *)0x0377DF10; +const void * RTTI_LevelUpMenu = (void *)0x0377E068; +const void * RTTI_nsLevelUpMenu__LevelUpPromptRequest = (void *)0x0377E0C8; +const void * RTTI_InputEvent = (void *)0x0377E3F8; +const void * RTTI_IDEvent = (void *)0x0377E420; +const void * RTTI_ButtonEvent = (void *)0x0377E440; +const void * RTTI_LooksInputRepeatHandler = (void *)0x0377E4B8; +const void * RTTI_LooksMenu = (void *)0x0377E4E8; +const void * RTTI_LooksMenuUtils__ExitConfirmCallback = (void *)0x0377E508; +const void * RTTI_LooksMenuUtils__ChangePresetConfirmCallback = (void *)0x0377E550; +const void * RTTI_CreationClubMenu = (void *)0x0377EBD8; +const void * RTTI_CreationClub__AsyncModRequest = (void *)0x0377EC00; +const void * RTTI_CreationClub__CreationListRequest = (void *)0x0377EC38; +const void * RTTI_CreationClub__BundleChildrenRequest = (void *)0x0377EC70; +const void * RTTI_CreationClub__CategorySearchRequest = (void *)0x0377ECB0; +const void * RTTI_CreationClub__GetCreationRequest = (void *)0x0377ECF0; +const void * RTTI_CreationClub__GetIntRequest = (void *)0x0377ED28; +const void * RTTI_CreationClub__DownloadImageRequest = (void *)0x0377ED60; +const void * RTTI_CreationClub__StringStringRequest = (void *)0x0377ED98; +const void * RTTI_CreationClub__SavedGFxValueRequest = (void *)0x0377EDD0; +const void * RTTI_CreationClub__DownloadCreationRequest = (void *)0x0377EE08; +const void * RTTI_CreationClub__CreationChanged_ConfirmResetCallback = (void *)0x0377EE50; +const void * RTTI_ModManager__AsyncModRequest = (void *)0x03780028; +const void * RTTI_ModManager__DownloadModRequest = (void *)0x03780060; +const void * RTTI_ModManager__CategorySearchRequest = (void *)0x03780098; +const void * RTTI_ModManager__GetDetailsRequest = (void *)0x037800D0; +const void * RTTI_ModManager__GetDependencyRequest = (void *)0x03780108; +const void * RTTI_ModManager__DownloadImageRequest = (void *)0x03780140; +const void * RTTI_ModManager__SavedGFxValueRequest = (void *)0x03780178; +const void * RTTI_ModManager__GetIntRequest = (void *)0x037801B0; +const void * RTTI_ModManager__StringStringRequest = (void *)0x037801E0; +const void * RTTI_ModManager__ModsChanged_ConfirmResetCallback = (void *)0x03780220; +const void * RTTI_ModManager__ReportMod_CategoryCallback = (void *)0x037802E0; +const void * RTTI_ModManager__ReportMod_ConfirmCallback = (void *)0x03780320; +const void * RTTI_ModManager__DeleteMod_ConfirmCallback = (void *)0x03780400; +const void * RTTI_ModManager__DeleteLibraryMod_ConfirmCallback = (void *)0x03780440; +const void * RTTI_ModManager__DeleteAllMods_ConfirmCallback = (void *)0x03780488; +const void * RTTI_ModManager__CloseModManager_ConfirmCallback = (void *)0x037804D0; +const void * RTTI_IMultiActivateUser = (void *)0x03780870; +const void * RTTI_MultiActivateManager = (void *)0x037808A0; +const void * RTTI_MultiActivateMenu = (void *)0x03780B80; +const void * RTTI_CompanionListener = (void *)0x03780BA8; +const void * RTTI_ActivateChoiceListener = (void *)0x03780C78; +const void * RTTI_PauseMenu = (void *)0x03780D40; +const void * RTTI_BSUIMessageData = (void *)0x03780D88; +const void * RTTI_nsPauseMenu__UserDisengagedCallback = (void *)0x03780DB0; +const void * RTTI_HolotapeDataManager = (void *)0x03780F78; +const void * RTTI_HolotapeMenu = (void *)0x03781170; +const void * RTTI_PipboyHolotapeMenu = (void *)0x03781198; +const void * RTTI_TerminalHolotapeMenu = (void *)0x037811C8; +const void * RTTI_PipboyLogMenu = (void *)0x03781290; +const void * RTTI_PipboyMapMenu = (void *)0x03781448; +const void * RTTI_PipboyInventoryMenu = (void *)0x037814E8; +const void * RTTI_PipboyMenu = (void *)0x03781640; +const void * RTTI_PipboyPerksMenu = (void *)0x037816F0; +const void * RTTI_PipboyQuestMenu = (void *)0x03781768; +const void * RTTI_PipboyRadioMenu = (void *)0x037817E0; +const void * RTTI_PipboyPlayerInfoMenu = (void *)0x03781858; +const void * RTTI_PipboySpecialMenu = (void *)0x037818D8; +const void * RTTI_PipboyStatsMenu = (void *)0x03781950; +const void * RTTI_PipboySubMenu = (void *)0x037812B8; +const void * RTTI_PipboyWorkshopMenu = (void *)0x03781A18; +const void * RTTI_PlayBinkMenu = (void *)0x03781A88; +const void * RTTI_nsPlayBinkMenu__InitDataMessage = (void *)0x03781AF8; +const void * RTTI_PowerArmorModMenu = (void *)0x03781F10; +const void * RTTI_IPromptMenuRequest = (void *)0x0377E108; +const void * RTTI_PromptMenu = (void *)0x03781FD0; +const void * RTTI_PromptMenuData = (void *)0x03781FF8; +const void * RTTI_RobotModMenu = (void *)0x037820F8; +const void * RTTI_ScopeMenuUtil__FadeCallback = (void *)0x03782210; +const void * RTTI_ScopeMenu = (void *)0x03782248; +const void * RTTI_SPECIALMenu = (void *)0x037822F8; +const void * RTTI_nsSPECIALMenu__ConfirmResetPointsCallback = (void *)0x03782320; +const void * RTTI_StartMenuBase = (void *)0x03780D60; +const void * RTTI_nsStartMenu_InputMapping__RemapHandler = (void *)0x037824F0; +const void * RTTI_StreamingInstallMenu = (void *)0x037828B8; +const void * RTTI_TerminalMenu = (void *)0x037829E8; +const void * RTTI_TerminalRunResultsCallback = (void *)0x03782A10; +const void * RTTI_TerminalMenuButtons = (void *)0x03782A48; +const void * RTTI_TerminalMenuUtils__HolotapeSelectionCallback = (void *)0x03782AF0; +const void * RTTI_Scaleform__RefCountImpl = (void *)0x0377C8C0; +const void * RTTI_Scaleform__RefCountImplCore = (void *)0x0377C8F0; +const void * RTTI_Scaleform__GFx__FunctionHandler = (void *)0x0377C988; +const void * RTTI_SWFToCodeFunctionHandler = (void *)0x0377CA18; +const void * RTTI_VATSMenuPrivate__VatsMenuObj = (void *)0x03782F20; +const void * RTTI_VATSMenu = (void *)0x03782F58; +const void * RTTI_HUDActionPointData = (void *)0x03782F78; +const void * RTTI_VATSMenuPrivate__ASFunctionHandler = (void *)0x03782FA8; +const void * RTTI_VATSMenuPrivate__VatsMenuObj__FXPartInfo = (void *)0x03782FE0; +const void * RTTI_VertibirdMenu = (void *)0x037832F8; +const void * RTTI_VignetteMenu = (void *)0x03783348; +const void * RTTI_WorkbenchMenuBase = (void *)0x0377D4B8; +const void * RTTI_nsInventory3DManager__NewInventoryMenuItemLoadTask = (void *)0x037837A0; +const void * RTTI_nsInventory3DManager__WorkshopMenuItemLoadTask = (void *)0x037837F0; +const void * RTTI_WorkshopMenuGeometry = (void *)0x037838D8; +const void * RTTI_WorkshopMenu = (void *)0x03783908; +const void * RTTI_WorkshopMenu__FXWorkshopMenu = (void *)0x03783970; +const void * RTTI_nsWorkshopMenu__WorkshopMenuPromptRequest = (void *)0x037839A8; +const void * RTTI_nsWS_Caravan__MessageData = (void *)0x03783A50; +const void * RTTI_Workshop_CaravanMenu = (void *)0x03783A80; +const void * RTTI_Workshop_CaravanMenu__FXWorkshopCaravanMenu = (void *)0x03783AB0; +const void * RTTI_PipboyArray = (void *)0x03783B20; +const void * RTTI_PipboyDataGroup = (void *)0x03783B48; +const void * RTTI_PipboyInventoryData = (void *)0x03784710; +const void * RTTI_PipboyLogData = (void *)0x03784748; +const void * RTTI_PipboyLogData__PopulateStatsVisitor = (void *)0x03784770; +const void * RTTI_PipboyManager = (void *)0x03784C38; +const void * RTTI_IPipboyThrottledValue = (void *)0x03785138; +const void * RTTI_PipboyMapData = (void *)0x03785168; +const void * RTTI_PipboyObject = (void *)0x03785A08; +const void * RTTI_PipboyPerksData = (void *)0x03785A80; +const void * RTTI_PipboyQuestData = (void *)0x03785B28; +const void * RTTI_PipboyPlayerInfoData = (void *)0x03785BD0; +const void * RTTI_PipboySpecialData = (void *)0x03785FF8; +const void * RTTI_PipboyStatsData = (void *)0x037860B0; +const void * RTTI_PipboyStatusData = (void *)0x03786130; +const void * RTTI_PipboyValue = (void *)0x03783AF8; +const void * RTTI_PipboyWorkshopData = (void *)0x037861A8; +const void * RTTI_PipboyRadioData = (void *)0x037861D8; +const void * RTTI_AbsorbEffect = (void *)0x03786238; +const void * RTTI_AccumulatingValueModifierEffect = (void *)0x037862F0; +const void * RTTI_ActiveEffect = (void *)0x03786290; +const void * RTTI_ActiveEffectReferenceEffectController = (void *)0x037863B0; +const void * RTTI_ActiveEffect__ForEachHitEffectVisitor = (void *)0x037863F0; +const void * RTTI_BSAnimationGraphChannel = (void *)0x03786548; +const void * RTTI_MagicItemDataCollector = (void *)0x03786578; +const void * RTTI_ActorMagicCaster = (void *)0x037865A8; +const void * RTTI_BSAttachTechniques__AttachTechniqueInput = (void *)0x037865D0; +const void * RTTI_RefAttachTechniqueInput = (void *)0x03786610; +const void * RTTI_CloakEffect = (void *)0x03786768; +const void * RTTI_MagicTarget__IPostCreationModification = (void *)0x03786790; +const void * RTTI_CommandEffect = (void *)0x03786848; +const void * RTTI_CommandSummonedEffect = (void *)0x037868A8; +const void * RTTI_CureEffect = (void *)0x03786910; +const void * RTTI_DamageEffect = (void *)0x03786970; +const void * RTTI_DarknessEffect = (void *)0x037869D0; +const void * RTTI_DetectLifeEffect = (void *)0x03786A30; +const void * RTTI_DisarmEffect = (void *)0x03786A90; +const void * RTTI_DisguiseEffect = (void *)0x03786B18; +const void * RTTI_DisguiseEffect__DetectionChecker = (void *)0x03786B68; +const void * RTTI_DispelEffect = (void *)0x03786C30; +const void * RTTI_DualValueModifierEffect = (void *)0x03786C90; +const void * RTTI_EtherealizationEffect = (void *)0x03786CF8; +const void * RTTI_GuideEffect = (void *)0x03786D88; +const void * RTTI_ImmunityEffect = (void *)0x03786DE8; +const void * RTTI_InvisibilityEffect = (void *)0x03786E48; +const void * RTTI_JetpackEffect = (void *)0x03786EC8; +const void * RTTI_LightEffect = (void *)0x03786F28; +const void * RTTI_LockEffect = (void *)0x03786F98; +const void * RTTI_MagicCaster = (void *)0x036D2700; +const void * RTTI_MagicCaster__PostCreationCallback = (void *)0x03787048; +const void * RTTI_ProcessLists__GetActorsFilter = (void *)0x03787080; +const void * RTTI_ActorTargetCheck = (void *)0x037870B8; +const void * RTTI_MagicTarget = (void *)0x03786C08; +const void * RTTI_FindEffectKeywordOnTargetFunctor = (void *)0x03787138; +const void * RTTI_ModelReferenceEffect = (void *)0x03787220; +const void * RTTI_NightEyeEffect = (void *)0x037872D0; +const void * RTTI_NonActorMagicTarget = (void *)0x036EA458; +const void * RTTI_OpenEffect = (void *)0x03787378; +const void * RTTI_ParalysisEffect = (void *)0x037873D8; +const void * RTTI_PeakValueModifierEffect = (void *)0x03787400; +const void * RTTI_ReanimateEffect = (void *)0x03787468; +const void * RTTI_ReferenceEffect = (void *)0x03787250; +const void * RTTI_WeaponEnchantmentController = (void *)0x037875B8; +const void * RTTI_ScriptEffect = (void *)0x03787628; +const void * RTTI_ScriptedRefEffect = (void *)0x03787650; +const void * RTTI_ShaderReferenceEffect = (void *)0x037876C0; +const void * RTTI_SlowTimeEffect = (void *)0x03787798; +const void * RTTI_SoulTrapEffect = (void *)0x037877F8; +const void * RTTI_StaggerEffect = (void *)0x03786AB8; +const void * RTTI_StimpakEffect = (void *)0x03787890; +const void * RTTI_BSPathing__CheckLineOfSightFilter = (void *)0x03787900; +const void * RTTI_CheckLineOfSightFilterMisc = (void *)0x03787938; +const void * RTTI_SummonPlacementEffect = (void *)0x03787970; +const void * RTTI_SummonCreatureEffect = (void *)0x037879A0; +const void * RTTI_TargetValueModifierEffect = (void *)0x03787A58; +const void * RTTI_RallyEffect = (void *)0x03787A88; +const void * RTTI_DemoralizeEffect = (void *)0x03787AB0; +const void * RTTI_TurnUndeadEffect = (void *)0x03787AD8; +const void * RTTI_BanishEffect = (void *)0x03787B00; +const void * RTTI_CalmEffect = (void *)0x03787B28; +const void * RTTI_FrenzyEffect = (void *)0x03787B50; +const void * RTTI_TelekinesisEffect = (void *)0x03787BB8; +const void * RTTI_ValueModifierEffect = (void *)0x03786260; +const void * RTTI_TESAudio__ScriptedMusicState = (void *)0x03788218; +const void * RTTI_BGSPlayerMusicChanger = (void *)0x03788488; +const void * RTTI_TESAudio__DuckingMgmt__InstancedIni = (void *)0x037887D8; +const void * RTTI_TESAudio__DuckingMgmt__AttenInstancedIni = (void *)0x03788810; +const void * RTTI_MenuTopicManager = (void *)0x03788920; +const void * RTTI_ActorUtils__ArmorRatingVisitorBase = (void *)0x03788BF0; +const void * RTTI_BGSGameWarningsHandler__GameWarningsSink = (void *)0x03788C90; +const void * RTTI_JobListManager__ServingThread = (void *)0x037892B0; +const void * RTTI_BGSSaveLoadManager = (void *)0x03789D30; +const void * RTTI_BGSSaveLoadThread = (void *)0x03789D60; +const void * RTTI_BGSSaveLoadStatsMap = (void *)0x0378A018; +const void * RTTI_BGSLoadGameBuffer = (void *)0x036D5538; +const void * RTTI_BGSSaveFormBuffer = (void *)0x0378A1A0; +const void * RTTI_BGSSaveGameBuffer = (void *)0x0378A1C8; +const void * RTTI_SaveStorageWrapper = (void *)0x0378A1F0; +const void * RTTI_LoadStorageWrapper = (void *)0x0378A220; +const void * RTTI_std___System_error_category = (void *)0x0378A2D8; +const void * RTTI_BSResource__CacheDrive__Op = (void *)0x0378A3C0; +const void * RTTI_BGSMoviePlayer = (void *)0x0378A898; +const void * RTTI_IExplosionFactory = (void *)0x0378A970; +const void * RTTI_ChainExplosion = (void *)0x0378A998; +const void * RTTI_EnhanceWeaponEffect = (void *)0x0378AB30; +const void * RTTI_IGCQueue = (void *)0x0378ABC0; +const void * RTTI_IDDQueueCollection = (void *)0x0378ABE0; +const void * RTTI_RegSettingCollection = (void *)0x0378F320; +const void * RTTI_BSTreeManager__IQueryCullingCamera = (void *)0x0378F390; +const void * RTTI_IMovementControllerNPCFunctor = (void *)0x0378F508; +const void * RTTI_HelpMessageManager = (void *)0x0378F540; +const void * RTTI_UISaveLoadManager = (void *)0x0378F7E0; +const void * RTTI_BGSImpactManager = (void *)0x0378F9D0; +const void * RTTI_Main = (void *)0x0378FCF0; +const void * RTTI_MovementAvoidBoxEventAdapter = (void *)0x0378FFC0; +const void * RTTI_PathManagerPositionPlayerAdapter = (void *)0x0378FFF8; +const void * RTTI_CharacterCollisionMessagePlayerAdapter = (void *)0x03790030; +const void * RTTI_PlayerSleepWaitMovementControllerAdapter = (void *)0x03790070; +const void * RTTI_PlayerSleepWaitMovementControllerAdapter__SwitchToLoadedSetControllerFunctor = (void *)0x037900B0; +const void * RTTI_PlayerSleepWaitMovementControllerAdapter__SwitchToUnloadedSetControllerFunctor = (void *)0x03790120; +const void * RTTI_DoorObstacleAdapter = (void *)0x03790188; +const void * RTTI_IMemoryManagerFile = (void *)0x037902B8; +const void * RTTI_IMemoryManagerFileFactory = (void *)0x037902E8; +const void * RTTI_MemoryManagerFile = (void *)0x03790370; +const void * RTTI_SceneGraph = (void *)0x037904A8; +const void * RTTI_SeenData = (void *)0x036E9DC8; +const void * RTTI_IntSeenData = (void *)0x036E9DE8; +const void * RTTI_SpawnHazardEffect = (void *)0x037905B0; +const void * RTTI_bhkIAddToWorld = (void *)0x03790678; +const void * RTTI_BSShaderProperty__ForEachVisitor = (void *)0x03792080; +const void * RTTI_ProcessObjectsLocal = (void *)0x037920B8; +const void * RTTI_TextureUpdateStage = (void *)0x03792110; +const void * RTTI_ProcessObjects = (void *)0x037920E8; +const void * RTTI_ProcessObjectsDetached = (void *)0x03792178; +const void * RTTI_ProcessObjects1 = (void *)0x037921A8; +const void * RTTI_ProcessObjects2 = (void *)0x037921D0; +const void * RTTI_ProcessObjects3 = (void *)0x037921F8; +const void * RTTI_ProcessObjects4 = (void *)0x03792220; +const void * RTTI_ProcessObjectsLOD = (void *)0x03792248; +const void * RTTI_ProcessObjectsGrass = (void *)0x03792270; +const void * RTTI_ProcessObjectsDecals = (void *)0x037922A0; +const void * RTTI_TextureUpdateStart = (void *)0x037922D0; +const void * RTTI_TextureUpdateDone = (void *)0x03792300; +const void * RTTI_Actor = (void *)0x036CC750; +const void * RTTI_IMovementState = (void *)0x03792CA0; +const void * RTTI_PackageList__IPackageVisitor = (void *)0x03792FC8; +const void * RTTI_IPostAnimationChannelUpdateFunctor = (void *)0x03792E58; +const void * RTTI_ActorUtils__ArmorRatingVisitor = (void *)0x03793000; +const void * RTTI_CombatQueuedEvent = (void *)0x03793038; +const void * RTTI_EquipActorOutfitItemsFunctor = (void *)0x03793B30; +const void * RTTI_SaveLoadMagicCasterVisitor = (void *)0x03793B68; +const void * RTTI_SaveGameMagicCasterVisitor = (void *)0x03793BA0; +const void * RTTI_LoadGameMagicCasterVisitor = (void *)0x03793BD8; +const void * RTTI_RevertSelectedSpellFunctor = (void *)0x03793C10; +const void * RTTI_Actor__ForEachSpellVisitor = (void *)0x03794728; +const void * RTTI_HasSpellVisitor = (void *)0x037947D8; +const void * RTTI_ActorMediator = (void *)0x037948D0; +const void * RTTI_ActorState = (void *)0x03792C78; +const void * RTTI_BGSVisitProceduresProcess = (void *)0x03794C10; +const void * RTTI_BGSVisitProceduresInitActorAnimPose = (void *)0x03794C40; +const void * RTTI_AIProcess__PendingActorHeadData = (void *)0x03794E60; +const void * RTTI_DetectionListener = (void *)0x03786B40; +const void * RTTI_EquippedItemData = (void *)0x037951C8; +const void * RTTI_EquippedWeaponData = (void *)0x037951F0; +const void * RTTI_BGSFootstepManager = (void *)0x037952C8; +const void * RTTI_BSDoorHavokController = (void *)0x03795538; +const void * RTTI_BSPlayerDistanceCheckController = (void *)0x037955A0; +const void * RTTI_BSSimpleScaleController = (void *)0x037955D8; +const void * RTTI_HighActorCuller = (void *)0x03795640; +const void * RTTI_MountInteraction = (void *)0x037956C0; +const void * RTTI_IMovementPlayerControlsFilter = (void *)0x03796308; +const void * RTTI_PlayerCharacter = (void *)0x037963B8; +const void * RTTI_BShkbUtils__GraphInspectionFunctor = (void *)0x03796680; +const void * RTTI_PlayerRegionState = (void *)0x03797140; +const void * RTTI_ProcessLists = (void *)0x03797B70; +const void * RTTI_IMovementFormIDManager = (void *)0x03797D38; +const void * RTTI_ProcessListMovementInterface = (void *)0x03797DB8; +const void * RTTI_RefrInteraction = (void *)0x037585B0; +const void * RTTI_SyncQueueObj = (void *)0x037585D8; +const void * RTTI_ActorMover = (void *)0x03797F58; +const void * RTTI_bhkCharacterCollisionHandler = (void *)0x03798448; +const void * RTTI_CharacterCollisionHandler = (void *)0x037984D0; +const void * RTTI_IMovementAvoidanceManager = (void *)0x03798748; +const void * RTTI_MovementAvoidanceManager = (void *)0x037987D0; +const void * RTTI_IMovementMessageInterface = (void *)0x03798A30; +const void * RTTI_IMovementDirectControl = (void *)0x03798A60; +const void * RTTI_IMovementPlannerDirectControl = (void *)0x03798A90; +const void * RTTI_IMovementSelectIdle = (void *)0x03798AC8; +const void * RTTI_IMovementMotionDrivenControl = (void *)0x03798AF8; +const void * RTTI_IMovementQueryAnimDeltas = (void *)0x03798B30; +const void * RTTI_MovementControllerNPC = (void *)0x03798B60; +const void * RTTI_BSPathingStreamWrite = (void *)0x03798C98; +const void * RTTI_BSPathingStreamSimpleBufferWrite = (void *)0x03798CC8; +const void * RTTI_MovementHandlerAgentDirectControl = (void *)0x03798D38; +const void * RTTI_MovementHandlerAgentPlayerControlsActionTrigger = (void *)0x03798FD0; +const void * RTTI_IMovementSetSprinting = (void *)0x03799300; +const void * RTTI_MovementHandlerAgentSprintActionTrigger = (void *)0x03799330; +const void * RTTI_MovementHandlerAgentStairsHelper = (void *)0x037995F8; +const void * RTTI_MovementMessage = (void *)0x03799850; +const void * RTTI_MovementMessageActorCollision = (void *)0x037998B0; +const void * RTTI_IMovementQueryState = (void *)0x03799B68; +const void * RTTI_MovementPlannerAgentDirectControl = (void *)0x03799B98; +const void * RTTI_IMovementSetWarp = (void *)0x03799F28; +const void * RTTI_MovementPlannerAgentWarp = (void *)0x03799F50; +const void * RTTI_ITweenerNodeFollowerSetup = (void *)0x0379A1C0; +const void * RTTI_MovementTweenerAgentNodeFollower = (void *)0x0379A1F0; +const void * RTTI_PathingStreamLoadGame = (void *)0x0379A4B8; +const void * RTTI_PathingStreamSaveGame = (void *)0x0379A4E8; +const void * RTTI_IMovementPlayerControls = (void *)0x0379A9B8; +const void * RTTI_PlayerInputHandler = (void *)0x0379A9E8; +const void * RTTI_HeldStateHandler = (void *)0x0379AA18; +const void * RTTI_PlayerControls = (void *)0x0379AA40; +const void * RTTI_MovementHandler = (void *)0x0379AC18; +const void * RTTI_LookHandler = (void *)0x0379AC40; +const void * RTTI_SprintHandler = (void *)0x0379AC68; +const void * RTTI_ReadyWeaponHandler = (void *)0x0379AC90; +const void * RTTI_AutoMoveHandler = (void *)0x0379ACC0; +const void * RTTI_ToggleRunHandler = (void *)0x0379ACE8; +const void * RTTI_ActivateHandler = (void *)0x0379AD10; +const void * RTTI_JumpHandler = (void *)0x0379AD38; +const void * RTTI_AttackBlockHandler = (void *)0x0379AD60; +const void * RTTI_RunHandler = (void *)0x0379AD90; +const void * RTTI_SneakHandler = (void *)0x0379ADB8; +const void * RTTI_TogglePOVHandler = (void *)0x0379ADE0; +const void * RTTI_MeleeThrowHandler = (void *)0x0379AE08; +const void * RTTI_GrabRotationHandler = (void *)0x0379AE30; +const void * RTTI_IStaticAvoidNodeManager = (void *)0x0379AE88; +const void * RTTI_StaticAvoidNodeManager = (void *)0x0379AF08; +const void * RTTI_AlarmPackage = (void *)0x03794BE8; +const void * RTTI_CustomActorPackageData = (void *)0x0379B168; +const void * RTTI_EscortActorPackageData = (void *)0x0379B208; +const void * RTTI_FleePackage = (void *)0x03792C50; +const void * RTTI_GuardPackageData = (void *)0x0379B2B8; +const void * RTTI_GuardActorPackageData = (void *)0x0379B2E0; +const void * RTTI_PatrolActorPackageData = (void *)0x0379B348; +const void * RTTI_SpectatorPackage = (void *)0x0379B3C8; +const void * RTTI_TrespassPackage = (void *)0x0379B428; +const void * RTTI_ActorKnowledge = (void *)0x0379B488; +const void * RTTI_DetectionCollector = (void *)0x03786BA0; +const void * RTTI_DetectionState = (void *)0x0379B540; +const void * RTTI_WeaponSwingHandler = (void *)0x0379B8E8; +const void * RTTI_AttackWinStartHandler = (void *)0x0379B918; +const void * RTTI_AttackWinEndHandler = (void *)0x0379B948; +const void * RTTI_AttackStopHandler = (void *)0x0379B978; +const void * RTTI_RecoilStopHandler = (void *)0x0379B9A0; +const void * RTTI_LeftHandSpellFireHandler = (void *)0x0379B9C8; +const void * RTTI_RightHandSpellFireHandler = (void *)0x0379B9F8; +const void * RTTI_VoiceSpellFireHandler = (void *)0x0379BA28; +const void * RTTI_LeftHandSpellCastHandler = (void *)0x0379BA58; +const void * RTTI_RightHandSpellCastHandler = (void *)0x0379BA88; +const void * RTTI_VoiceSpellCastHandler = (void *)0x0379BAB8; +const void * RTTI_WeaponBeginDrawHandler = (void *)0x0379BAE8; +const void * RTTI_WeaponBeginSheatheHandler = (void *)0x0379BB18; +const void * RTTI_WeaponDrawHandler = (void *)0x0379BB48; +const void * RTTI_WeaponSheatheHandler = (void *)0x0379BB70; +const void * RTTI_CameraOverrideStartHandler = (void *)0x0379BBA0; +const void * RTTI_CameraOverrideStopHandler = (void *)0x0379BBD8; +const void * RTTI_HitFrameHandler = (void *)0x0379BC08; +const void * RTTI_AnticipateAttackHandler = (void *)0x0379BC30; +const void * RTTI_StaggeredStopHandler = (void *)0x0379BC60; +const void * RTTI_ChairEnterHandler = (void *)0x0379BC90; +const void * RTTI_PlayerChairEnterHandler = (void *)0x0379BCB8; +const void * RTTI_PlayerTerminalEnterHandler = (void *)0x0379BCE8; +const void * RTTI_BedEnterHandler = (void *)0x0379BD20; +const void * RTTI_PlayerBedEnterHandler = (void *)0x0379BD48; +const void * RTTI_ChairFurnitureExitHandler = (void *)0x0379BD78; +const void * RTTI_BedFurnitureExitHandler = (void *)0x0379BDA8; +const void * RTTI_PlayerFurnitureExitHandler = (void *)0x0379BDD8; +const void * RTTI_KillActorHandler = (void *)0x0379BE10; +const void * RTTI_HeadTrackingOnHandler = (void *)0x0379BE38; +const void * RTTI_HeadTrackingOffHandler = (void *)0x0379BE68; +const void * RTTI_HeadTrackingRotationOnHandler = (void *)0x0379BE98; +const void * RTTI_HeadTrackingRotationOffHandler = (void *)0x0379BED0; +const void * RTTI_FlightTakeOffHandler = (void *)0x0379BF08; +const void * RTTI_FlightCruisingHandler = (void *)0x0379BF38; +const void * RTTI_FlightHoveringHandler = (void *)0x0379BF68; +const void * RTTI_FlightLandingHandler = (void *)0x0379BF98; +const void * RTTI_FlightPerchingHandler = (void *)0x0379BFC8; +const void * RTTI_FlightLandHandler = (void *)0x0379BFF8; +const void * RTTI_FlightLandEndHandler = (void *)0x0379C020; +const void * RTTI_FlightActionHandler = (void *)0x0379C050; +const void * RTTI_FlightActionEntryEndHandler = (void *)0x0379C080; +const void * RTTI_FlightActionEndHandler = (void *)0x0379C0B8; +const void * RTTI_FlightActionGrabHandler = (void *)0x0379C0E8; +const void * RTTI_FlightActionReleaseHandler = (void *)0x0379C118; +const void * RTTI_FlightCrashLandStartHandler = (void *)0x0379C150; +const void * RTTI_BowDrawnHandler = (void *)0x0379C188; +const void * RTTI_BowReleaseHandler = (void *)0x0379C1B0; +const void * RTTI_ArrowAttachHandler = (void *)0x0379C1D8; +const void * RTTI_ArrowDetachHandler = (void *)0x0379C208; +const void * RTTI_BowZoomStartHandler = (void *)0x0379C238; +const void * RTTI_BowZoomStopHandler = (void *)0x0379C268; +const void * RTTI_InterruptCastHandler = (void *)0x0379C298; +const void * RTTI_EndSummonAnimationHandler = (void *)0x0379C2C8; +const void * RTTI_PickNewIdleHandler = (void *)0x0379C2F8; +const void * RTTI_DoNotInterruptAnimationHandler = (void *)0x0379C328; +const void * RTTI_DeathStopHandler = (void *)0x0379C360; +const void * RTTI_ActionActivateDoneHandler = (void *)0x0379C388; +const void * RTTI_StopMountCameraHandler = (void *)0x0379C3B8; +const void * RTTI_PairedStopHandler = (void *)0x0379C3E8; +const void * RTTI_CameraShakeHandler = (void *)0x0379C410; +const void * RTTI_KillMoveStartHandler = (void *)0x0379C440; +const void * RTTI_KillMoveEndHandler = (void *)0x0379C470; +const void * RTTI_DeathEmoteHandler = (void *)0x0379C4A0; +const void * RTTI_AddRagdollHandler = (void *)0x0379C4C8; +const void * RTTI_MotionDrivenHandler = (void *)0x0379C4F0; +const void * RTTI_AnimationDrivenHandler = (void *)0x0379C520; +const void * RTTI_AllowRotationHandler = (void *)0x0379C550; +const void * RTTI_RemoveRagdollHandler = (void *)0x0379C580; +const void * RTTI_RagdollStartHandler = (void *)0x0379C5B0; +const void * RTTI_GetUpStartHandler = (void *)0x0379C5E0; +const void * RTTI_GetUpEndHandler = (void *)0x0379C608; +const void * RTTI_RagdollAndGetUpHandler = (void *)0x0379C630; +const void * RTTI_MountDismountEndHandler = (void *)0x0379C660; +const void * RTTI_NPCAttachHandler = (void *)0x0379C690; +const void * RTTI_NPCDetachHandler = (void *)0x0379C6B8; +const void * RTTI_EnableCharacterBumperHandler = (void *)0x0379C6E0; +const void * RTTI_DisableCharacterBumperHandler = (void *)0x0379C718; +const void * RTTI_EnableCollisionHandler = (void *)0x0379C750; +const void * RTTI_DisableCollisionHandler = (void *)0x0379C780; +const void * RTTI_StartFloatingHandler = (void *)0x0379C7B0; +const void * RTTI_StopFloatingHandler = (void *)0x0379C7E0; +const void * RTTI_AnimationObjectLoadHandler = (void *)0x0379C810; +const void * RTTI_AnimationObjectDrawHandler = (void *)0x0379C848; +const void * RTTI_EnableCharacterPitchHandler = (void *)0x0379C880; +const void * RTTI_DisableCharacterPitchHandler = (void *)0x0379C8B8; +const void * RTTI_JumpAnimEventHandler = (void *)0x0379C8F0; +const void * RTTI_IdleDialogueEnterHandler = (void *)0x0379C920; +const void * RTTI_IdleDialogueExitHandler = (void *)0x0379C950; +const void * RTTI_AnimatedCameraStartHandler = (void *)0x0379C980; +const void * RTTI_AnimatedCameraDeltaStartHandler = (void *)0x0379C9B8; +const void * RTTI_AnimatedCameraEndHandler = (void *)0x0379C9F0; +const void * RTTI_PitchOverrideStartHandler = (void *)0x0379CA20; +const void * RTTI_PitchOverrideEndHandler = (void *)0x0379CA50; +const void * RTTI_ZeroPitchHandler = (void *)0x0379CA80; +const void * RTTI_WeaponFireHandler = (void *)0x0379CAA8; +const void * RTTI_ReloadStateChangeHandler = (void *)0x0379CAD0; +const void * RTTI_ReloadCompleteHandler = (void *)0x0379CB00; +const void * RTTI_SightedStateChangeHandler = (void *)0x0379CB30; +const void * RTTI_PlayerSightedStateChangeHandler = (void *)0x0379CB60; +const void * RTTI_AttackStateChangeHandler = (void *)0x0379CB98; +const void * RTTI_AttackStateChangeHandler0 = (void *)0x0379CBC8; +const void * RTTI_AttackStateChangeHandler1 = (void *)0x0379CBF8; +const void * RTTI_AttackStateChangeHandler2 = (void *)0x0379CC28; +const void * RTTI_AttackStateChangeHandler3 = (void *)0x0379CC58; +const void * RTTI_AttackStateChangeHandler4 = (void *)0x0379CC88; +const void * RTTI_RelaxedStateChangeHandler = (void *)0x0379CCB8; +const void * RTTI_AlertStateChangeHandler = (void *)0x0379CCE8; +const void * RTTI_ReadyStateChangeHandler = (void *)0x0379CD18; +const void * RTTI_GunDownStateChangeHandler = (void *)0x0379CD48; +const void * RTTI_SneakStateChangeHandler = (void *)0x0379CD78; +const void * RTTI_PlayerSneakStateChangeHandler = (void *)0x0379CDA8; +const void * RTTI_LoopingActivateHandler = (void *)0x0379CDE0; +const void * RTTI_ReevaluateGraphStateHandler = (void *)0x0379CE10; +const void * RTTI_UncullBoneHandler = (void *)0x0379CE48; +const void * RTTI_CullFurnitureBoneHandler = (void *)0x0379CE70; +const void * RTTI_UncullFurnitureBoneHandler = (void *)0x0379CEA0; +const void * RTTI_CullWeaponsHandler = (void *)0x0379CED8; +const void * RTTI_UncullWeaponsHandler = (void *)0x0379CF08; +const void * RTTI_IdleFlavorHandler = (void *)0x0379CF38; +const void * RTTI_PerformActivationHandler = (void *)0x0379CF60; +const void * RTTI_AttachReferenceHandler = (void *)0x0379CF90; +const void * RTTI_PipboyBootSequenceHandler = (void *)0x0379CFC0; +const void * RTTI_FaceEmotionalIdleHandler = (void *)0x0379CFF0; +const void * RTTI_UseStimpakHandler = (void *)0x0379D020; +const void * RTTI_UseStealthBoyHandler = (void *)0x0379D048; +const void * RTTI_HolotapeLoadedHandler = (void *)0x0379D078; +const void * RTTI_AwakeSoundFadeHandler = (void *)0x0379D0A8; +const void * RTTI_AwakeSoundStopHandler = (void *)0x0379D0D8; +const void * RTTI_AwakeSoundPauseHandler = (void *)0x0379D108; +const void * RTTI_AwakeSoundResumeHandler = (void *)0x0379D138; +const void * RTTI_PairedAttackDialogueHandler = (void *)0x0379D168; +const void * RTTI_WeaponAttachHandler = (void *)0x0379D1A0; +const void * RTTI_WeaponDetachHandler = (void *)0x0379D1D0; +const void * RTTI_StartPCapVoiceHandler = (void *)0x0379D200; +const void * RTTI_EjectShellCasingHandler = (void *)0x0379D230; +const void * RTTI_SandManKillHandler = (void *)0x0379D260; +const void * RTTI_KnockdownHandler = (void *)0x0379D290; +const void * RTTI_KnockdownTargetHandler = (void *)0x0379D2B8; +const void * RTTI_SyncDeferDeathHandler = (void *)0x0379D2E8; +const void * RTTI_SyncEarlyExitHandler = (void *)0x0379D318; +const void * RTTI_PlayerFastEquipSoundHandler = (void *)0x0379D348; +const void * RTTI_DesyncInteractionHandler = (void *)0x0379D380; +const void * RTTI_UpdateSightedHandler = (void *)0x0379D3B0; +const void * RTTI_AnimationObject = (void *)0x037B01E8; +const void * RTTI_BGShkMatFadeController = (void *)0x037B16C0; +const void * RTTI_BSDirectAtModifierInterface = (void *)0x037B1900; +const void * RTTI_BSPassByTargetTriggerModifierInterface = (void *)0x037B1BA0; +const void * RTTI_BSRagdollContactListenerModifierInterface = (void *)0x037B2570; +const void * RTTI_ArrowProjectile = (void *)0x037B2948; +const void * RTTI_BarrierProjectile = (void *)0x037B29A8; +const void * RTTI_BeamProjectile = (void *)0x037B2A28; +const void * RTTI_ConeProjectile = (void *)0x037B2B18; +const void * RTTI_Explosion = (void *)0x0378A9C0; +const void * RTTI_ExplosionClosestHitCollector = (void *)0x037B2C28; +const void * RTTI_FlameProjectile = (void *)0x037B2CA0; +const void * RTTI_GrenadeProjectile = (void *)0x037B2D08; +const void * RTTI_Hazard = (void *)0x03786D68; +const void * RTTI_MissileProjectile = (void *)0x037B2970; +const void * RTTI_Projectile = (void *)0x0378A948; +const void * RTTI_HealthDamageFunctor = (void *)0x037B2F98; +const void * RTTI_VATS = (void *)0x037B3388; +const void * RTTI_IValidateGoalFunctor = (void *)0x037B3590; +const void * RTTI_VATSUtils__ValidateStrangerLineOfSightGoalToOriginFunctor = (void *)0x037B35C0; +const void * RTTI_CombatAimController = (void *)0x037B3678; +const void * RTTI_CombatAimControllerBase = (void *)0x037B36A8; +const void * RTTI_CombatTrackTargetAimController = (void *)0x037B36D8; +const void * RTTI_CombatMeleeAimController = (void *)0x037B3710; +const void * RTTI_CombatProjectileAimController = (void *)0x037B3740; +const void * RTTI_CombatDisableAimController = (void *)0x037B3778; +const void * RTTI_CombatMatchTargetAimController = (void *)0x037B37B0; +const void * RTTI_CombatProjectileDebugData = (void *)0x037B37E8; +const void * RTTI_CombatAreaReference = (void *)0x037B46D0; +const void * RTTI_CombatAreaStandard = (void *)0x037B4700; +const void * RTTI_CombatAreaHoldPosition = (void *)0x037B4730; +const void * RTTI_CombatBlackboardMemberBase = (void *)0x03754498; +const void * RTTI_CombatCoverSearchResult = (void *)0x037B50F0; +const void * RTTI_CombatCoverLocation = (void *)0x037B5120; +const void * RTTI_CombatCoverSearchDebugData = (void *)0x037B5150; +const void * RTTI_CombatCoverSearch = (void *)0x037B5188; +const void * RTTI_CombatEnterCoverPathController = (void *)0x037B51B0; +const void * RTTI_CombatMantlePathController = (void *)0x037B51E8; +const void * RTTI_CombatCluster = (void *)0x037B84A8; +const void * RTTI_CombatGroupDetectionListener = (void *)0x037B84D0; +const void * RTTI_CombatFormObject = (void *)0x037B8AE0; +const void * RTTI_CombatInventoryItem = (void *)0x037B8B90; +const void * RTTI_CombatInventoryItemComposite = (void *)0x037B8BC0; +const void * RTTI_CombatInventoryItemMelee = (void *)0x037B8BF8; +const void * RTTI_CombatInventoryItemRanged = (void *)0x037B8C28; +const void * RTTI_CombatInventoryItemThrown = (void *)0x037B8C58; +const void * RTTI_CombatInventoryItemGrenade = (void *)0x037B8C88; +const void * RTTI_CombatInventoryItemShield = (void *)0x037B8CC0; +const void * RTTI_CombatInventoryItemOneHandedBlock = (void *)0x037B8CF0; +const void * RTTI_CombatInventoryItemTorch = (void *)0x037B8D28; +const void * RTTI_CombatInventoryItemMagic = (void *)0x037B8D58; +const void * RTTI_CombatMagicCaster = (void *)0x037B9B78; +const void * RTTI_CombatInventoryItemStaff = (void *)0x037B9BA0; +const void * RTTI_CombatInventoryItemPotion = (void *)0x037B9BD0; +const void * RTTI_CombatInventoryItemScroll = (void *)0x037B9C00; +const void * RTTI_CombatMagicItemData = (void *)0x037B9C30; +const void * RTTI_CombatMagicItemSkillChecker = (void *)0x037B9C60; +const void * RTTI_CombatMagicCasterOffensive = (void *)0x037B9C98; +const void * RTTI_CombatMagicCasterWard = (void *)0x037B9CD0; +const void * RTTI_CombatMagicCasterRestore = (void *)0x037B9D00; +const void * RTTI_CombatMagicCasterSummon = (void *)0x037B9D30; +const void * RTTI_CombatMagicCasterStagger = (void *)0x037B9D60; +const void * RTTI_CombatMagicCasterDisarm = (void *)0x037B9D90; +const void * RTTI_CombatMagicCasterCloak = (void *)0x037B9DC0; +const void * RTTI_CombatMagicCasterLight = (void *)0x037B9DF0; +const void * RTTI_CombatMagicCasterInvisibility = (void *)0x037B9E20; +const void * RTTI_CombatMagicCasterChameleon = (void *)0x037B9E58; +const void * RTTI_CombatMagicCasterBoundItem = (void *)0x037B9E90; +const void * RTTI_CombatMagicCasterArmor = (void *)0x037B9EC8; +const void * RTTI_CombatMagicCasterTargetEffect = (void *)0x037B9EF8; +const void * RTTI_CombatMagicCasterReanimate = (void *)0x037B9F30; +const void * RTTI_CombatMagicCasterParalyze = (void *)0x037B9F68; +const void * RTTI_CombatMagicCasterScript = (void *)0x037B9F98; +const void * RTTI_CombatTargetLocationSearchResult = (void *)0x037C9A30; +const void * RTTI_CombatTargetLocation = (void *)0x037C9A68; +const void * RTTI_CombatTargetLocationSearch = (void *)0x037C9A98; +const void * RTTI_CombatTargetSelector = (void *)0x037CBFA8; +const void * RTTI_CombatTargetSelectorStandard = (void *)0x037CBFD8; +const void * RTTI_CombatTargetSelectorPreferred = (void *)0x037CC010; +const void * RTTI_CombatTargetSelectorFixed = (void *)0x037CC048; +const void * RTTI_CombatTargetSelectorRandom = (void *)0x037CC078; +const void * RTTI_CombatThreat = (void *)0x037CC8E0; +const void * RTTI_CombatThreatExplosion = (void *)0x037CC908; +const void * RTTI_CombatThreatProjectile = (void *)0x037CC938; +const void * RTTI_CombatThreatLOF = (void *)0x037CC968; +const void * RTTI_CombatThreatMelee = (void *)0x037CC990; +const void * RTTI_ProjectileLOSCollector = (void *)0x037CD210; +const void * RTTI_CombatBehaviorResource = (void *)0x037CD328; +const void * RTTI_CombatBehaviorTreeRootNode = (void *)0x037CD3C8; +const void * RTTI_CombatBehaviorTreeLinkNode = (void *)0x037CD400; +const void * RTTI_CombatBehaviorTree = (void *)0x0374E1B0; +const void * RTTI_CombatBehaviorTreeCombat = (void *)0x037CD470; +const void * RTTI_CombatBehaviorTreeNode = (void *)0x0374DFD8; +const void * RTTI_CombatAcquireSearchDebugData = (void *)0x037CD5C8; +const void * RTTI_CombatBehaviorTreeAcquireObject = (void *)0x037CD600; +const void * RTTI_CombatBehaviorTreeAction = (void *)0x037CE128; +const void * RTTI_CombatBehaviorTreeActionEquipmentSelector = (void *)0x037CE160; +const void * RTTI_CombatBehaviorTreeAvoidThreat = (void *)0x037CEDA0; +const void * RTTI_CombatBehaviorTreeBlock = (void *)0x037D1448; +const void * RTTI_CombatApproachTargetPathController = (void *)0x037D1B40; +const void * RTTI_CombatBehaviorTreeCloseMovement = (void *)0x037D1B80; +const void * RTTI_CombatBehaviorTreeFindAttackLocation = (void *)0x037D2870; +const void * RTTI_CombatFlankingSearchData = (void *)0x037D4CF0; +const void * RTTI_CombatBehaviorTreeFlankingMovement = (void *)0x037D4D20; +const void * RTTI_CombatBehaviorTreeFlee = (void *)0x037DAF80; +const void * RTTI_CombatBehaviorTreeHide = (void *)0x037DCA30; +const void * RTTI_CombatBehaviorTreeFlight = (void *)0x037DDA90; +const void * RTTI_DiveBombPathController = (void *)0x037DDAC0; +const void * RTTI_CombatBehaviorTreeLowCombat = (void *)0x037DEA88; +const void * RTTI_CombatBehaviorTreeMagic = (void *)0x037DF128; +const void * RTTI_CombatStaticBlackboardMemberBase = (void *)0x037DF7F8; +const void * RTTI_CombatMeleeDebugData = (void *)0x037DF830; +const void * RTTI_CombatBehaviorTreeMelee = (void *)0x037DFA38; +const void * RTTI_CombatDisableActionController = (void *)0x037DFF58; +const void * RTTI_CombatBehaviorTreeCheckUnreachableTarget = (void *)0x037DFF90; +const void * RTTI_CombatBehaviorTreeReturnToCombatArea = (void *)0x037DFFD0; +const void * RTTI_CombatBehaviorTreeFindValidLocation = (void *)0x037E1CA0; +const void * RTTI_CombatBehaviorTreeMovement = (void *)0x037E4660; +const void * RTTI_CombatBehaviorTreeBow = (void *)0x037E4F00; +const void * RTTI_CombatBehaviorTreeRangedMovement = (void *)0x037E5170; +const void * RTTI_CombatBehaviorTreeSearch = (void *)0x037E5CA0; +const void * RTTI_CombatSearchLockData = (void *)0x037E77A0; +const void * RTTI_CombatBehaviorTreeUseCover = (void *)0x037EC4F0; +const void * RTTI_CombatBehaviorMoveInCover = (void *)0x037EC528; +const void * RTTI_CombatFindCoverPathSpeedController = (void *)0x037EC558; +const void * RTTI_CombatChangePositionPathController = (void *)0x037EE358; +const void * RTTI_CombatBehaviorTreeUsePotion = (void *)0x037EFA08; +const void * RTTI_CombatPathDestinationFollowActor = (void *)0x037EFD70; +const void * RTTI_CombatAnimatedPath = (void *)0x037EFDA8; +const void * RTTI_CombatPathBuilder = (void *)0x03752D48; +const void * RTTI_CombatNavmeshSearch = (void *)0x037535E0; +const void * RTTI_IPathBuilder = (void *)0x0374E048; +const void * RTTI_CombatPathBuilderStandard = (void *)0x03752D18; +const void * RTTI_CombatPathBuilderOpen = (void *)0x037535B0; +const void * RTTI_CombatPathMovementMessage = (void *)0x037F0018; +const void * RTTI_CombatPathingDebugData = (void *)0x037F0178; +const void * RTTI_CombatPathingRequest = (void *)0x0374DE70; +const void * RTTI_CombatPathingRequestStandard = (void *)0x037F01A8; +const void * RTTI_CombatPathingRequestAdapter = (void *)0x037F01E0; +const void * RTTI_CombatPathingRequestMultiGoal = (void *)0x037F0218; +const void * RTTI_CombatPathDestinationRefs = (void *)0x037F1060; +const void * RTTI_CombatFollowTargetPathController = (void *)0x037F1090; +const void * RTTI_CombatPathingTweener = (void *)0x037F10C8; +const void * RTTI_HorseCameraState = (void *)0x03804B40; +const void * RTTI_PlayerCamera = (void *)0x03804DB0; +const void * RTTI_AutoVanityState = (void *)0x03804F78; +const void * RTTI_FreeCameraState = (void *)0x03804FA0; +const void * RTTI_IronSightsState = (void *)0x03804FC8; +const void * RTTI_FurnitureCameraState = (void *)0x03804FF0; +const void * RTTI_PlayerCameraTransitionState = (void *)0x03805020; +const void * RTTI_BleedoutCameraState = (void *)0x03805058; +const void * RTTI_FirstPersonState = (void *)0x03805088; +const void * RTTI_TweenMenuCameraState = (void *)0x03805490; +const void * RTTI_VATSCameraState = (void *)0x03805528; +const void * RTTI_BookMenu = (void *)0x03805738; +const void * RTTI_Console = (void *)0x038058F0; +const void * RTTI_IStringForwarder = (void *)0x03805910; +const void * RTTI_ConsoleNativeUIMenu = (void *)0x03805C18; +const void * RTTI_OpenContainerMenuMessage = (void *)0x03805CE0; +const void * RTTI_ContainerMenu = (void *)0x03805D10; +const void * RTTI_nsContainerMenu__ConfirmTakeAllTheThingsCallback = (void *)0x03805D40; +const void * RTTI_nsContainerMenu__CanPickPocketAliasChecker = (void *)0x03805D90; +const void * RTTI_nsContainerMenu__ValidFunc = (void *)0x03805DD0; +const void * RTTI_CreditsMenu = (void *)0x03805E40; +const void * RTTI_CursorMenu = (void *)0x03805E90; +const void * RTTI_DialogueMenu = (void *)0x03806128; +const void * RTTI_FaderMenu = (void *)0x03806258; +const void * RTTI_FavoritesManager = (void *)0x03806398; +const void * RTTI_FavoritesMenu = (void *)0x038065C8; +const void * RTTI_PowerArmorGeometry = (void *)0x038069A8; +const void * RTTI_PowerArmorHUDMenu = (void *)0x03806C20; +const void * RTTI_HUDMenu = (void *)0x03806C48; +const void * RTTI_HUDPowerArmorLowBatterWarningText = (void *)0x03806DC8; +const void * RTTI_HUDStealthMeter = (void *)0x03806E40; +const void * RTTI_HUDLocationText = (void *)0x03806E68; +const void * RTTI_ExplosiveIndicators = (void *)0x03806E90; +const void * RTTI_DirectionalHitIndicators = (void *)0x03806F58; +const void * RTTI_HUDFlashLightWidget = (void *)0x03807030; +const void * RTTI_PowerArmorHUDUtils__MainStatusHandler = (void *)0x03807060; +const void * RTTI_Inventory3DManager = (void *)0x03808008; +const void * RTTI_nsInventory3DManager__NewInventoryMenuItemMaterialSwapMaterialLoadTask = (void *)0x03808040; +const void * RTTI_nsInventory3DManager__NewInventoryMenuItemMaterialSwapTextureLoadTask = (void *)0x03808110; +const void * RTTI_LoadingMenu = (void *)0x038086D0; +const void * RTTI_LockpickingMenu = (void *)0x03808968; +const void * RTTI_MainMenu = (void *)0x03808CB0; +const void * RTTI_nsMainMenu__DeviceSelectCallback = (void *)0x03808CD0; +const void * RTTI_nsMainMenu__NoDurangoUserSignedInCallback = (void *)0x03808D08; +const void * RTTI_nsMainMenu__DLCChanged_ConfirmResetCallback = (void *)0x03808D50; +const void * RTTI_MenuControls = (void *)0x038094B0; +const void * RTTI_GFxConvertHandler = (void *)0x03809678; +const void * RTTI_DisconnectHandler = (void *)0x038096A0; +const void * RTTI_ClickHandler = (void *)0x038096C8; +const void * RTTI_QuickSaveLoadHandler = (void *)0x038096F0; +const void * RTTI_MenuOpenHandler = (void *)0x03809720; +const void * RTTI_ScreenshotHandler = (void *)0x03809748; +const void * RTTI_CameraZoomHandler = (void *)0x03809770; +const void * RTTI_PipboyHandler = (void *)0x03809798; +const void * RTTI_MessageBoxData = (void *)0x03809B88; +const void * RTTI_MessageBoxMenu = (void *)0x03809BB0; +const void * RTTI_MessageBoxMenu__FXMessageBoxMenu = (void *)0x03809BD8; +const void * RTTI_SafeZoneMenu = (void *)0x03809C38; +const void * RTTI_SitWaitMenu = (void *)0x03809CC8; +const void * RTTI_SleepWaitMenu = (void *)0x03809D68; +const void * RTTI_TitleSequenceMenu = (void *)0x03809E98; +const void * RTTI_TutorialMenu = (void *)0x03809F00; +const void * RTTI_UIBlurManager = (void *)0x0380A560; +const void * RTTI_FaderData = (void *)0x0380AAE0; +const void * RTTI_RefHandleUIData = (void *)0x03782998; +const void * RTTI_TESFormUIData = (void *)0x037829C0; +const void * RTTI_LoadingMenuData = (void *)0x0380AB00; +const void * RTTI_DialogueMenuUtils__DialogueMessageData = (void *)0x0380AB28; +const void * RTTI_GameScript__Internal__AnimationCallbacks = (void *)0x0380AFE8; +const void * RTTI_GameScript__DelayFunctor = (void *)0x0380B1F0; +const void * RTTI_GameScript__DelayFunctorFactory = (void *)0x0380B220; +const void * RTTI_BSScript__IHandleReaderWriter = (void *)0x0380B2B0; +const void * RTTI_GameScript__BaseHandleReaderWriter = (void *)0x0380B2E8; +const void * RTTI_GameScript__DataFileHandleReaderWriter = (void *)0x0380B320; +const void * RTTI_GameScript__SaveFileHandleReaderWriter = (void *)0x0380B360; +const void * RTTI_GameScript__CombatEventHandler = (void *)0x0380B740; +const void * RTTI_GameScript__FragmentEventHandler = (void *)0x0380CB68; +const void * RTTI_GameScript__CustomEventHandler = (void *)0x0380DB70; +const void * RTTI_GameScript__Logger = (void *)0x0380E150; +const void * RTTI_GameScript__InventoryEventHandler = (void *)0x0380E920; +const void * RTTI_GameScript__Internal__LOSEventData = (void *)0x0380F2B0; +const void * RTTI_GameScript__LOSEventHandler = (void *)0x0380F2E8; +const void * RTTI_GameScript__Internal__LOSDetectionEventData = (void *)0x0380F520; +const void * RTTI_GameScript__Internal__LOSDirectEventData = (void *)0x0380F560; +const void * RTTI_GameScript__MenuEventHandler = (void *)0x0380FBF8; +const void * RTTI_GameScript__PathingCallbackMgr = (void *)0x038102C0; +const void * RTTI_GameScript__Internal__IProfileCallQuery = (void *)0x03810570; +const void * RTTI_GameScript__Profiler = (void *)0x038105B0; +const void * RTTI_GameScript__QuestCallbackMgr = (void *)0x038106D8; +const void * RTTI_GameScript__SavePatcher = (void *)0x03810908; +const void * RTTI_GameScript__SleepEventHandler = (void *)0x03810A00; +const void * RTTI_GameScript__SoundCallbackMgr = (void *)0x03810E70; +const void * RTTI_BSScript__IStore = (void *)0x038110B0; +const void * RTTI_GameScript__Store = (void *)0x038110D8; +const void * RTTI_BSResource__ArchiveFoundStreamCB = (void *)0x03815138; +const void * RTTI_BSCounterStorage = (void *)0x03815280; +const void * RTTI_BSScript__IObjectHandlePolicy = (void *)0x03815448; +const void * RTTI_GameVM = (void *)0x03815480; +const void * RTTI_GameScript__HandlePolicy = (void *)0x03815710; +const void * RTTI_GameScript__ObjectBindPolicy = (void *)0x03815740; +const void * RTTI_BSScript__IStackCallbackFunctor = (void *)0x038157A8; +const void * RTTI_BSScript__IStackCallbackSaveInterface = (void *)0x03815650; +const void * RTTI_BSScript__IProfilePolicy = (void *)0x03810540; +const void * RTTI_BSScript__ILoader = (void *)0x038157E0; +const void * RTTI_BSScript__ObjectBindPolicy = (void *)0x03815778; +const void * RTTI_BSScript__IClientVM = (void *)0x03815620; +const void * RTTI_BSScript__ISavePatcherInterface = (void *)0x03810938; +const void * RTTI_GameScript__BasicEventHandler = (void *)0x03824908; +const void * RTTI_GameScript__TeleportEventHandler = (void *)0x03825AF8; +const void * RTTI_GameScript__TimerEventHandler = (void *)0x03825DE8; +const void * RTTI_GameScript__TrackedStatsEventHandler = (void *)0x038265B0; +const void * RTTI_GameScript__DistanceEventHandler = (void *)0x03826AC0; +const void * RTTI_GameScript__WaitEventHandler = (void *)0x03827250; +const void * RTTI_CCallbackBase = (void *)0x038276B8; +const void * RTTI_BSSteamSystemUtility = (void *)0x038277F8; +const void * RTTI_BSSteamAwardsSystemUtility = (void *)0x03827850; +const void * RTTI_Json__StyledWriter = (void *)0x038278E8; +const void * RTTI_Json__Exception = (void *)0x03827970; +const void * RTTI_Json__RuntimeError = (void *)0x03827998; +const void * RTTI_Json__LogicError = (void *)0x038279C0; +const void * RTTI_Json__CharReader = (void *)0x038279E8; +const void * RTTI_Json__CharReader__Factory = (void *)0x03827A10; +const void * RTTI_Json__CharReaderBuilder = (void *)0x03827A40; +const void * RTTI_Json__StreamWriter = (void *)0x03827A70; +const void * RTTI_Json__StreamWriter__Factory = (void *)0x03827A98; +const void * RTTI_Json__StreamWriterBuilder = (void *)0x03827AC8; +const void * RTTI_Json__Writer = (void *)0x0375A2A8; +const void * RTTI_Json__OurCharReader = (void *)0x03827AF8; +const void * RTTI_Json__BuiltStyledStreamWriter = (void *)0x03827B28; +const void * RTTI_hkLoader = (void *)0x03827BE0; +const void * RTTI_BShkbHkxDB__QueuedHandles = (void *)0x036F1CC0; +const void * RTTI_BShkbGlobalTransitionData = (void *)0x03827C00; +const void * RTTI_BSAnimationDBData = (void *)0x03827C30; +const void * RTTI_BShkbHkxDB__ProjectDBData = (void *)0x03827C58; +const void * RTTI_BShkbHkxDB__hkxDBData = (void *)0x03827C88; +const void * RTTI_BShkbHkxDB__StreamAdaptor = (void *)0x038283E0; +const void * RTTI_hknpRefMaterial = (void *)0x038291C0; +const void * RTTI_hknpMaterialLibrary = (void *)0x038291E8; +const void * RTTI_hknpSurfaceVelocity = (void *)0x03829218; +const void * RTTI_hknpLinearSurfaceVelocity = (void *)0x03829248; +const void * RTTI_hkReferencedObjectLock = (void *)0x03829298; +const void * RTTI_hkTask = (void *)0x03829308; +const void * RTTI_hknpConstraintSolver = (void *)0x03829328; +const void * RTTI_hknpWeldingModifier = (void *)0x03829358; +const void * RTTI_hknpWorld = (void *)0x03829388; +const void * RTTI_hknpSimulation = (void *)0x038293A8; +const void * RTTI_hknpContactConstraintSolver = (void *)0x03829410; +const void * RTTI_hknpCollisionDetector = (void *)0x03829448; +const void * RTTI_hkSecondaryCommandDispatcher = (void *)0x03829478; +const void * RTTI_hknpInternalCommandProcessor = (void *)0x038294B0; +const void * RTTI_hknpConstraintAtomSolver = (void *)0x038294E8; +const void * RTTI_hknpDefaultModifierSet = (void *)0x03829518; +const void * RTTI_hknpPostCollideTask = (void *)0x03829548; +const void * RTTI_hknpPostSolveTask = (void *)0x03829578; +const void * RTTI_hkDebugCommandProcessor = (void *)0x038295A0; +const void * RTTI_hknpConvexCompositeCollisionDetector = (void *)0x038295D0; +const void * RTTI_hknpCompositeCompositeCollisionDetector = (void *)0x03829648; +const void * RTTI_hknpCompositeCollisionDetector = (void *)0x03829610; +const void * RTTI_hknpSetShapeKeyACdDetector = (void *)0x03829688; +const void * RTTI_hknpShapeKeyArrayCacheCdDetector = (void *)0x038296C0; +const void * RTTI_hknpApiCommandProcessor = (void *)0x03829738; +const void * RTTI_hknpManifoldEventCreator = (void *)0x03829768; +const void * RTTI_hknpSingleThreadedSimulation = (void *)0x03829798; +const void * RTTI_hknpContactImpulseClippedEventCreator = (void *)0x038297D0; +const void * RTTI_hknpContactImpulseEventCreator = (void *)0x03829810; +const void * RTTI_hknpMassChangerModifier = (void *)0x03829848; +const void * RTTI_hknpTriggerModifier = (void *)0x03829878; +const void * RTTI_hknpRestitutionModifier = (void *)0x038298A8; +const void * RTTI_hknpSoftContactModifier = (void *)0x038298D8; +const void * RTTI_hknpTriangleWeldingModifier = (void *)0x03829908; +const void * RTTI_hknpNeighborWeldingModifier = (void *)0x03829940; +const void * RTTI_hknpMotionWeldingModifier = (void *)0x03829978; +const void * RTTI_hknpConvexConvexCollisionDetector = (void *)0x038299A8; +const void * RTTI_hknpSignedDistanceFieldCollisionDetector = (void *)0x038299E0; +const void * RTTI_hknpTriggerCollisionDetector = (void *)0x03829A20; +const void * RTTI_hknpConstraintForceEventCreator = (void *)0x03829A58; +const void * RTTI_hknpConstraintForceExceededEventCreator = (void *)0x03829A90; +const void * RTTI_hknpMxContactSolver = (void *)0x03829AD0; +const void * RTTI_hknpStreamContactSolverBase = (void *)0x03829B00; +const void * RTTI_hknpStreamContactSolver = (void *)0x03829B70; +const void * RTTI_hknpActionManager = (void *)0x03829E00; +const void * RTTI_hknpShapeMassProperties = (void *)0x0382A1B0; +const void * RTTI_hknpPhysicsSystem = (void *)0x0382A1E0; +const void * RTTI_hknpCompositeShape = (void *)0x036CE9C8; +const void * RTTI_hknpHeightFieldShape = (void *)0x036CE9F8; +const void * RTTI_hknpCompressedHeightFieldShape = (void *)0x0382A220; +const void * RTTI_hkCrc32StreamWriter = (void *)0x0382A258; +const void * RTTI_hkTagfileWriter = (void *)0x0382A2C0; +const void * RTTI_hkTagfileWriter__AddDataObjectListener = (void *)0x0382A2E8; +const void * RTTI_hkTypeInfoRegistry = (void *)0x0382A328; +const void * RTTI_hkTagfileReader = (void *)0x0382A398; +const void * RTTI_hkBinaryTagfileWriter = (void *)0x0382A3C0; +const void * RTTI_hkXmlTagfileReader = (void *)0x0382A3F0; +const void * RTTI_hkXmlTagfileWriter = (void *)0x0382A420; +const void * RTTI_hkMemoryAllocator = (void *)0x0382A4E0; +const void * RTTI_hkContainerTempAllocator__Allocator = (void *)0x0382A508; +const void * RTTI_hkContainerHeapAllocator__Allocator = (void *)0x0382A548; +const void * RTTI_hkContainerDebugAllocator__Allocator = (void *)0x0382A588; +const void * RTTI_hkVtableClassRegistry = (void *)0x0382A5E8; +const void * RTTI_hkClassNameRegistry = (void *)0x0382A658; +const void * RTTI_hkBuiltinTypeRegistry = (void *)0x0382A688; +const void * RTTI_hkDynamicClassNameRegistry = (void *)0x0382A6F8; +const void * RTTI_hkDefaultClassNameRegistry = (void *)0x0382A730; +const void * RTTI_hkDefaultBuiltinTypeRegistry = (void *)0x0382A7B8; +const void * RTTI_hkLifoAllocator = (void *)0x0382A7F0; +const void * RTTI_hkcdGskBase__ShapeInterface = (void *)0x0382A818; +const void * RTTI_hkcdGsk_Vector4ShapeInterface = (void *)0x0382A850; +const void * RTTI_hknpTriangleShape = (void *)0x0382A910; +const void * RTTI_hknpDynamicCompoundShapeData = (void *)0x0382A938; +const void * RTTI_hknpShapeKeyMask = (void *)0x0382A9E8; +const void * RTTI_hknpCompoundShapeKeyMask = (void *)0x0382AA10; +const void * RTTI_hknpStaticCompoundShapeData = (void *)0x0382AA40; +const void * RTTI_hknpCompoundShapeInternalsKeyMask = (void *)0x0382AA78; +const void * RTTI_hknpStaticCompoundShapeKeyMask = (void *)0x0382AAB0; +const void * RTTI_hkGeometryUtils__IVertices = (void *)0x0382ABA8; +const void * RTTI_hkcdSimdTree = (void *)0x0382ABD8; +const void * RTTI_hkcdSimdTree__LeafCollector = (void *)0x0382AC00; +const void * RTTI_hknpCompressedMeshShapeData = (void *)0x0382AC38; +const void * RTTI_hknpCompressedMeshShapeInternals__KeyMask = (void *)0x0382AD10; +const void * RTTI_BSAnimationGraphManager = (void *)0x0382B028; +const void * RTTI_SubgraphAndOffsetDBData = (void *)0x0382B080; +const void * RTTI_AnimationClipDataSingleton = (void *)0x0382B0B0; +const void * RTTI_hkaSkeleton = (void *)0x0382B5D8; +const void * RTTI_hkbEventPayload = (void *)0x0382B600; +const void * RTTI_hkbStringEventPayload = (void *)0x0382B628; +const void * RTTI_hkbAnimationBindingSet = (void *)0x0382B688; +const void * RTTI_BShkbAnimationGraph = (void *)0x0382B6B8; +const void * RTTI_BSIRagdollDriver = (void *)0x0382B6E8; +const void * RTTI_BSAnimationGraphLoadScrapper = (void *)0x0382B880; +const void * RTTI_bhkIWorldStepListener = (void *)0x0382B8B8; +const void * RTTI_AnimationSpeedInformationDBData = (void *)0x0382BAE8; +const void * RTTI_hknpCompoundShape = (void *)0x0382BD18; +const void * RTTI_hknpDynamicCompoundShape = (void *)0x0382BD40; +const void * RTTI_hkcdSimdTree__BuildContext = (void *)0x0382BD70; +const void * RTTI_hknpExternMeshShapeData = (void *)0x0382BDA0; +const void * RTTI_hknpExternMeshShapeBuildContext = (void *)0x0382BDD0; +const void * RTTI_hknpCharacterContext = (void *)0x0382BE08; +const void * RTTI_IAnimationClipLoaderSingleton = (void *)0x0382BE38; +const void * RTTI_AnimationFileManagerSingleton = (void *)0x0382BE70; +const void * RTTI_AnimationStanceDataSingletonDBData = (void *)0x0382C0D0; +const void * RTTI_IAnimationStanceData = (void *)0x0382C2E8; +const void * RTTI_hkFileSystem__Iterator__Impl = (void *)0x0382C7D0; +const void * RTTI_hkErrStream = (void *)0x0382C878; +const void * RTTI_hknpScaledConvexShapeBase = (void *)0x0382C8C0; +const void * RTTI_hknpScaledConvexShape = (void *)0x0382C920; +const void * RTTI_hknpMotionPropertiesLibrary = (void *)0x0382C9A8; +const void * RTTI_AnimationSpeedContour = (void *)0x0382CCD8; +const void * RTTI_IndividualClipAnimationSpeedContour = (void *)0x0382CD28; +const void * RTTI_SpeedSampledAnimationSpeedContour = (void *)0x0382CD68; +const void * RTTI_CollectionAnimationSpeedContour = (void *)0x0382CDA0; +const void * RTTI_hkbBehaviorGraph__GlobalTransitionData = (void *)0x03827B90; +const void * RTTI_hkbSymbolIdMap = (void *)0x0382D3B8; +const void * RTTI_IBehaviorGraphSwapSingleton = (void *)0x0382D3E0; +const void * RTTI_IBehaviorGraphSymbolSwapSingleton = (void *)0x0382D418; +const void * RTTI_BSBehaviorGraphSwapSingleton = (void *)0x0382D450; +const void * RTTI_BShkbUtils__SearchAllStateMachineChildrenFunctor = (void *)0x0382DCC0; +const void * RTTI_IiStateTaggingListenerSingleton = (void *)0x0382DDB8; +const void * RTTI_BSiStateTaggingListenerSingleton = (void *)0x0382DDF0; +const void * RTTI_hkbRagdollDriver = (void *)0x0382E040; +const void * RTTI_BSIRagdollDriverSingleton = (void *)0x037B1450; +const void * RTTI_hkbAssetLoader = (void *)0x0382E0B8; +const void * RTTI_IStateMachineCallbackSingleton = (void *)0x0382E198; +const void * RTTI_BSStateMachineCallbackSingleton = (void *)0x0382E1D0; +const void * RTTI_BSSubBehaviorGraphSingletonData = (void *)0x0382E428; +const void * RTTI_BSSubBehaviorGraphSingletonDataAddToDeferredDeleterTask = (void *)0x0382EAA0; +const void * RTTI_BShkVisibilityController = (void *)0x0382F020; +const void * RTTI_hkbNode = (void *)0x0382F228; +const void * RTTI_hkbGenerator = (void *)0x0382F270; +const void * RTTI_hkbStateListener = (void *)0x0382F298; +const void * RTTI_BSiStateTaggingGenerator = (void *)0x0382F2C0; +const void * RTTI_BSiStateTaggingStateListener = (void *)0x0382F2F0; +const void * RTTI_BGSGamebryoSequenceGenerator = (void *)0x0382F328; +const void * RTTI_BSLimbIKModifierCastInfo = (void *)0x0374C208; +const void * RTTI_BSILimbIKModifierSingleton = (void *)0x0374C268; +const void * RTTI_hkaFootPlacementIkSolver = (void *)0x0382F360; +const void * RTTI_BSIAlignBoneModifierSingleton = (void *)0x0374CB08; +const void * RTTI_BSILookAtModifierSingleton = (void *)0x0374CDA0; +const void * RTTI_hknpCharacterStateManager = (void *)0x0382F390; +const void * RTTI_hkbFootIkDriver = (void *)0x0382F3E8; +const void * RTTI_hkbPoseMatchingGeneratorInternalState = (void *)0x0382F410; +const void * RTTI_hkbRagdollInterface = (void *)0x0382F490; +const void * RTTI_hkbnpSerializedRagdollInterface = (void *)0x0382F4C0; +const void * RTTI_hkbnpRagdollSkeletonRemapData = (void *)0x0382F4F8; +const void * RTTI_hkbnpRagdollScaledSkeleton = (void *)0x0382F530; +const void * RTTI_hkbnpRagdollInterface = (void *)0x0382F568; +const void * RTTI_BSIDirectAtModifierSingleton = (void *)0x037B1938; +const void * RTTI_BSIPassByTargetTriggerModifierSingleton = (void *)0x037B1BE0; +const void * RTTI_BSIRagdollContactListenerModifierSingleton = (void *)0x037B25B0; +const void * RTTI_hkbBehaviorGraph = (void *)0x0382F5D0; +const void * RTTI_hkbNodeInternalStateInfo = (void *)0x0382F5F8; +const void * RTTI_hkbVariableValueSet = (void *)0x0382F628; +const void * RTTI_hkBufferedStreamReader = (void *)0x0382F658; +const void * RTTI_hkSeekableStreamReader = (void *)0x0382F688; +const void * RTTI_hkResource = (void *)0x0382F6B8; +const void * RTTI_LOCALNAMESPACE__hkNativeResource = (void *)0x0382F6E0; +const void * RTTI_hkOstream = (void *)0x0382C838; +const void * RTTI_hkMemoryTrackStreamWriter = (void *)0x0382F7F8; +const void * RTTI_hkArrayStreamWriter = (void *)0x0382F828; +const void * RTTI_hkSignal0__Slot = (void *)0x0382F858; +const void * RTTI_hkTaskQueue = (void *)0x0382F980; +const void * RTTI_hkTaskQueue__EmptyTask = (void *)0x0382F9A8; +const void * RTTI_hkBlockStreamCommandWriter = (void *)0x0382F9E8; +const void * RTTI_ErrorCommandDispatcher = (void *)0x0382FA20; +const void * RTTI_hknpBroadPhaseConfig = (void *)0x0382FA50; +const void * RTTI_hknpDefaultBroadPhaseConfig = (void *)0x0382FA80; +const void * RTTI_hknpShapeTagCodec = (void *)0x0382FAB8; +const void * RTTI_hknpNullShapeTagCodec = (void *)0x0382FAE0; +const void * RTTI_hknpConstraintCollisionFilter = (void *)0x0382FB80; +const void * RTTI_hknpBodyQualityLibrary = (void *)0x0382FCE0; +const void * RTTI_hknpEventDispatcher = (void *)0x0382FD10; +const void * RTTI_hknpEventMergeAndDispatcher = (void *)0x0382FD40; +const void * RTTI_hknpSpaceSplitter = (void *)0x0382FD78; +const void * RTTI_hknpSingleCellSpaceSplitter = (void *)0x0382FDA0; +const void * RTTI_hknpGridSpaceSplitter = (void *)0x0382FDD8; +const void * RTTI_hknpDynamicSpaceSplitter = (void *)0x0382FE08; +const void * RTTI_hknpMultithreadedSimulation = (void *)0x0382FE38; +const void * RTTI_hknpNarrowPhaseTask = (void *)0x0382FE70; +const void * RTTI_hknpSurfaceVelocityModifier = (void *)0x0382FEA0; +const void * RTTI_hknpBroadPhase = (void *)0x0382FED8; +const void * RTTI_hknpHybridBroadPhase = (void *)0x0382FF00; +const void * RTTI_hkOArchive = (void *)0x038303F0; +const void * RTTI_hkPackfileWriter = (void *)0x03830428; +const void * RTTI_hkIstream = (void *)0x03830450; +const void * RTTI_hkDataWorld = (void *)0x03830490; +const void * RTTI_hkDataObjectImpl = (void *)0x038304B8; +const void * RTTI_hkDataArrayImpl = (void *)0x03830508; +const void * RTTI_hkDataClassImpl = (void *)0x03830530; +const void * RTTI_hkDataRefCounted = (void *)0x038304E0; +const void * RTTI_hkDataClassDict = (void *)0x03830558; +const void * RTTI_hkDataObjectDict = (void *)0x03830580; +const void * RTTI_hkDataWorldDict = (void *)0x038305A8; +const void * RTTI_VariableIntArrayImplementation = (void *)0x03830610; +const void * RTTI_ByteArrayImplementation = (void *)0x03830648; +const void * RTTI_ArrayOfTuplesImplementation = (void *)0x038306B0; +const void * RTTI_ArrayOfTuplesImplementation__View = (void *)0x038306E8; +const void * RTTI_RealArrayImplementation = (void *)0x03830720; +const void * RTTI_RealArrayView = (void *)0x03830788; +const void * RTTI_VecArrayImplementation = (void *)0x038307B0; +const void * RTTI_PointerArrayImplementation = (void *)0x038307E0; +const void * RTTI_CstringArrayImplementation = (void *)0x03830868; +const void * RTTI_ArrayArrayImplementation = (void *)0x038308D8; +const void * RTTI_StructArrayImplementation = (void *)0x03830958; +const void * RTTI_StructArrayImplementation__Object = (void *)0x03830988; +const void * RTTI_hkDataClassNative = (void *)0x038309C8; +const void * RTTI_hkDataWorldNative = (void *)0x038309F0; +const void * RTTI_hkDataObjectNative = (void *)0x03830A18; +const void * RTTI_hkDataArrayNative = (void *)0x03830A48; +const void * RTTI_hkSubStreamWriter = (void *)0x03830A78; +const void * RTTI_hkBinaryPackfileWriter = (void *)0x03830AA0; +const void * RTTI_hkBinaryTagfileReader = (void *)0x03830AD0; +const void * RTTI_hkVersionPatchManager = (void *)0x03830B20; +const void * RTTI_hkVersionPatchManager__ClassWrapper = (void *)0x03830B90; +const void * RTTI_hkDefaultClassWrapper = (void *)0x03830BD0; +const void * RTTI_hkSerializeDeprecated = (void *)0x03830C20; +const void * RTTI_hkMallocAllocator = (void *)0x03830CB0; +const void * RTTI_hkStaticClassNameRegistry = (void *)0x03830CF8; +const void * RTTI_hknpGlobals = (void *)0x03830D88; +const void * RTTI_hkgpConvexHull = (void *)0x03830F40; +const void * RTTI_hkcdSimdTree__IParallelBuild__IRunnable = (void *)0x03830FE8; +const void * RTTI_hkcdSimdTreeUtils__Build__EmptyRunnable = (void *)0x03831028; +const void * RTTI_hkcdSimdTreeUtils__Build__IRefit = (void *)0x03831068; +const void * RTTI_hkcdSimdTreeUtils__Build__Task = (void *)0x03831260; +const void * RTTI_hkbEventPayloadList = (void *)0x038314F0; +const void * RTTI_hkaSkeletonMapper = (void *)0x03831520; +const void * RTTI_hkbAnimationBindingWithTriggers = (void *)0x03831548; +const void * RTTI_hkbCharacterSetup = (void *)0x03831580; +const void * RTTI_hkbHandIkDriver = (void *)0x038315A8; +const void * RTTI_hkbHandle = (void *)0x038315D0; +const void * RTTI_hkbCharacter = (void *)0x038315F0; +const void * RTTI_hkbDockingDriver = (void *)0x03831618; +const void * RTTI_hkaBoneAttachment = (void *)0x03831640; +const void * RTTI_hknpArrayAction = (void *)0x038316C8; +const void * RTTI_hknpEaseConstraintsAction = (void *)0x038316F0; +const void * RTTI_hkHardwareInfo = (void *)0x03831BD0; +const void * RTTI_hkDummySingleton = (void *)0x03831C50; +const void * RTTI_hkStdioStreamReader = (void *)0x03831CB0; +const void * RTTI_hkWin32StreamWriter = (void *)0x03831CE0; +const void * RTTI_hkBufferedStreamWriter = (void *)0x03831D10; +const void * RTTI_hknpDecoratorShape = (void *)0x0382C8F0; +const void * RTTI_hkbBindable = (void *)0x0382F248; +const void * RTTI_hkbStateMachine = (void *)0x03831D48; +const void * RTTI_hkbStateMachine__TransitionInfoArray = (void *)0x03831D70; +const void * RTTI_hkbStateMachine__EventPropertyArray = (void *)0x03831DB0; +const void * RTTI_hkbStateMachine__StateInfo = (void *)0x03831DF0; +const void * RTTI_BSBehaviorGraphSwapGenerator = (void *)0x03831E20; +const void * RTTI_hkbReferencePoseGenerator = (void *)0x03831E58; +const void * RTTI_hkbBehaviorReferenceGenerator = (void *)0x03831E90; +const void * RTTI_hkaAnimationBinding = (void *)0x03831ED0; +const void * RTTI_hkbClipGenerator = (void *)0x03831F00; +const void * RTTI_hkbClipTriggerArray = (void *)0x03831F28; +const void * RTTI_hkbBehaviorGraphStringData = (void *)0x03831F98; +const void * RTTI_hkaAnimationContainer = (void *)0x038320D0; +const void * RTTI_hkbBindableCollector = (void *)0x03832678; +const void * RTTI_hkbBindable__CacheBindablesCollector = (void *)0x038326A8; +const void * RTTI_hkbNode__BoundVariablesCollector = (void *)0x038326E8; +const void * RTTI_hkbReferencedGeneratorSyncInfo = (void *)0x03832720; +const void * RTTI_hkbWorld = (void *)0x03832760; +const void * RTTI_hkbSceneModifier = (void *)0x03832780; +const void * RTTI_hkbFootIkSceneModifier = (void *)0x038327A8; +const void * RTTI_hkbHandIkSceneModifier = (void *)0x038327D8; +const void * RTTI_hkbHandIkFixupSceneModifier = (void *)0x03832808; +const void * RTTI_hkbDockingSceneModifier = (void *)0x03832840; +const void * RTTI_hkbBoneWeightArray = (void *)0x03832880; +const void * RTTI_hkbBlenderGeneratorChild = (void *)0x038328B0; +const void * RTTI_hkbBlenderGenerator = (void *)0x038328E0; +const void * RTTI_hkbPoseMatchingGenerator = (void *)0x03832CE8; +const void * RTTI_hkbTransitionEffect = (void *)0x03832D20; +const void * RTTI_hkbGeneratorTransitionEffect = (void *)0x03832D50; +const void * RTTI_hkbPhysicsInterface = (void *)0x03832D88; +const void * RTTI_hknpGroupCollisionFilter = (void *)0x03832E18; +const void * RTTI_hkbnpPhysicsInterface = (void *)0x03832F68; +const void * RTTI_hknpCharacterProxyCinfo = (void *)0x03832F98; +const void * RTTI_hknpCharacterRigidBodyCinfo = (void *)0x03832FC8; +const void * RTTI_hknpRagdollData = (void *)0x03833238; +const void * RTTI_hknpRagdoll = (void *)0x03833260; +const void * RTTI_hknpBreakableConstraintData = (void *)0x038332E0; +const void * RTTI_hkaDefaultAnimationControl = (void *)0x03833318; +const void * RTTI_hkaDefaultAnimationControlMapperData = (void *)0x03833380; +const void * RTTI_hkbProjectStringData = (void *)0x038338C8; +const void * RTTI_hkbProjectData = (void *)0x038338F8; +const void * RTTI_hkMemoryTrackStreamReader = (void *)0x03833A20; +const void * RTTI_hkStackTracer = (void *)0x03833AF8; +const void * RTTI_hkDebugDisplay = (void *)0x03833B40; +const void * RTTI_hkDebugDisplayHandler = (void *)0x03833BA0; +const void * RTTI_hkProcess = (void *)0x03833BD0; +const void * RTTI_hkDebugDisplayProcess = (void *)0x03833BF0; +const void * RTTI_hknpPairCollisionFilter = (void *)0x0382FBB8; +const void * RTTI_hknpSolverData = (void *)0x03833C20; +const void * RTTI_hknpSingleThreadedSolverData = (void *)0x03833C48; +const void * RTTI_hknpPrepareNarrowPhaseTask = (void *)0x03833C80; +const void * RTTI_hknpPrepare2ndRoundNarrowPhaseTask = (void *)0x03833CB8; +const void * RTTI_hknpPrepareConstraintsTask = (void *)0x03833CF8; +const void * RTTI_hknpBuildConstraintJacobiansTask = (void *)0x03833D30; +const void * RTTI_hknpSolverTask = (void *)0x03833D68; +const void * RTTI_hknpConvexConvexShapeBaseInterface = (void *)0x03833D90; +const void * RTTI_hknpHBPUpdateDirtyBodiesTask = (void *)0x03833DE0; +const void * RTTI_hknpHBPUpdateTreeTask = (void *)0x03833E18; +const void * RTTI_hknpHBPTreeVsTreeTask = (void *)0x03833E48; +const void * RTTI_hknpHBPFinishUpdateTask = (void *)0x03833E78; +const void * RTTI_hkDisplayGeometry = (void *)0x03834050; +const void * RTTI_hkDisplaySphere = (void *)0x03834078; +const void * RTTI_hkDisplayCapsule = (void *)0x038340A0; +const void * RTTI_hkDisplayConvex = (void *)0x038340C8; +const void * RTTI_hkDisplayAABB = (void *)0x038340F0; +const void * RTTI_hkDisplayWireframe = (void *)0x03834118; +const void * RTTI_hkpConstraintData = (void *)0x03833288; +const void * RTTI_hkpLimitedHingeConstraintData = (void *)0x03834148; +const void * RTTI_hkpFixedConstraintData = (void *)0x03834180; +const void * RTTI_hkpDeformableFixedConstraintData = (void *)0x038341B0; +const void * RTTI_hkpRagdollLimitsData = (void *)0x038341E8; +const void * RTTI_hkpHingeLimitsData = (void *)0x03834218; +const void * RTTI_hkpStiffSpringConstraintData = (void *)0x03834248; +const void * RTTI_hkpBallAndSocketConstraintData = (void *)0x03834280; +const void * RTTI_hkpWheelConstraintData = (void *)0x038342B8; +const void * RTTI_hkpHingeConstraintData = (void *)0x038342E8; +const void * RTTI_hkpPrismaticConstraintData = (void *)0x03834318; +const void * RTTI_hkpRagdollConstraintData = (void *)0x03834350; +const void * RTTI_hkpPointToPlaneConstraintData = (void *)0x03834380; +const void * RTTI_hkpPulleyConstraintData = (void *)0x038343B8; +const void * RTTI_hkpPointToPathConstraintData = (void *)0x038343E8; +const void * RTTI_hkOffsetOnlyStreamWriter = (void *)0x03834450; +const void * RTTI_hkObjectWriter = (void *)0x03834480; +const void * RTTI_hkPlatformObjectWriter = (void *)0x038344A8; +const void * RTTI_hkPlatformObjectWriter__Cache = (void *)0x038344D8; +const void * RTTI_hkMemoryStreamReader = (void *)0x038349B0; +const void * RTTI_hkTypeManager = (void *)0x038349E0; +const void * RTTI_hkPackfileData = (void *)0x03834A08; +const void * RTTI_hkObjectResource = (void *)0x03834A30; +const void * RTTI_hkIArchive = (void *)0x03834AB8; +const void * RTTI_hkParserBuffer = (void *)0x038381A0; +const void * RTTI_hkXmlStreamParser = (void *)0x038381C8; +const void * RTTI_hkXmlParser = (void *)0x038381F0; +const void * RTTI_hkXmlParser__StartElement = (void *)0x03838218; +const void * RTTI_hkXmlParser__EndElement = (void *)0x03838270; +const void * RTTI_hkXmlParser__Characters = (void *)0x038382A0; +const void * RTTI_hkXmlParser__Node = (void *)0x03838248; +const void * RTTI_hkgpMesh = (void *)0x038382D8; +const void * RTTI_hkgpMesh__IConvexOverlap = (void *)0x03838370; +const void * RTTI_hkgpMesh__IConvexOverlap__IConvexShape = (void *)0x038383A0; +const void * RTTI_hkgpMesh__TriangleShape = (void *)0x038383E0; +const void * RTTI_hkgpMesh__ExternShape = (void *)0x03838410; +const void * RTTI_hkgpTriangulatorBase = (void *)0x03838440; +const void * RTTI_hkgpJobQueue__IJob = (void *)0x038385A8; +const void * RTTI_IConvexOverlapImpl = (void *)0x03838758; +const void * RTTI_IConvexOverlapImpl__ShapeInterface = (void *)0x03838788; +const void * RTTI_hkgpIndexedMesh = (void *)0x03838818; +const void * RTTI_hkgpIndexedMesh__IVertexRemoval = (void *)0x03838840; +const void * RTTI_hkgpIndexedMesh__ITriangleRemoval = (void *)0x03838878; +const void * RTTI_hkgpIndexedMesh__IEdgeCollapse = (void *)0x038388B0; +const void * RTTI_hkgpIndexedMeshInternals__DefaultEdgeCollapseInterface = (void *)0x038388F0; +const void * RTTI_hkbRealEventPayload = (void *)0x03838960; +const void * RTTI_hkbIntEventPayload = (void *)0x03838990; +const void * RTTI_hkbNamedEventPayload = (void *)0x038389C0; +const void * RTTI_hkbNamedRealEventPayload = (void *)0x038389F0; +const void * RTTI_hkbNamedIntEventPayload = (void *)0x03838A20; +const void * RTTI_hkbNamedStringEventPayload = (void *)0x03838A50; +const void * RTTI_hkaMirroredSkeleton = (void *)0x03838A88; +const void * RTTI_hkSimpleLocalFrame = (void *)0x03838AE0; +const void * RTTI_hkbCharacterControllerDriver = (void *)0x03838B10; +const void * RTTI_hkbCharacterData = (void *)0x03838B48; +const void * RTTI_hkbProjectAssetManager = (void *)0x03838B78; +const void * RTTI_hkDefaultError = (void *)0x03839258; +const void * RTTI_hkSocket = (void *)0x038392A0; +const void * RTTI_hkSocket__ReaderAdapter = (void *)0x038392C0; +const void * RTTI_hkSocket__WriterAdapter = (void *)0x038392F0; +const void * RTTI_hkbStateMachineInternalState = (void *)0x03839320; +const void * RTTI_hkaAnimationControl = (void *)0x03833350; +const void * RTTI_hkbClipGeneratorInternalState = (void *)0x03839358; +const void * RTTI_hkaAnimation = (void *)0x03839390; +const void * RTTI_hkaReferencePoseAnimation = (void *)0x038393B8; +const void * RTTI_hkbBehaviorGraphData = (void *)0x038393F0; +const void * RTTI_hkbCondition = (void *)0x03839420; +const void * RTTI_hkbExpressionCondition = (void *)0x03839448; +const void * RTTI_hkbParametricMotionGenerator = (void *)0x03839478; +const void * RTTI_hkbAssetBundle = (void *)0x038395A8; +const void * RTTI_hkbFootIkDriverInfo = (void *)0x03839C00; +const void * RTTI_hkbCharacterStringData = (void *)0x03839C60; +const void * RTTI_hkbHandIkDriverInfo = (void *)0x0383A0C0; +const void * RTTI_hkbMirroredSkeletonInfo = (void *)0x0383A0F0; +const void * RTTI_hkbVariableBindingSet = (void *)0x0383A128; +const void * RTTI_hkbAttachmentSceneModifier = (void *)0x0383A158; +const void * RTTI_hkbAttachmentFixupSceneModifier = (void *)0x0383A190; +const void * RTTI_hkbRagdollSceneModifier = (void *)0x0383A1C8; +const void * RTTI_hkbCharacterControllerSceneModifier = (void *)0x0383A1F8; +const void * RTTI_hkbBlenderGeneratorInternalState = (void *)0x0383A238; +const void * RTTI_hkbGeneratorTransitionEffectInternalState = (void *)0x0383A390; +const void * RTTI_hkLocalFrameGroup = (void *)0x0383A3D0; +const void * RTTI_hkLocalFrame = (void *)0x03838AB8; +const void * RTTI_hkBlockStreamAllocator = (void *)0x0383A3F8; +const void * RTTI_hkFixedBlockStreamAllocator = (void *)0x0383A428; +const void * RTTI_hkbnpCharacterProxyController = (void *)0x0383A460; +const void * RTTI_hkbnpCharacterRigidBodyController = (void *)0x0383A4F8; +const void * RTTI_hkbRagdollController = (void *)0x0383A530; +const void * RTTI_hkbnpRagdollPoweredConstraintController = (void *)0x0383A560; +const void * RTTI_hkbnpRagdollRigidBodyController = (void *)0x0383A5A0; +const void * RTTI_hkVisualDebugger = (void *)0x0383A698; +const void * RTTI_hkpWrappedConstraintData = (void *)0x038332B0; +const void * RTTI_hkObjectCopier = (void *)0x0383AA30; +const void * RTTI_hkbSequencedData = (void *)0x0383AC78; +const void * RTTI_hkbEventSequencedData = (void *)0x0383ACA0; +const void * RTTI_hkbRealVariableSequencedData = (void *)0x0383ACD0; +const void * RTTI_hkbBoolVariableSequencedData = (void *)0x0383AD08; +const void * RTTI_hkbIntVariableSequencedData = (void *)0x0383AD40; +const void * RTTI_hkbSequenceStringData = (void *)0x0383AD78; +const void * RTTI_hkbModifier = (void *)0x0383ADA8; +const void * RTTI_hkbSequence = (void *)0x0383ADD0; +const void * RTTI_hkProcessFactory = (void *)0x0383AEA0; +const void * RTTI_hknpSolverSchedulerTask = (void *)0x0383AF00; +const void * RTTI_hkXmlLexAnalyzer = (void *)0x0383AFC0; +const void * RTTI_hkgpIndexedMesh__EdgeBarrier = (void *)0x0383B470; +const void * RTTI_hkgpCgoInternal__ICollapse = (void *)0x0383B4A8; +const void * RTTI_hkLocalFrameCollector = (void *)0x0383B528; +const void * RTTI_hkbClosestLocalFrameCollector = (void *)0x0383B558; +const void * RTTI_hkbInternal__hks__Visitor = (void *)0x0383D2D0; +const void * RTTI_hkbInternal__hks__MemoryAllocationsByType = (void *)0x0383D300; +const void * RTTI_hkbInternal__hks__netTransport = (void *)0x0383D340; +const void * RTTI_hkbInternal__hks__netTransportTCPIP = (void *)0x0383D378; +const void * RTTI_hkbInternal__LuaPlus__LuaStateOutFile = (void *)0x0383D3B0; +const void * RTTI_hkbInternal__hks__CompilerStateInterface = (void *)0x0383D3F0; +const void * RTTI_hkbInternal__hks__CompilerParseAcceptor = (void *)0x0383D430; +const void * RTTI_hkbInternal__hks__CodeGenerator = (void *)0x0383D470; +const void * RTTI_hkbInternal__hks__SimpleCompilerState = (void *)0x0383D518; +const void * RTTI_hkbInternal__hks__Preprocessor = (void *)0x0383D558; +const void * RTTI_hkbInternal__hks__Preprocessor__PreprocessorStateProxy = (void *)0x0383D590; +const void * RTTI_hkbInternal__hks__IProfiler = (void *)0x0383D5E0; +const void * RTTI_hkbInternal__hks__FrequencyProfiler = (void *)0x0383D610; +const void * RTTI_hkbInternal__hks__GettableProfiler = (void *)0x0383D648; +const void * RTTI_hkbInternal__hks__ChunkVisitor = (void *)0x0383D680; +const void * RTTI_hkbInternal__hks__ReachableChunksAreValid = (void *)0x0383D6B8; +const void * RTTI_hkbInternal__hks__HeapVisualizer = (void *)0x0383D6F8; +const void * RTTI_hkbScriptAssetLoader = (void *)0x0383D730; +const void * RTTI_hkBsdSocket = (void *)0x0383E200; +const void * RTTI_hkbCompiledExpressionSet = (void *)0x0383E3F8; +const void * RTTI_hkbProceduralBlenderGenerator = (void *)0x038394B0; +const void * RTTI_hkxAttributeHolder = (void *)0x0383E490; +const void * RTTI_hkxMesh__UserChannelInfo = (void *)0x0383E4C0; +const void * RTTI_hkaMeshBinding = (void *)0x0383E4F0; +const void * RTTI_hkbCharacterController = (void *)0x0383A4C8; +const void * RTTI_hkbnpCharacterController = (void *)0x0383A498; +const void * RTTI_hknpCharacterProxy = (void *)0x0383ED10; +const void * RTTI_hknpCharacterProxyInternals__QueryCollector = (void *)0x0383ED40; +const void * RTTI_hknpCharacterRigidBody = (void *)0x0383EE50; +const void * RTTI_hknpRagdollMotorController = (void *)0x0383F0E8; +const void * RTTI_hknpRagdollController = (void *)0x0383A668; +const void * RTTI_hknpRagdollKeyFrameHierarchyController = (void *)0x0383A628; +const void * RTTI_hkProcessHandler = (void *)0x0383F120; +const void * RTTI_hkServerProcessHandler = (void *)0x0383F148; +const void * RTTI_hkCommandRouter = (void *)0x0383F178; +const void * RTTI_hkbSequenceInternalState = (void *)0x0383F1B0; +const void * RTTI_hkMemorySystem = (void *)0x0383F1E0; +const void * RTTI_hkbDockingTarget = (void *)0x0383F208; +const void * RTTI_hkbLineDockingTarget = (void *)0x0383F230; +const void * RTTI_hkbPlaneDockingTarget = (void *)0x0383F260; +const void * RTTI_hkxMeshSection = (void *)0x0383F688; +const void * RTTI_hkxMesh = (void *)0x0383F6B0; +const void * RTTI_hkInspectProcess = (void *)0x0383F9D0; +const void * RTTI_hkMemorySnapshotProcess = (void *)0x0383F9F8; +const void * RTTI_hkRemoteObjectProcess = (void *)0x0383FA28; +const void * RTTI_hkStatisticsProcess = (void *)0x0383FA58; +const void * RTTI_hkDisplaySerializeOStream = (void *)0x0383FA88; +const void * RTTI_hkReplayStreamReader = (void *)0x0383FAB8; +const void * RTTI_hkServerDebugDisplayHandler = (void *)0x0383FAE8; +const void * RTTI_hkDisplaySerializeIStream = (void *)0x0383FB20; +const void * RTTI_hkxIndexBuffer = (void *)0x03840128; +const void * RTTI_hkxMaterial = (void *)0x03840528; +const void * RTTI_hkxVertexAnimation = (void *)0x038405F8; +const void * RTTI_hkVdbCommandWriter = (void *)0x03840688; +const void * RTTI_hkMonitorStreamColorTable = (void *)0x03840AA8; +const void * RTTI_hkDisplayCone = (void *)0x03840AD8; +const void * RTTI_hkDisplaySemiCircle = (void *)0x03840B00; +const void * RTTI_hkDisplayPlane = (void *)0x03840B30; +const void * RTTI_hkDisplayMesh = (void *)0x03840B58; +const void * RTTI_hkDisplayBox = (void *)0x03840B80; +const void * RTTI_hkDisplayCylinder = (void *)0x03840BA8; +const void * RTTI_hkDisplayTaperedCapsule = (void *)0x03840BD0; +const void * RTTI_hkxVertexBuffer = (void *)0x03840D90; +const void * RTTI_hkIndexedTransformSet = (void *)0x03840DB8; +const void * RTTI_hkxSparselyAnimatedBool = (void *)0x03840DE8; +const void * RTTI_hkxSparselyAnimatedInt = (void *)0x03840E18; +const void * RTTI_hkxEnum = (void *)0x03840E78; +const void * RTTI_hkxSparselyAnimatedEnum = (void *)0x03840E98; +const void * RTTI_hkxSparselyAnimatedString = (void *)0x03840EC8; +const void * RTTI_hkxAnimatedFloat = (void *)0x03840F50; +const void * RTTI_hkxAnimatedVector = (void *)0x03840FD0; +const void * RTTI_hkxAnimatedQuaternion = (void *)0x03840FF8; +const void * RTTI_hkxAnimatedMatrix = (void *)0x03841080; +const void * RTTI_hkTrackerScanSnapshot = (void *)0x038410E0; +const void * RTTI_hkTrackerTypeTreeCache = (void *)0x03841128; +const void * RTTI_hkTrackerArrayLayoutHandler = (void *)0x03841158; +const void * RTTI_hkTrackerPointerMapLayoutHandler = (void *)0x038411C0; +const void * RTTI_hkTrackerStringPtrLayoutHandler = (void *)0x038411F8; +const void * RTTI_hkTrackerStringMapLayoutHandler = (void *)0x03841230; +const void * RTTI_hkTrackerTypeLayout = (void *)0x03841268; +const void * RTTI_hkTrackerLayoutCalculator = (void *)0x03841298; +const void * RTTI_hkTrackerLayoutHandler = (void *)0x03841190; +const void * RTTI_hkTrackerRefPtrLayoutHandler = (void *)0x038412C8; +const void * RTTI_hkTrackerQueueLayoutHandler = (void *)0x03841300; +const void * RTTI_hkTrackerPadSpuLayoutHandler = (void *)0x03841338; +const void * RTTI_hkTrackerJobQueueLayoutHandler = (void *)0x03841370; +const void * RTTI_hkTrackerJobQueueDynamicDataLayoutHandler = (void *)0x038413B0; +const void * RTTI_hkTrackerSetLayoutHandler = (void *)0x038413F0; +const void * RTTI_hkTrackerFlagsLayoutHandler = (void *)0x03841420; +const void * RTTI_hkTrackerEnumLayoutHandler = (void *)0x03841458; +const void * RTTI_hkMeshBody = (void *)0x038419D0; +const void * RTTI_hkMemoryMeshBody = (void *)0x038419F8; +const void * RTTI_hkMemoryTracker = (void *)0x03841A20; +const void * RTTI_hkDummyMemoryTrackerImpl = (void *)0x03841A48; +const void * RTTI_hkDefaultMemoryTracker = (void *)0x03841A78; +const void * RTTI_AudioLoadForPlaybackTask = (void *)0x03841BE8; +const void * RTTI_AudioLoadToCacheTask = (void *)0x03841C50; +const void * RTTI_BSAudioManagerThread = (void *)0x03841C80; +const void * RTTI_BSNullImplAudio = (void *)0x03841CB0; +const void * RTTI_BSIAudioEffectParameters = (void *)0x03841D38; +const void * RTTI_BSAudioEffects__BSOverdriveParams = (void *)0x03841D68; +const void * RTTI_BSAudioEffects__BSStateVariableFilterParams = (void *)0x03841DA0; +const void * RTTI_BSAudioEffects__BSDelayEffectParams = (void *)0x03841DE8; +const void * RTTI_BSAudio = (void *)0x03841CD8; +const void * RTTI_BSGameSound = (void *)0x03842410; +const void * RTTI_BSXAudio2Audio = (void *)0x038429C0; +const void * RTTI_BSAudioListener = (void *)0x03842C48; +const void * RTTI_BSNullImplAudioListener = (void *)0x03842C70; +const void * RTTI_BSNullImplGameSound = (void *)0x03842CA0; +const void * RTTI_BSXAudio2AudioListener = (void *)0x03842CD8; +const void * RTTI_BSAudioDataSrcBase = (void *)0x03842DB8; +const void * RTTI_BSXAudio2DataSrc = (void *)0x03842E48; +const void * RTTI_BSXAudio2GameSound = (void *)0x03842F50; +const void * RTTI_BSIAudioEffectVisitorBase = (void *)0x03842F80; +const void * RTTI_BSCXAPOWrapper = (void *)0x03843D18; +const void * RTTI_BSCompanionIListener = (void *)0x03843DF0; +const void * RTTI_BSCompanionThread = (void *)0x03843E20; +const void * RTTI_BSCompanionAutodiscover = (void *)0x03843E48; +const void * RTTI_BSByteBuffer = (void *)0x03844068; +const void * RTTI_BSCompanionConnection = (void *)0x038440E0; +const void * RTTI_BSCompanionDirectListener = (void *)0x03844308; +const void * RTTI_BSCompanionDirectConnection = (void *)0x03844338; +const void * RTTI_BSCompanionSocket = (void *)0x03844370; +const void * RTTI_ScrapHeap = (void *)0x038457E0; +const void * RTTI_BSThread = (void *)0x036D4628; +const void * RTTI_CompactingStore__Store = (void *)0x03845990; +const void * RTTI_CompactingStore__MoveCallback = (void *)0x038459C0; +const void * RTTI_CompactingStore__NoopMoveCallback = (void *)0x038459F8; +const void * RTTI_MemoryHeap = (void *)0x03845C60; +const void * RTTI_UnitTestMemoryHeap = (void *)0x03845CD8; +const void * RTTI_IMemoryHeap = (void *)0x03845CB0; +const void * RTTI_ZeroOverheadHeap = (void *)0x03845D08; +const void * RTTI_UnitTestZeroOverheadHeap = (void *)0x03845D30; +const void * RTTI_BSTGlobalEvent = (void *)0x03845D60; +const void * RTTI_BSSmallBlockAllocator = (void *)0x03845F50; +const void * RTTI_BSSmallBlockAllocatorUtil__UserPoolBase = (void *)0x03845F80; +const void * RTTI_IMemoryStoreBase = (void *)0x03845828; +const void * RTTI_IMemoryStore = (void *)0x03845800; +const void * RTTI_AbstractHeap = (void *)0x03845C88; +const void * RTTI_MouseMoveEvent = (void *)0x03846440; +const void * RTTI_CursorMoveEvent = (void *)0x03846468; +const void * RTTI_ThumbstickEvent = (void *)0x03846490; +const void * RTTI_CharacterEvent = (void *)0x038464B8; +const void * RTTI_DeviceConnectEvent = (void *)0x038464E0; +const void * RTTI_KinectEvent = (void *)0x03846510; +const void * RTTI_BSInputDevice = (void *)0x03846618; +const void * RTTI_BSGamepadDevice = (void *)0x03846640; +const void * RTTI_BSPCKeyboardDevice = (void *)0x03846690; +const void * RTTI_BSPCMouseDevice = (void *)0x038466E8; +const void * RTTI_BSKeyboardDevice = (void *)0x038466C0; +const void * RTTI_BSPCVirtualKeyboardDevice = (void *)0x03846738; +const void * RTTI_BSPCGamepadDeviceDelegate = (void *)0x03846798; +const void * RTTI_BSPCGamepadDeviceHandler = (void *)0x038467C8; +const void * RTTI_BSPCGamepadDevice = (void *)0x038467F8; +const void * RTTI_BSPCOrbisGamepadDevice = (void *)0x03846820; +const void * RTTI_BSMouseDevice = (void *)0x03846710; +const void * RTTI_BSVirtualKeyboardDevice = (void *)0x03846768; +const void * RTTI_NiFile = (void *)0x03846870; +const void * RTTI_NiBinaryStream = (void *)0x036F1A10; +const void * RTTI_NiAllocator = (void *)0x03846890; +const void * RTTI_BSNiAllocator = (void *)0x038468B8; +const void * RTTI_NiStandardAllocator = (void *)0x038468E0; +const void * RTTI_NiSearchPath = (void *)0x03846910; +const void * RTTI_NiMemStream = (void *)0x03846938; +const void * RTTI_BSStringPool__IterationCallback = (void *)0x03846988; +const void * RTTI_BSSystemFileAsyncFunctor = (void *)0x038469C0; +const void * RTTI_BSSystemFileStreamer__Streamer = (void *)0x038469F8; +const void * RTTI_BSSystemFileStreamer__Streamer__StreamerThread = (void *)0x03846A80; +const void * RTTI_BSSystemFileStreamer__Streamer__ControlThread = (void *)0x03846AD0; +const void * RTTI_BSSystemFileStreamer__Streamer__PhysicalReadThread = (void *)0x03846B20; +const void * RTTI_BSJobs__JobThread = (void *)0x03846D00; +const void * RTTI_BSFile = (void *)0x03846D30; +const void * RTTI_BSFile__PageCache__PhysReadType = (void *)0x03846D50; +const void * RTTI_Archive = (void *)0x03846EB8; +const void * RTTI_ArchiveFile = (void *)0x03846F20; +const void * RTTI_CompressedArchiveFile = (void *)0x03846F48; +const void * RTTI_BSTaskJobber = (void *)0x03846F78; +const void * RTTI_BSTaskJobber__Task = (void *)0x037444E8; +const void * RTTI_BSSystemFileStorage = (void *)0x03846FA0; +const void * RTTI_BSSocket = (void *)0x03844398; +const void * RTTI_BSSearchPath = (void *)0x03846FD0; +const void * RTTI_BSListenSocket = (void *)0x03847010; +const void * RTTI_BSTaskletManager = (void *)0x038470E8; +const void * RTTI_BSWin32TaskletManager = (void *)0x03847110; +const void * RTTI_BSTaskletGroupData = (void *)0x03847140; +const void * RTTI_BSTasklet = (void *)0x03847170; +const void * RTTI_BSTaskletData = (void *)0x03847190; +const void * RTTI_BSTaskletManagerCallback = (void *)0x038471B8; +const void * RTTI_BSWin32TaskletGroupData = (void *)0x038471E8; +const void * RTTI_BSResource__IEntryDB = (void *)0x036DA0F0; +const void * RTTI_BSResource__IEntryDB__PostFlushNotify = (void *)0x036CFFF8; +const void * RTTI_BSResource__Stream = (void *)0x03847218; +const void * RTTI_BSResource__AsyncStream = (void *)0x03847270; +const void * RTTI_BSResource__StreamBase = (void *)0x03847240; +const void * RTTI_BSResource__Archive2__ReaderStream = (void *)0x038472A0; +const void * RTTI_BSResource__Archive2__Index = (void *)0x03847328; +const void * RTTI_BSResource__Archive2__Manager__MemoryCacheLocation = (void *)0x03847360; +const void * RTTI_BSResource__Archive2__AsyncReaderStream = (void *)0x038473E0; +const void * RTTI_BSResource__SDirectory2__ThreadCursor = (void *)0x03847420; +const void * RTTI_BSResource__LocationTraverser = (void *)0x03847688; +const void * RTTI_BSResource__CacheDrive = (void *)0x038486A8; +const void * RTTI_BSResource__ICacheDriveOp = (void *)0x0378A3F0; +const void * RTTI_BSResource__CacheDrive__Impl = (void *)0x038486D8; +const void * RTTI_BSResource__CacheDrive__Task = (void *)0x03848710; +const void * RTTI_BSResource__MemoryFileLocation = (void *)0x038473A8; +const void * RTTI_BSResource__MemoryFileStream = (void *)0x03848758; +const void * RTTI_BSResourceNiBinaryStream = (void *)0x03848840; +const void * RTTI_NiObject = (void *)0x036CDB00; +const void * RTTI_NiExtraData = (void *)0x036CDB20; +const void * RTTI_NiObjectNET = (void *)0x036D89C8; +const void * RTTI_NiNode = (void *)0x036D8A40; +const void * RTTI_BSGeometrySegmentData = (void *)0x03848A20; +const void * RTTI_IRendererResourceManager = (void *)0x03848A50; +const void * RTTI_BSFlattenedBoneTree = (void *)0x03848AA0; +const void * RTTI_NiAVObject = (void *)0x036D89F0; +const void * RTTI_NiTexture = (void *)0x03848B18; +const void * RTTI_NiTimeController = (void *)0x036D6048; +const void * RTTI_NiSwitchNode = (void *)0x03848BA0; +const void * RTTI_NiPointLight = (void *)0x036F1260; +const void * RTTI_NiStringExtraData = (void *)0x03848D30; +const void * RTTI_BSPositionData = (void *)0x03848D60; +const void * RTTI_NiCamera = (void *)0x03848DA8; +const void * RTTI_NiGeometryData = (void *)0x03848DE8; +const void * RTTI_BSGeometry = (void *)0x03848E18; +const void * RTTI_BSSkin__BoneData = (void *)0x03848E58; +const void * RTTI_BSSkin__Instance = (void *)0x03848E80; +const void * RTTI_NiIntegerExtraData = (void *)0x03848EA8; +const void * RTTI_NiLight = (void *)0x03848BD0; +const void * RTTI_BSTriShape = (void *)0x03743A68; +const void * RTTI_BSDynPosData = (void *)0x03848EE8; +const void * RTTI_NiParticles = (void *)0x03848F18; +const void * RTTI_NiStream = (void *)0x036F11E8; +const void * RTTI_NiAVObjectPalette = (void *)0x03849668; +const void * RTTI_NiDefaultAVObjectPalette = (void *)0x03849690; +const void * RTTI_NiDirectionalLight = (void *)0x038496C8; +const void * RTTI_BSDynamicTriShape = (void *)0x03849700; +const void * RTTI_NiIntegersExtraData = (void *)0x03849728; +const void * RTTI_NiCullingProcess = (void *)0x03734498; +const void * RTTI_BSSegmentedTriShape = (void *)0x03849768; +const void * RTTI_NiGeometry = (void *)0x038497E8; +const void * RTTI_NiTriBasedGeom = (void *)0x038497C0; +const void * RTTI_NiTriShape = (void *)0x03849798; +const void * RTTI_NiSkinPartition = (void *)0x03849880; +const void * RTTI_NiSkinData = (void *)0x038498B0; +const void * RTTI_NiSkinInstance = (void *)0x038498E0; +const void * RTTI_NiShadeProperty = (void *)0x03849908; +const void * RTTI_NiAlphaAccumulator = (void *)0x03849960; +const void * RTTI_NiAmbientLight = (void *)0x038499C8; +const void * RTTI_NiBinaryExtraData = (void *)0x038499F0; +const void * RTTI_NiBooleanExtraData = (void *)0x03849A18; +const void * RTTI_NiBSPNode = (void *)0x03849A50; +const void * RTTI_NiColorExtraData = (void *)0x03849A70; +const void * RTTI_NiFloatExtraData = (void *)0x03849A98; +const void * RTTI_NiFloatsExtraData = (void *)0x03849AC0; +const void * RTTI_NiFogProperty = (void *)0x03849AE8; +const void * RTTI_NiParticlesData = (void *)0x03849B18; +const void * RTTI_NiParticleMeshesData = (void *)0x03849B48; +const void * RTTI_NiParticleMeshes = (void *)0x03849B80; +const void * RTTI_NiSpotLight = (void *)0x03849BB0; +const void * RTTI_NiStringsExtraData = (void *)0x03849BD8; +const void * RTTI_NiSwitchStringExtraData = (void *)0x03849C08; +const void * RTTI_NiTriShapeData = (void *)0x03849C40; +const void * RTTI_NiTriStripsData = (void *)0x03849CA0; +const void * RTTI_NiTriStrips = (void *)0x03849CD0; +const void * RTTI_NiVectorExtraData = (void *)0x03849D00; +const void * RTTI_BSLODTriShape = (void *)0x03849D38; +const void * RTTI_NiAdditionalGeometryData = (void *)0x03849D68; +const void * RTTI_NiAccumulator = (void *)0x03849938; +const void * RTTI_NiBackToFrontAccumulator = (void *)0x03849990; +const void * RTTI_NiTriBasedGeomData = (void *)0x03849C68; +const void * RTTI_NiPick__Results = (void *)0x03849DC8; +const void * RTTI_NiCollisionTraversals__IFindIntersections = (void *)0x03849EA8; +const void * RTTI_NiCollisionData = (void *)0x03849EE8; +const void * RTTI_NiBoundingVolume = (void *)0x03849F38; +const void * RTTI_NiBoxBV = (void *)0x03849F60; +const void * RTTI_NiCapsuleBV = (void *)0x03849F80; +const void * RTTI_NiHalfSpaceBV = (void *)0x03849FA8; +const void * RTTI_NiSphereBV = (void *)0x03849FD0; +const void * RTTI_NiUnionBV = (void *)0x0384A068; +const void * RTTI_NiOBBRoot = (void *)0x0384A130; +const void * RTTI_NiIntersector = (void *)0x0384A158; +const void * RTTI_NiBoxSphereIntersector = (void *)0x0384A180; +const void * RTTI_NiBoxCapsuleIntersector = (void *)0x0384A1B0; +const void * RTTI_NiCapsuleCapsuleIntersector = (void *)0x0384A1E0; +const void * RTTI_NiCapsuleSphereIntersector = (void *)0x0384A218; +const void * RTTI_NiCapsuleTriIntersector = (void *)0x0384A250; +const void * RTTI_NiSphereTriIntersector = (void *)0x0384A280; +const void * RTTI_NiOBBNode = (void *)0x0384A2B0; +const void * RTTI_NiOBBLeaf = (void *)0x0384A2D0; +const void * RTTI_NiControllerManager = (void *)0x0384A300; +const void * RTTI_NiControllerSequence = (void *)0x0384A420; +const void * RTTI_NiTextKeyMatch = (void *)0x0384A450; +const void * RTTI_NiMultiTargetTransformController = (void *)0x0384A478; +const void * RTTI_BSMultiTargetTreadTransfController = (void *)0x0384A4E8; +const void * RTTI_NiInterpController = (void *)0x0384A4B0; +const void * RTTI_NiFloatInterpolator = (void *)0x0384A558; +const void * RTTI_NiColorInterpolator = (void *)0x0384A5E8; +const void * RTTI_NiSingleInterpController = (void *)0x0384A620; +const void * RTTI_NiTransformInterpolator = (void *)0x0384A658; +const void * RTTI_NiPosData = (void *)0x0384A6C0; +const void * RTTI_NiPathInterpolator = (void *)0x0384A6E0; +const void * RTTI_NiBlendTransformInterpolator = (void *)0x0384A718; +const void * RTTI_NiBlendFloatInterpolator = (void *)0x0384A788; +const void * RTTI_NiFloatExtraDataController = (void *)0x0384A7C0; +const void * RTTI_NiVisController = (void *)0x0384A830; +const void * RTTI_NiTransformController = (void *)0x0384A890; +const void * RTTI_NiBlendAccumTransformInterpolator = (void *)0x0384A8C0; +const void * RTTI_NiBlendInterpolator = (void *)0x0384A750; +const void * RTTI_BSBlendTreadTransfInterpolator = (void *)0x0384AA20; +const void * RTTI_NiInterpolator = (void *)0x0384A5B8; +const void * RTTI_NiKeyBasedInterpolator = (void *)0x0384A588; +const void * RTTI_NiTransformData = (void *)0x0384AB18; +const void * RTTI_NiBlendBoolInterpolator = (void *)0x0384AB58; +const void * RTTI_NiBlendColorInterpolator = (void *)0x0384AB90; +const void * RTTI_NiBlendPoint3Interpolator = (void *)0x0384ABC8; +const void * RTTI_NiBlendQuaternionInterpolator = (void *)0x0384AC00; +const void * RTTI_NiBoolData = (void *)0x0384AC38; +const void * RTTI_NiBoolInterpolator = (void *)0x0384AC68; +const void * RTTI_NiBoolTimelineInterpolator = (void *)0x0384ACA0; +const void * RTTI_NiBSplineBasisData = (void *)0x0384ACD8; +const void * RTTI_NiBSplineData = (void *)0x0384AD08; +const void * RTTI_NiBSplineColorInterpolator = (void *)0x0384AD38; +const void * RTTI_NiBSplineCompColorInterpolator = (void *)0x0384ADA8; +const void * RTTI_NiBSplineCompFloatInterpolator = (void *)0x0384ADE8; +const void * RTTI_NiBSplineCompPoint3Interpolator = (void *)0x0384AE60; +const void * RTTI_NiBSplineCompTransformInterpolator = (void *)0x0384AED8; +const void * RTTI_NiBSplineFloatInterpolator = (void *)0x0384AE20; +const void * RTTI_NiBSplinePoint3Interpolator = (void *)0x0384AE98; +const void * RTTI_NiBSplineTransformInterpolator = (void *)0x0384AF18; +const void * RTTI_NiColorExtraDataController = (void *)0x0384AF70; +const void * RTTI_NiFloatsExtraDataController = (void *)0x0384AFB0; +const void * RTTI_NiFloatsExtraDataPoint3Controller = (void *)0x0384AFF0; +const void * RTTI_NiKeyframeManager = (void *)0x0384B030; +const void * RTTI_NiLightColorController = (void *)0x0384B1E8; +const void * RTTI_NiLightDimmerController = (void *)0x0384B250; +const void * RTTI_NiLightRadiusController = (void *)0x0384B2B8; +const void * RTTI_NiLookAtController = (void *)0x0384B2F0; +const void * RTTI_NiLookAtInterpolator = (void *)0x0384B328; +const void * RTTI_NiMorphData = (void *)0x0384B360; +const void * RTTI_NiPathController = (void *)0x0384B3C0; +const void * RTTI_NiPoint3Interpolator = (void *)0x0384B3F0; +const void * RTTI_NiQuaternionInterpolator = (void *)0x0384B428; +const void * RTTI_NiFloatController = (void *)0x0384B460; +const void * RTTI_NiRollController = (void *)0x0384B488; +const void * RTTI_NiRotData = (void *)0x0384B4B8; +const void * RTTI_NiSequence = (void *)0x0384B4E0; +const void * RTTI_NiSequenceStreamHelper = (void *)0x0384B668; +const void * RTTI_NiStringPalette = (void *)0x0384B698; +const void * RTTI_NiTextKeyExtraData = (void *)0x0384B6C0; +const void * RTTI_NiUVData = (void *)0x0384B6F0; +const void * RTTI_BSAnimNotes = (void *)0x0384B710; +const void * RTTI_BSAnimNote = (void *)0x0384B738; +const void * RTTI_BSGrabIKNote = (void *)0x0384B760; +const void * RTTI_BSLookIKNote = (void *)0x0384B788; +const void * RTTI_BSRotAccumTransfInterpolator = (void *)0x0384B7B8; +const void * RTTI_BSTreadTransfInterpolator = (void *)0x0384B7F8; +const void * RTTI_BSFrustumFOVController = (void *)0x0384B830; +const void * RTTI_NiExtraDataController = (void *)0x0384A7F8; +const void * RTTI_NiBoolInterpController = (void *)0x0384A858; +const void * RTTI_NiBSplineInterpolator = (void *)0x0384AD70; +const void * RTTI_NiPoint3InterpController = (void *)0x0384B218; +const void * RTTI_NiFloatInterpController = (void *)0x0384B280; +const void * RTTI_NiParticleSystem = (void *)0x0384B940; +const void * RTTI_NiPSysMeshEmitter = (void *)0x0384B970; +const void * RTTI_BSPSysInheritVelocityModifier = (void *)0x0384BB78; +const void * RTTI_NiPSysEmitterCtlr = (void *)0x0384BBD0; +const void * RTTI_NiPSysGravityModifier = (void *)0x0384BC30; +const void * RTTI_BSPSysHavokUpdateModifier = (void *)0x0384BC68; +const void * RTTI_NiMeshParticleSystem = (void *)0x0384BCD0; +const void * RTTI_NiPSysCylinderEmitter = (void *)0x0384BD10; +const void * RTTI_BSStripParticleSystem = (void *)0x0384BD78; +const void * RTTI_NiPSysEmitter = (void *)0x0384B998; +const void * RTTI_NiPSysData = (void *)0x0384BDC0; +const void * RTTI_NiPSysModifierCtlr = (void *)0x0384BBF8; +const void * RTTI_NiPSysModifier = (void *)0x0384B9C0; +const void * RTTI_NiPSysMeshUpdateModifier = (void *)0x0384BC98; +const void * RTTI_NiMeshPSysData = (void *)0x0384BE08; +const void * RTTI_NiPSysUpdateCtlr = (void *)0x0384BF38; +const void * RTTI_NiPSysAirFieldAirFrictionCtlr = (void *)0x0384BF68; +const void * RTTI_NiPSysAirFieldInheritVelocityCtlr = (void *)0x0384BFD8; +const void * RTTI_NiPSysAirFieldModifier = (void *)0x0384C028; +const void * RTTI_NiPSysAirFieldSpreadCtlr = (void *)0x0384C090; +const void * RTTI_NiPSysAgeDeathModifier = (void *)0x0384C0C8; +const void * RTTI_NiPSysBombModifier = (void *)0x0384C100; +const void * RTTI_NiPSysBoundUpdateModifier = (void *)0x0384C138; +const void * RTTI_NiPSysBoxEmitter = (void *)0x0384C170; +const void * RTTI_NiPSysColliderManager = (void *)0x0384C1A0; +const void * RTTI_NiPSysColorModifier = (void *)0x0384C1D8; +const void * RTTI_NiPSysDragFieldModifier = (void *)0x0384C210; +const void * RTTI_NiPSysDragModifier = (void *)0x0384C248; +const void * RTTI_NiPSysEmitterCtlrData = (void *)0x0384C278; +const void * RTTI_NiPSysEmitterDeclinationCtlr = (void *)0x0384C2B0; +const void * RTTI_NiPSysEmitterDeclinationVarCtlr = (void *)0x0384C2F0; +const void * RTTI_NiPSysEmitterInitialRadiusCtlr = (void *)0x0384C330; +const void * RTTI_NiPSysEmitterLifeSpanCtlr = (void *)0x0384C370; +const void * RTTI_NiPSysEmitterPlanarAngleCtlr = (void *)0x0384C3A8; +const void * RTTI_NiPSysEmitterPlanarAngleVarCtlr = (void *)0x0384C3E8; +const void * RTTI_NiPSysEmitterSpeedCtlr = (void *)0x0384C428; +const void * RTTI_NiPSysFieldAttenuationCtlr = (void *)0x0384C460; +const void * RTTI_NiPSysFieldMagnitudeCtlr = (void *)0x0384C4A0; +const void * RTTI_NiPSysFieldMaxDistanceCtlr = (void *)0x0384C4D8; +const void * RTTI_NiPSysGravityFieldModifier = (void *)0x0384C518; +const void * RTTI_NiPSysGravityStrengthCtlr = (void *)0x0384C558; +const void * RTTI_NiPSysGrowFadeModifier = (void *)0x0384C590; +const void * RTTI_NiPSysInitialRotAngleCtlr = (void *)0x0384C5C8; +const void * RTTI_NiPSysInitialRotAngleVarCtlr = (void *)0x0384C600; +const void * RTTI_NiPSysInitialRotSpeedCtlr = (void *)0x0384C640; +const void * RTTI_NiPSysInitialRotSpeedVarCtlr = (void *)0x0384C678; +const void * RTTI_NiPSysModifierActiveCtlr = (void *)0x0384C6B8; +const void * RTTI_NiPSysPlanarCollider = (void *)0x0384C720; +const void * RTTI_NiPSysPositionModifier = (void *)0x0384C780; +const void * RTTI_NiPSysRadialFieldModifier = (void *)0x0384C7B8; +const void * RTTI_NiPSysResetOnLoopCtlr = (void *)0x0384C7F0; +const void * RTTI_NiPSysRotationModifier = (void *)0x0384C828; +const void * RTTI_NiPSysSpawnModifier = (void *)0x0384C860; +const void * RTTI_NiPSysSphereEmitter = (void *)0x0384C898; +const void * RTTI_NiPSysSphericalCollider = (void *)0x0384C8D0; +const void * RTTI_NiPSysTurbulenceFieldModifier = (void *)0x0384C908; +const void * RTTI_NiPSysVortexFieldModifier = (void *)0x0384C948; +const void * RTTI_BSStripPSysData = (void *)0x0384C980; +const void * RTTI_BSPSysRecycleBoundModifier = (void *)0x0384C9B0; +const void * RTTI_NiPSysVolumeEmitter = (void *)0x0384BD40; +const void * RTTI_NiPSysModifierFloatCtlr = (void *)0x0384BFA0; +const void * RTTI_NiPSysFieldModifier = (void *)0x0384C058; +const void * RTTI_NiPSysModifierBoolCtlr = (void *)0x0384C6E8; +const void * RTTI_NiPSysCollider = (void *)0x0384C750; +const void * RTTI_BSStaticTriShapeDB__QueuedHandles = (void *)0x0384CA18; +const void * RTTI_BSStaticTriShapeDB__Builder = (void *)0x0384CE70; +const void * RTTI_BSStaticTriShapeDB__Manager = (void *)0x0384CEA8; +const void * RTTI_BSStaticTriShapeDB__CsgStream = (void *)0x0384D0D8; +const void * RTTI_ITextureDB = (void *)0x0384D4A0; +const void * RTTI_BSTextureDB__QueuedHandles = (void *)0x036D4148; +const void * RTTI_BSMaterialDB__QueuedHandles = (void *)0x0384DC28; +const void * RTTI_BSTaskThread = (void *)0x036D4600; +const void * RTTI_IOManager = (void *)0x0384DD50; +const void * RTTI_IOManagerThread = (void *)0x0384DE10; +const void * RTTI_IOTask = (void *)0x036CFFA0; +const void * RTTI_IOSystemTask = (void *)0x036E8738; +const void * RTTI_QueuedFile = (void *)0x036CFF78; +const void * RTTI_QueuedNamedFile = (void *)0x0384DE38; +const void * RTTI_BSQueuedResourceCollectionBase = (void *)0x036CFFC0; +const void * RTTI_BSMultiBound = (void *)0x0384DE70; +const void * RTTI_BSMultiBoundRoom = (void *)0x0384DE98; +const void * RTTI_BSXFlags = (void *)0x0384DEE8; +const void * RTTI_BSValueNode = (void *)0x0384DF08; +const void * RTTI_BSTextureStreamer__TextureModifyBatch = (void *)0x0384DF30; +const void * RTTI_BSTextureStreamer__Manager = (void *)0x0384DF70; +const void * RTTI_BSTextureStreamer__StreamDetailI = (void *)0x0384E198; +const void * RTTI_BSTextureStreamer__zlibStreamDetail = (void *)0x0384E1D0; +const void * RTTI_BSTextureStreamer__TextureModifyBatchNoLimit = (void *)0x0384E210; +const void * RTTI_BSWindModifier = (void *)0x0384E260; +const void * RTTI_BSPortalGraph = (void *)0x0384E290; +const void * RTTI_BSPortalGraphEntry = (void *)0x0384E2B8; +const void * RTTI_BSOcclusionShape = (void *)0x036D9710; +const void * RTTI_BSRangeNode = (void *)0x0384E2E8; +const void * RTTI_BSBlastNode = (void *)0x0384E310; +const void * RTTI_BSDebrisNode = (void *)0x0384E338; +const void * RTTI_BSDamageStage = (void *)0x0384E360; +const void * RTTI_PArrayPoint = (void *)0x0384E3B8; +const void * RTTI_BSPSysArrayEmitter = (void *)0x0384E3E0; +const void * RTTI_BSMultiBoundShape = (void *)0x036D8898; +const void * RTTI_BSInstanceTriShape = (void *)0x0384E528; +const void * RTTI_BSMultiStreamInstanceTriShape = (void *)0x0384E558; +const void * RTTI_BSIStream = (void *)0x0384E590; +const void * RTTI_BSOStream = (void *)0x0384E620; +const void * RTTI_BSFurnitureMarkerNode = (void *)0x0384E688; +const void * RTTI_BSOcclusionBox = (void *)0x0384E6B8; +const void * RTTI_BSOcclusionPlane = (void *)0x036D96E8; +const void * RTTI_BSPortalSharedNode = (void *)0x0384E710; +const void * RTTI_BSBehaviorGraphExtraData = (void *)0x0384E740; +const void * RTTI_BSCullingProcess = (void *)0x0384E798; +const void * RTTI_BSNiNode = (void *)0x036DE260; +const void * RTTI_BSSubIndexTriShape = (void *)0x0384E7C0; +const void * RTTI_BSSegmentDataStorage = (void *)0x0384E7F0; +const void * RTTI_BSParticleSystemManager = (void *)0x0384E820; +const void * RTTI_BSBound = (void *)0x0384E850; +const void * RTTI_BSMergeInstancedTriShape = (void *)0x0384E870; +const void * RTTI_BSMultiBoundNode = (void *)0x0384DEC0; +const void * RTTI_BSBoneMap = (void *)0x0384E8D8; +const void * RTTI_BSSceneGraph = (void *)0x037904D0; +const void * RTTI_BSReference = (void *)0x0384EB58; +const void * RTTI_BSNodeReferences = (void *)0x0384EC20; +const void * RTTI_BSShapeConstructor = (void *)0x0384EC50; +const void * RTTI_BSBoxConstructor = (void *)0x0384EC80; +const void * RTTI_BSOrientedBoxConstructor = (void *)0x0384ECA8; +const void * RTTI_BSSphereConstructor = (void *)0x0384ECD8; +const void * RTTI_BSCylinderConstructor = (void *)0x0384ED08; +const void * RTTI_BSCapsuleConstructor = (void *)0x0384ED38; +const void * RTTI_BSArrowConstructor = (void *)0x0384ED68; +const void * RTTI_BSCircleConstructor = (void *)0x0384ED98; +const void * RTTI_BSRingConstructor = (void *)0x0384EDC8; +const void * RTTI_BSFlatQuadConstructor = (void *)0x0384EDF0; +const void * RTTI_BSDiskConstructor = (void *)0x0384EE20; +const void * RTTI_BSPSysMultiTargetEmitterCtlr = (void *)0x0384EEA8; +const void * RTTI_BSTextureArray__Texture = (void *)0x0384EEE0; +const void * RTTI_BSTextureArray__StaticTexture = (void *)0x0384EF10; +const void * RTTI_BSTextureArray__StaticTextureIndexed = (void *)0x0384EF48; +const void * RTTI_BSTextureArray__MasterTexture = (void *)0x0384EF88; +const void * RTTI_BSProceduralLightningTasklet = (void *)0x0384F098; +const void * RTTI_BSProceduralLightningController = (void *)0x0384F0D0; +const void * RTTI_BSBoneLODExtraData = (void *)0x0384F380; +const void * RTTI_BSMasterParticleSystem = (void *)0x0384F3C0; +const void * RTTI_BSParentVelocityModifier = (void *)0x0384F3F8; +const void * RTTI_BSInvMarker = (void *)0x0384F430; +const void * RTTI_BSWArray = (void *)0x0384F458; +const void * RTTI_BSMultiBoundCapsule = (void *)0x0384F478; +const void * RTTI_BSPSysStripUpdateModifier = (void *)0x0384F4A8; +const void * RTTI_BSPSysSubTexModifier = (void *)0x0384F4D8; +const void * RTTI_BSPSysScaleModifier = (void *)0x0384F508; +const void * RTTI_BSLagBoneController = (void *)0x0384F538; +const void * RTTI_BSNonUniformScaleExtraData = (void *)0x0384F568; +const void * RTTI_BSCombinedTriShape = (void *)0x0384F5A0; +const void * RTTI_BSDistantObjectInstanceDataBlock = (void *)0x0384F5D0; +const void * RTTI_BSTableCurve = (void *)0x0384F618; +const void * RTTI__com_error = (void *)0x0384FA18; +const void * RTTI_BSD3DResourceCreator = (void *)0x03851A60; +const void * RTTI_BSConnectPoint__Children = (void *)0x03851E30; +const void * RTTI_BSConnectPoint__Parents = (void *)0x03851E60; +const void * RTTI_hknpBSWorld = (void *)0x038521A0; +const void * RTTI_bhkWorld = (void *)0x038521C8; +const void * RTTI_bhkCharProxyManager = (void *)0x038521E8; +const void * RTTI_bhkCharRigidBodyManager = (void *)0x03852218; +const void * RTTI_hclBSWorld = (void *)0x03852248; +const void * RTTI_hknpBackfaceCollisionModifier = (void *)0x03852290; +const void * RTTI_hknpBSShapeCodec = (void *)0x038524D8; +const void * RTTI_bhkNPCollisionObject = (void *)0x03852C30; +const void * RTTI_bhkNPCollisionObjectBase = (void *)0x03852C60; +const void * RTTI_bhkNPCollisionObjectUnlinked = (void *)0x03852C90; +const void * RTTI_bhkNPCollisionProxyObject = (void *)0x03852CC8; +const void * RTTI_bhkNPCollisionObjectUnlinked__LinkExtraData = (void *)0x03852D00; +const void * RTTI_bhkPhysicsSystem = (void *)0x03852D50; +const void * RTTI_bhkRagdollSystem = (void *)0x03852D78; +const void * RTTI_bhkCollisionFilter = (void *)0x03853008; +const void * RTTI_hknpCompressedMeshShapeCinfo = (void *)0x03853208; +const void * RTTI_hknpDefaultCompressedMeshShapeCinfo = (void *)0x03853240; +const void * RTTI_hknpBSMaterial = (void *)0x03853280; +const void * RTTI_hknpCharacterState = (void *)0x03853360; +const void * RTTI_bhkCharacterState = (void *)0x03853390; +const void * RTTI_bhkCharacterController = (void *)0x038533C0; +const void * RTTI_bhkICharOrientationController = (void *)0x038534D8; +const void * RTTI_bhkCharacterControllerCinfo = (void *)0x03853510; +const void * RTTI_hknpBSMoveLimitModifier = (void *)0x03853548; +const void * RTTI_bhkCharacterStateClimbing = (void *)0x03853768; +const void * RTTI_bhkCharacterStateFlying = (void *)0x03853798; +const void * RTTI_BSClothExtraData = (void *)0x038537E0; +const void * RTTI_hclTransformSet = (void *)0x038538E8; +const void * RTTI_hclInstantiationUtil = (void *)0x03853910; +const void * RTTI_BSClothUtils__BSInstantiationUtil = (void *)0x03853940; +const void * RTTI_BSClothUtils__BSTransformSet = (void *)0x03853978; +const void * RTTI_BSClothUtils__BSTriShapeBuffer = (void *)0x038539B0; +const void * RTTI_hknpClosestUniqueBodyIdHitCollector = (void *)0x03853A10; +const void * RTTI_bhkWorldM = (void *)0x03853AF0; +const void * RTTI_hkaNpRagdollRaycastInterface = (void *)0x03853B60; +const void * RTTI_bhkRagdollPenetrationUtil = (void *)0x03853B98; +const void * RTTI_hkThreadPool = (void *)0x03853C18; +const void * RTTI_bhkThreadPool = (void *)0x03853C40; +const void * RTTI_hknpFixedClosestHitCollector = (void *)0x03854F78; +const void * RTTI_hknpBSMouseSpringAction = (void *)0x03854FB8; +const void * RTTI_hknpCharacterProxyListener = (void *)0x038550B0; +const void * RTTI_bhkCharProxyController = (void *)0x038550E8; +const void * RTTI_bhkCharacterProxy = (void *)0x03855118; +const void * RTTI_bhkCharProxyControllerCinfo = (void *)0x03855140; +const void * RTTI_hknpCharacterRigidBodyListener = (void *)0x03855258; +const void * RTTI_bhkCharRigidBodyController = (void *)0x03855290; +const void * RTTI_bhkCharacterRigidBodyCinfo = (void *)0x038552C8; +const void * RTTI_bhkCharacterRigidBody = (void *)0x03855300; +const void * RTTI_bhkCharRigidBodyControllerCinfo = (void *)0x03855330; +const void * RTTI_hknpBSBroadPhaseConfig = (void *)0x03855420; +const void * RTTI_hknpSafeEaseConstraintsAction = (void *)0x03855458; +const void * RTTI_BSPistonController = (void *)0x03855598; +const void * RTTI_hclWorld = (void *)0x03852270; +const void * RTTI_hclWorldListener = (void *)0x038555C8; +const void * RTTI_bhkBipedOrientationController = (void *)0x03855700; +const void * RTTI_hkThreadMemory = (void *)0x03855948; +const void * RTTI_bhkMemorySystem = (void *)0x03855978; +const void * RTTI_hclBSClothParameterizedWindAction = (void *)0x03855A28; +const void * RTTI_hkPackfileWriter__AddObjectListener = (void *)0x03855A60; +const void * RTTI_hknpWorldSnapshot = (void *)0x03855AA0; +const void * RTTI_hclSimClothInstance = (void *)0x03855B00; +const void * RTTI_hclCollidable = (void *)0x03855B30; +const void * RTTI_hclClothInstance = (void *)0x03855B58; +const void * RTTI_hkaSplineCompressedAnimation = (void *)0x03855DD0; +const void * RTTI_hkpConstraintMotor = (void *)0x03855EF8; +const void * RTTI_hkpLimitedForceConstraintMotor = (void *)0x03855F28; +const void * RTTI_hkpPositionConstraintMotor = (void *)0x03855F60; +const void * RTTI_hkbCustomIdSelector = (void *)0x03855F98; +const void * RTTI_hkbStateChooserWrapper = (void *)0x03855FC8; +const void * RTTI_hkbAttachmentSetup = (void *)0x038561B0; +const void * RTTI_hkbBehaviorGraphInternalState = (void *)0x03856218; +const void * RTTI_hkbBlendingTransitionEffectInternalState = (void *)0x038567B8; +const void * RTTI_hkbBoneIndexArray = (void *)0x03856800; +const void * RTTI_hkbCharacterControllerModifierInternalState = (void *)0x03856A50; +const void * RTTI_hkbCombineTransformsModifierInternalState = (void *)0x03856AA0; +const void * RTTI_hkbComputeDirectionModifierInternalState = (void *)0x03856CC0; +const void * RTTI_hkbComputeRotationFromAxisAngleModifierInternalState = (void *)0x03856D20; +const void * RTTI_hkbComputeRotationToTargetModifierInternalState = (void *)0x03856D80; +const void * RTTI_hkbDampingModifierInternalState = (void *)0x03856E50; +const void * RTTI_hkbDelayedModifierInternalState = (void *)0x03856EB0; +const void * RTTI_hkbDetectCloseToGroundModifierInternalState = (void *)0x03856F60; +const void * RTTI_hkbDockingGeneratorInternalState = (void *)0x038571D8; +const void * RTTI_hkbExpressionDataArray = (void *)0x03857380; +const void * RTTI_hkbEvaluateExpressionModifierInternalState = (void *)0x038573B0; +const void * RTTI_hkbEventDrivenModifierInternalState = (void *)0x03857600; +const void * RTTI_hkpSpringDamperConstraintMotor = (void *)0x03857640; +const void * RTTI_hkbEventRangeDataArray = (void *)0x03857740; +const void * RTTI_hkbEventsFromRangeModifierInternalState = (void *)0x03857770; +const void * RTTI_hkbExtrapolatingTransitionEffect = (void *)0x03857868; +const void * RTTI_hkbExtrapolatingTransitionEffectInternalState = (void *)0x038578E0; +const void * RTTI_hkbGetUpModifierInternalState = (void *)0x03858130; +const void * RTTI_hkbGetWorldFromModelModifierInternalState = (void *)0x03858170; +const void * RTTI_hkbJigglerGroup = (void *)0x038583F8; +const void * RTTI_hkbJigglerModifierInternalState = (void *)0x03858520; +const void * RTTI_hkbLayerGeneratorInternalState = (void *)0x03858888; +const void * RTTI_hkbLookAtModifierInternalState = (void *)0x03858968; +const void * RTTI_hkbManualSelectorGeneratorInternalState = (void *)0x038589A0; +const void * RTTI_hkbManualSelectorTransitionEffectInternalState = (void *)0x03858A10; +const void * RTTI_hkbModifierWrapper = (void *)0x03858A58; +const void * RTTI_hkbMoveCharacterModifierInternalState = (void *)0x03858A90; +const void * RTTI_hkStringObject = (void *)0x03858C48; +const void * RTTI_hkbRotateCharacterModifierInternalState = (void *)0x03858D40; +const void * RTTI_hkbScriptCondition = (void *)0x03858D88; +const void * RTTI_hkbStringCondition = (void *)0x03859B58; +const void * RTTI_hkbTimerModifierInternalState = (void *)0x03859C00; +const void * RTTI_hkbTransformVectorModifierInternalState = (void *)0x03859DC8; +const void * RTTI_hkpVelocityConstraintMotor = (void *)0x0385A340; +const void * RTTI_hkcdPlanarEntity = (void *)0x0385A760; +const void * RTTI_hkcdPlanarSolid__NodeStorage = (void *)0x0385AA00; +const void * RTTI_hkbSimulationControlCommand = (void *)0x0385B1C0; +const void * RTTI_hkbCharacterControlCommand = (void *)0x0385B1F8; +const void * RTTI_hkbRaiseEventCommand = (void *)0x0385B230; +const void * RTTI_hkbSetSelectedCharacterCommand = (void *)0x0385B260; +const void * RTTI_hkbSetWordVariableCommand = (void *)0x0385B298; +const void * RTTI_hkbSetLocalTimeOfClipGeneratorCommand = (void *)0x0385B2C8; +const void * RTTI_hkbSetBehaviorCommand = (void *)0x0385B308; +const void * RTTI_hkbSetNodePropertyCommand = (void *)0x0385B338; +const void * RTTI_hkbRebuildScriptsCommand = (void *)0x0385B368; +const void * RTTI_hkbCameraVariablesChangedCommand = (void *)0x0385B398; +const void * RTTI_hkbAiControlPathToCommand = (void *)0x0385B3D0; +const void * RTTI_hkbAiControlCancelPathCommand = (void *)0x0385B400; +const void * RTTI_hkbCharacterAddedInfo = (void *)0x0385B438; +const void * RTTI_hkbCharacterInfo = (void *)0x0385B468; +const void * RTTI_hkbCharacterSteppedInfo = (void *)0x0385B490; +const void * RTTI_hkbCharacterSkinInfo = (void *)0x0385B4C0; +const void * RTTI_hkbBehaviorInfo = (void *)0x0385B4F0; +const void * RTTI_hkbBehaviorEventsInfo = (void *)0x0385B518; +const void * RTTI_hkbEventRaisedInfo = (void *)0x0385B548; +const void * RTTI_hkbSimulationStateInfo = (void *)0x0385B578; +const void * RTTI_hkbLinkedSymbolInfo = (void *)0x0385B5A8; +const void * RTTI_hkbAuxiliaryNodeInfo = (void *)0x0385B5D8; +const void * RTTI_hkbBehaviorGraphInternalStateInfo = (void *)0x0385B608; +const void * RTTI_hkbCharacterSelectedInfo = (void *)0x0385B640; +const void * RTTI_hkbCameraShakeEventPayload = (void *)0x0385B880; +const void * RTTI_hkbHoldFromBlendingTransitionEffect = (void *)0x0385C200; +const void * RTTI_hkbNullPhysicsInterface = (void *)0x0385C240; +const void * RTTI_hkbParticleSystemEventPayload = (void *)0x0385C370; +const void * RTTI_hkbGeneratorOutputListener = (void *)0x0385C3C0; +const void * RTTI_hkbPoseStoringGeneratorOutputListener = (void *)0x0385C3F8; +const void * RTTI_hkbPoseStoringGeneratorOutputListener__StoredPose = (void *)0x0385C440; +const void * RTTI_hkbTestIdSelector = (void *)0x0385C5A8; +const void * RTTI_hkMeshTexture = (void *)0x0385CEE8; +const void * RTTI_hkMemoryMeshTexture = (void *)0x0385CF10; +const void * RTTI_BSIStateManagerModifier__BSIStateManagerStateListener = (void *)0x0385DAD0; +const void * RTTI_hkpCallbackConstraintMotor = (void *)0x0385E358; +const void * RTTI_hkpCogWheelConstraintData = (void *)0x0385E3C8; +const void * RTTI_hkpLinearClearanceConstraintData = (void *)0x0385E548; +const void * RTTI_hkpParametricCurve = (void *)0x0385E650; +const void * RTTI_hkpLinearParametricCurve = (void *)0x0385E680; +const void * RTTI_hkpRackAndPinionConstraintData = (void *)0x0385E800; +const void * RTTI_hkpRotationalConstraintData = (void *)0x0385E8A0; +const void * RTTI_hkpWheelFrictionConstraintData = (void *)0x0385EFC0; +const void * RTTI_hclConstraintSet = (void *)0x0385FA28; +const void * RTTI_hclAntiPinchConstraintSet = (void *)0x0385FA50; +const void * RTTI_hclBendLinkConstraintSet = (void *)0x0385FB10; +const void * RTTI_hclBendLinkConstraintSetMx = (void *)0x0385FBA0; +const void * RTTI_hclBendStiffnessConstraintSet = (void *)0x0385FC08; +const void * RTTI_hclBendStiffnessConstraintSetMx = (void *)0x0385FCA0; +const void * RTTI_hclOperator = (void *)0x0385FD10; +const void * RTTI_hclBlendSomeVerticesOperator = (void *)0x0385FD38; +const void * RTTI_hclBonePlanesConstraintSet = (void *)0x0385FDA0; +const void * RTTI_hclBoneSpaceMeshMeshDeformOperator = (void *)0x0385FDD8; +const void * RTTI_hclBoneSpaceMeshMeshDeformPNOperator = (void *)0x0385FE18; +const void * RTTI_hclBoneSpaceMeshMeshDeformPNTBOperator = (void *)0x0385FE58; +const void * RTTI_hclBoneSpaceMeshMeshDeformPNTOperator = (void *)0x0385FE98; +const void * RTTI_hclBoneSpaceMeshMeshDeformPOperator = (void *)0x0385FED8; +const void * RTTI_hclBoneSpaceSkinOperator = (void *)0x0385FF18; +const void * RTTI_hclBoneSpaceSkinPNOperator = (void *)0x0385FF48; +const void * RTTI_hclBoneSpaceSkinPNTBOperator = (void *)0x0385FF80; +const void * RTTI_hclBoneSpaceSkinPNTOperator = (void *)0x0385FFB8; +const void * RTTI_hclBoneSpaceSkinPOperator = (void *)0x0385FFF0; +const void * RTTI_hclBufferDefinition = (void *)0x03860028; +const void * RTTI_hclShape = (void *)0x03860058; +const void * RTTI_hclCapsuleShape = (void *)0x03860078; +const void * RTTI_hclClothData = (void *)0x038601F0; +const void * RTTI_hclClothState = (void *)0x03860278; +const void * RTTI_hclCompressibleLinkConstraintSet = (void *)0x038602D0; +const void * RTTI_hclCompressibleLinkConstraintSetMx = (void *)0x03860368; +const void * RTTI_hclConvexGeometryShape = (void *)0x038603A8; +const void * RTTI_hclConvexHeightFieldShape = (void *)0x038603D8; +const void * RTTI_hclConvexPlanesShape = (void *)0x03860408; +const void * RTTI_hclCopyVerticesOperator = (void *)0x03860438; +const void * RTTI_hclGatherAllVerticesOperator = (void *)0x03860468; +const void * RTTI_hclGatherSomeVerticesOperator = (void *)0x038604D0; +const void * RTTI_hclInputConvertOperator = (void *)0x03860508; +const void * RTTI_hclLocalRangeConstraintSet = (void *)0x03860648; +const void * RTTI_hclMeshBoneDeformOperator = (void *)0x038606B0; +const void * RTTI_hclMeshMeshDeformOperator = (void *)0x038608B0; +const void * RTTI_hclMoveParticlesOperator = (void *)0x03860918; +const void * RTTI_hclObjectSpaceMeshMeshDeformOperator = (void *)0x03860948; +const void * RTTI_hclObjectSpaceMeshMeshDeformPNOperator = (void *)0x03860988; +const void * RTTI_hclObjectSpaceMeshMeshDeformPNTBOperator = (void *)0x038609C8; +const void * RTTI_hclObjectSpaceMeshMeshDeformPNTOperator = (void *)0x03860A08; +const void * RTTI_hclObjectSpaceMeshMeshDeformPOperator = (void *)0x03860A48; +const void * RTTI_hclObjectSpaceSkinOperator = (void *)0x03860A88; +const void * RTTI_hclObjectSpaceSkinPNOperator = (void *)0x03860AC0; +const void * RTTI_hkpVehicleFrictionDescription = (void *)0x03860B88; +const void * RTTI_hclObjectSpaceSkinPNTBOperator = (void *)0x03860BC0; +const void * RTTI_hclObjectSpaceSkinPNTOperator = (void *)0x03860BF8; +const void * RTTI_hclObjectSpaceSkinPOperator = (void *)0x03860C30; +const void * RTTI_hclOutputConvertOperator = (void *)0x03860C68; +const void * RTTI_hclPlaneShape = (void *)0x03860C98; +const void * RTTI_hclScratchBufferDefinition = (void *)0x03860CC8; +const void * RTTI_hclShadowBufferDefinition = (void *)0x03860D00; +const void * RTTI_hclSimClothData = (void *)0x03860EB0; +const void * RTTI_hclSimClothPose = (void *)0x03860EE0; +const void * RTTI_hclSimpleMeshBoneDeformOperator = (void *)0x03860F38; +const void * RTTI_hclAction = (void *)0x038559D8; +const void * RTTI_hclSimpleWindAction = (void *)0x038559F8; +const void * RTTI_hclSimulateOperator = (void *)0x03860FA0; +const void * RTTI_hclSkinOperator = (void *)0x03861008; +const void * RTTI_hclSphereShape = (void *)0x03861030; +const void * RTTI_hclStandardLinkConstraintSet = (void *)0x038610B8; +const void * RTTI_hclStandardLinkConstraintSetMx = (void *)0x03861150; +const void * RTTI_hclStaticShadowBufferDefinition = (void *)0x038612C0; +const void * RTTI_hclStretchLinkConstraintSet = (void *)0x03861458; +const void * RTTI_hclStretchLinkConstraintSetMx = (void *)0x03861560; +const void * RTTI_hclTaperedCapsuleShape = (void *)0x03861598; +const void * RTTI_hknpBodyReference = (void *)0x038615C8; +const void * RTTI_hclTransformSetDefinition = (void *)0x038615F8; +const void * RTTI_hclTransitionConstraintSet = (void *)0x03861658; +const void * RTTI_hclUpdateAllVertexFramesOperator = (void *)0x03861690; +const void * RTTI_hknpCircularSurfaceVelocity = (void *)0x038616C8; +const void * RTTI_hclUpdateSomeVertexFramesOperator = (void *)0x03861730; +const void * RTTI_hclVolumeConstraint = (void *)0x038617C8; +const void * RTTI_hclVolumeConstraintMx = (void *)0x038618B8; +const void * RTTI_hknpExternMeshShapeGeometry = (void *)0x038618E8; +const void * RTTI_hknpDefaultExternMeshShapeGeometry = (void *)0x03861920; +const void * RTTI_hknpDisableCollisionFilter = (void *)0x03861960; +const void * RTTI_hknpMaterialPalette = (void *)0x03861D48; +const void * RTTI_hknpRefWorldCinfo = (void *)0x03861DD8; +const void * RTTI_hknpTyremarksWheel = (void *)0x038620A0; +const void * RTTI_hknpTyremarksInfo = (void *)0x038620D0; +const void * RTTI_hknpVehicleData = (void *)0x03862128; +const void * RTTI_hknpVehicleAerodynamics = (void *)0x03862150; +const void * RTTI_hknpVehicleDefaultAerodynamics = (void *)0x03862180; +const void * RTTI_hknpVehicleDriverInputStatus = (void *)0x038621B8; +const void * RTTI_hknpVehicleDriverInput = (void *)0x038621F0; +const void * RTTI_hknpVehicleDriverInputAnalogStatus = (void *)0x03862220; +const void * RTTI_hknpVehicleDefaultAnalogDriverInput = (void *)0x03862260; +const void * RTTI_hknpVehicleBrake = (void *)0x038622D0; +const void * RTTI_hknpVehicleDefaultBrake = (void *)0x038622F8; +const void * RTTI_hknpVehicleEngine = (void *)0x03862328; +const void * RTTI_hknpVehicleDefaultEngine = (void *)0x03862350; +const void * RTTI_hknpVehicleSteering = (void *)0x03862380; +const void * RTTI_hknpVehicleDefaultSteering = (void *)0x038623B0; +const void * RTTI_hknpVehicleSuspension = (void *)0x03862690; +const void * RTTI_hknpVehicleDefaultSuspension = (void *)0x038626C0; +const void * RTTI_hknpVehicleTransmission = (void *)0x038626F8; +const void * RTTI_hknpVehicleDefaultTransmission = (void *)0x03862728; +const void * RTTI_hknpVehicleVelocityDamper = (void *)0x03862760; +const void * RTTI_hknpVehicleDefaultVelocityDamper = (void *)0x03862790; +const void * RTTI_hknpVehicleInstance = (void *)0x038627F8; +const void * RTTI_hknpVehicleWheelCollide = (void *)0x03862858; +const void * RTTI_hknpVehicleLinearCastWheelCollide = (void *)0x03862888; +const void * RTTI_hknpVehicleRayCastWheelCollide = (void *)0x038628C0; +const void * RTTI_hknpStaticCompoundShapeInternals = (void *)0x038632A8; +const void * RTTI_hkaAnimationPreviewColorContainer = (void *)0x03863378; +const void * RTTI_hkaAnimatedReferenceFrame = (void *)0x038633B0; +const void * RTTI_hkaDefaultAnimatedReferenceFrame = (void *)0x038633E0; +const void * RTTI_hkaFootstepAnalysisInfoContainer = (void *)0x03863418; +const void * RTTI_hkaInterleavedUncompressedAnimation = (void *)0x03863450; +const void * RTTI_hkaLosslessCompressedAnimation = (void *)0x03863498; +const void * RTTI_hkaParameterizedAnimationReferenceFrame = (void *)0x038634D8; +const void * RTTI_hkaQuantizedAnimation = (void *)0x03863590; +const void * RTTI_hknpCompressedMeshShape = (void *)0x038635C0; +const void * RTTI_hknpStaticCompoundShape = (void *)0x038632E0; +const void * RTTI_hknpMaskedShape = (void *)0x038635F0; +const void * RTTI_hknpMaskedShape__MaskWrapper = (void *)0x03863618; +const void * RTTI_bhkCharacterStateFloating = (void *)0x03863650; +const void * RTTI_bhkCharacterStateInAir = (void *)0x038636A8; +const void * RTTI_bhkCharacterStateJumping = (void *)0x038636E0; +const void * RTTI_bhkCharacterStateOnGround = (void *)0x03863760; +const void * RTTI_bhkCharacterStateSwimming = (void *)0x03863798; +const void * RTTI_bhkQuadOrientationController = (void *)0x038637F0; +const void * RTTI_hclBuffer = (void *)0x038539E8; +const void * RTTI_hkaNpRagdollPenetrationDetector = (void *)0x03863878; +const void * RTTI_bhkCharacterPointCollector = (void *)0x038638B8; +const void * RTTI_hknpBSCharacterProxy = (void *)0x038638F8; +const void * RTTI_hknpBSCharacterRigidBody = (void *)0x03863930; +const void * RTTI_hkSolverAllocator = (void *)0x03863B60; +const void * RTTI_bhkThreadMemorySource = (void *)0x03863BA0; +const void * RTTI_hknpDummyShape = (void *)0x03863BD0; +const void * RTTI_hknpExternMeshShape = (void *)0x03863BF8; +const void * RTTI_hclPointContactPlanesShape = (void *)0x03863C28; +const void * RTTI_hkbAttachmentModifier = (void *)0x03863C60; +const void * RTTI_hkbAttributeModifier = (void *)0x03863C90; +const void * RTTI_hkbBlendingTransitionEffect = (void *)0x038578A0; +const void * RTTI_hkbCharacterControllerModifier = (void *)0x03863CC0; +const void * RTTI_hkbCombineTransformsModifier = (void *)0x03863CF8; +const void * RTTI_hkbComputeDirectionModifier = (void *)0x03863D30; +const void * RTTI_hkbComputeRotationFromAxisAngleModifier = (void *)0x03863D68; +const void * RTTI_hkbComputeRotationToTargetModifier = (void *)0x03863DA8; +const void * RTTI_hkbDampingModifier = (void *)0x03863DE8; +const void * RTTI_hkbDelayedModifier = (void *)0x03863E18; +const void * RTTI_hkbDetectCloseToGroundModifier = (void *)0x03863E48; +const void * RTTI_hkbDockingGenerator = (void *)0x03863E88; +const void * RTTI_hkbEvaluateExpressionModifier = (void *)0x03863EB8; +const void * RTTI_hkbEvaluateHandleModifier = (void *)0x03863EF0; +const void * RTTI_hkbEventDrivenModifier = (void *)0x03863F20; +const void * RTTI_hkbEventsFromRangeModifier = (void *)0x03863F50; +const void * RTTI_hkbExtractRagdollPoseModifier = (void *)0x03863F88; +const void * RTTI_hkbFootIkControlsModifier = (void *)0x03863FC0; +const void * RTTI_hkbFootIkModifier = (void *)0x03863FF8; +const void * RTTI_hkbGetHandleOnBoneModifier = (void *)0x03864020; +const void * RTTI_hkbGetUpModifier = (void *)0x03864058; +const void * RTTI_hkbGetWorldFromModelModifier = (void *)0x03864080; +const void * RTTI_hkbHandIkControlsModifier = (void *)0x038640B8; +const void * RTTI_hkbHandIkModifier = (void *)0x038640E8; +const void * RTTI_hkbJigglerModifier = (void *)0x03864110; +const void * RTTI_hkbKeyframeBonesModifier = (void *)0x03864140; +const void * RTTI_hkbLayer = (void *)0x03864170; +const void * RTTI_hkbLayerGenerator = (void *)0x03864190; +const void * RTTI_hkbLookAtModifier = (void *)0x038641B8; +const void * RTTI_hkbManualSelectorGenerator = (void *)0x038641E0; +const void * RTTI_hkbManualSelectorTransitionEffect = (void *)0x03864218; +const void * RTTI_hkbMirrorModifier = (void *)0x03864250; +const void * RTTI_hkbModifierGenerator = (void *)0x03864280; +const void * RTTI_hkbModifierList = (void *)0x038642B0; +const void * RTTI_hkbMoveBoneAttachmentModifier = (void *)0x038642E0; +const void * RTTI_hkbMoveCharacterModifier = (void *)0x03864318; +const void * RTTI_hkbPoweredRagdollControlsModifier = (void *)0x03864348; +const void * RTTI_hkbRigidBodyRagdollControlsModifier = (void *)0x03864380; +const void * RTTI_hkbRotateCharacterModifier = (void *)0x038643C0; +const void * RTTI_hkbScriptGenerator = (void *)0x038643F8; +const void * RTTI_hkbSenseHandleModifier = (void *)0x03864428; +const void * RTTI_hkbSetWorldFromModelModifier = (void *)0x03864458; +const void * RTTI_hkbTimerModifier = (void *)0x03864490; +const void * RTTI_hkbTransformVectorModifier = (void *)0x038644B8; +const void * RTTI_hkbTwistModifier = (void *)0x038644F0; +const void * RTTI_hkUuidPseudoRandomGenerator = (void *)0x03864538; +const void * RTTI_hkResourceContainer = (void *)0x038645B8; +const void * RTTI_hkResourceMap = (void *)0x03864610; +const void * RTTI_hkResourceBase = (void *)0x038645E8; +const void * RTTI_hkResourceHandle = (void *)0x03864638; +const void * RTTI_hkMemoryResourceHandle = (void *)0x03864660; +const void * RTTI_hkMemoryResourceContainer = (void *)0x03864690; +const void * RTTI_hkContainerResourceMap = (void *)0x038646C0; +const void * RTTI_hkcdPlanarSolid__ArrayMgr = (void *)0x038646F0; +const void * RTTI_hkcdConvexCellsCollection = (void *)0x03864720; +const void * RTTI_hkcdPlanarCsgOperand = (void *)0x03864750; +const void * RTTI_hkcdPlanarGeometry = (void *)0x03864780; +const void * RTTI_hkcdPlanarGeometry__VertexStorage = (void *)0x038647B0; +const void * RTTI_hkcdPlanarGeometryPlanesCollection = (void *)0x03864878; +const void * RTTI_hkcdPlanarGeometryPolygonCollection = (void *)0x038648B8; +const void * RTTI_hkcdPlanarSolid = (void *)0x038648F8; +const void * RTTI_hkcdShape = (void *)0x03864920; +const void * RTTI_hkcdDynamicAabbTree = (void *)0x03864940; +const void * RTTI_hkcdStaticAabbTree = (void *)0x03864970; +const void * RTTI_hkbAlignBoneModifier = (void *)0x038649A0; +const void * RTTI_hkbAnimatedSkeletonGenerator = (void *)0x038649D0; +const void * RTTI_hkRemoteObjectClientSideListener = (void *)0x03864A08; +const void * RTTI_hkbBehaviorClient = (void *)0x03864A40; +const void * RTTI_hkbClientCharacterState = (void *)0x03864A68; +const void * RTTI_hkbComputeWorldFromModelModifier = (void *)0x03864A98; +const void * RTTI_hkbCustomTestGeneratorHiddenTypes = (void *)0x03864AD0; +const void * RTTI_hkbCustomTestGeneratorSimpleTypes = (void *)0x03864B08; +const void * RTTI_hkbCustomTestGeneratorComplexTypes = (void *)0x03864B40; +const void * RTTI_hkbCustomTestGeneratorNestedTypesBase = (void *)0x03864B80; +const void * RTTI_hkbCustomTestGeneratorNestedTypes = (void *)0x03864BC0; +const void * RTTI_hkbCustomTestGeneratorBoneTypes = (void *)0x03864BF8; +const void * RTTI_hkbCustomTestGeneratorAnnotatedTypes = (void *)0x03864C30; +const void * RTTI_hkbCustomTestGenerator = (void *)0x03864C70; +const void * RTTI_hkbGravityModifier = (void *)0x03864CA0; +const void * RTTI_hkbPinBonesGenerator = (void *)0x03864CD0; +const void * RTTI_hkbRadialSelectorGenerator = (void *)0x03864D00; +const void * RTTI_hkbStateDependentModifier = (void *)0x03864D38; +const void * RTTI_hkSkinnedMeshShape = (void *)0x03864D68; +const void * RTTI_hkStorageSkinnedMeshShape = (void *)0x03864D98; +const void * RTTI_hkMeshMaterial = (void *)0x03864DC8; +const void * RTTI_hkMemoryMeshMaterial = (void *)0x03864DF0; +const void * RTTI_BSAlignBoneModifier = (void *)0x03864E20; +const void * RTTI_hkMeshShape = (void *)0x03864E50; +const void * RTTI_hkMemoryMeshShape = (void *)0x03864E78; +const void * RTTI_BSAssignVariablesModifier = (void *)0x03864EA0; +const void * RTTI_hkMemoryMeshTexture__Sampler = (void *)0x03864ED0; +const void * RTTI_hkMeshVertexBuffer = (void *)0x03864F38; +const void * RTTI_hkMemoryMeshVertexBuffer = (void *)0x03864F68; +const void * RTTI_BSBoneSwitchGenerator = (void *)0x03864F98; +const void * RTTI_hkMultipleVertexBuffer = (void *)0x03864FC8; +const void * RTTI_BSBoneSwitchGeneratorBoneData = (void *)0x03864FF8; +const void * RTTI_hkSkinBinding = (void *)0x03865030; +const void * RTTI_BSComputeAddBoneAnimModifier = (void *)0x03865058; +const void * RTTI_hkSkinnedRefMeshShape = (void *)0x03865090; +const void * RTTI_BSCyclicBlendTransitionGenerator = (void *)0x038650C0; +const void * RTTI_BSDecomposeVectorModifier = (void *)0x038650F8; +const void * RTTI_BSDirectAtCapturePoseModifier = (void *)0x03865128; +const void * RTTI_BSDirectAtModifier = (void *)0x03865160; +const void * RTTI_BSDistTriggerModifier = (void *)0x03865190; +const void * RTTI_BSEventEveryNEventsModifier = (void *)0x038651C0; +const void * RTTI_BSEventOnDeactivateModifier = (void *)0x038651F8; +const void * RTTI_BSEventOnFalseToTrueModifier = (void *)0x03865230; +const void * RTTI_BSGetTimeStepModifier = (void *)0x03865268; +const void * RTTI_BSIStateManagerModifier = (void *)0x03865298; +const void * RTTI_BSInterpValueModifier = (void *)0x038652C8; +const void * RTTI_BSIsActiveModifier = (void *)0x038652F8; +const void * RTTI_BSLimbCycleModifier = (void *)0x03865328; +const void * RTTI_BSLimbIKModifier = (void *)0x03865358; +const void * RTTI_BSLookAtCapturePoseModifier = (void *)0x03865380; +const void * RTTI_BSLookAtModifier = (void *)0x038653B8; +const void * RTTI_BSModifyOnceModifier = (void *)0x038653E0; +const void * RTTI_BSOffsetAnimationGenerator = (void *)0x03865410; +const void * RTTI_BSPassByTargetTriggerModifier = (void *)0x03865448; +const void * RTTI_BSRagdollContactListenerModifier = (void *)0x03865480; +const void * RTTI_BSRandomAlarmModifier = (void *)0x038654B8; +const void * RTTI_BSRetargetBodyBlend = (void *)0x038654E8; +const void * RTTI_BSReverseSpineTwistModifier = (void *)0x03865518; +const void * RTTI_BSRootTwistModifier = (void *)0x03865550; +const void * RTTI_BSSpeedSamplerModifier = (void *)0x03865580; +const void * RTTI_BSTimerModifier = (void *)0x038655B0; +const void * RTTI_BSTweenerModifier = (void *)0x038655E0; +const void * RTTI_DynamicAnimationTaggingGenerator = (void *)0x03865608; +const void * RTTI_hclAntiPinchConstraintInstanceData = (void *)0x03865640; +const void * RTTI_hclClothContainer = (void *)0x03865690; +const void * RTTI_hknpBallGun = (void *)0x038656B8; +const void * RTTI_hknpFirstPersonGun = (void *)0x038656E0; +const void * RTTI_hknpProjectileGun = (void *)0x03865710; +const void * RTTI_hknpGunProjectile = (void *)0x03865738; +const void * RTTI_hclTransitionConstraintInstanceData = (void *)0x03865808; +const void * RTTI_hclVolumeConstraintInstanceData = (void *)0x03865848; +const void * RTTI_hclVolumeConstraintMxInstanceData = (void *)0x03865880; +const void * RTTI_hknpDestructionShapeProperties = (void *)0x038658B8; +const void * RTTI_hknpLodShape = (void *)0x038658F0; +const void * RTTI_hknpMalleableConstraintData = (void *)0x03865918; +const void * RTTI_hknpPhysicsSceneData = (void *)0x03865950; +const void * RTTI_hkaParameterizedReferenceFrame = (void *)0x03865980; +const void * RTTI_hkaAngularReferenceFrame = (void *)0x038659B8; +const void * RTTI_hkaDirectionalReferenceFrame = (void *)0x038659E8; +const void * RTTI_hkaFootstepAnalysisInfo = (void *)0x03865A20; +const void * RTTI_hkaPredictiveCompressedAnimation = (void *)0x03865A50; +const void * RTTI_hclScratchBuffer = (void *)0x03865A88; +const void * RTTI_hclShadowBuffer = (void *)0x03865AB0; +const void * RTTI_hclStaticShadowBuffer = (void *)0x03865AD8; +const void * RTTI_hkcdConvexCellsTree3D = (void *)0x03865B30; +const void * RTTI_hkcdVertexGeometry = (void *)0x03865BF8; +const void * RTTI_hkcdVertexGeometry__VPolygonCollectionBase = (void *)0x03865C30; +const void * RTTI_hkcdVertexGeometry__VPolygonCollection = (void *)0x03865C70; +const void * RTTI_hkMeshTexture__Sampler = (void *)0x03864F08; +const void * RTTI_hkcdConvexCellsTree2D = (void *)0x03865CD0; +const void * RTTI_hkImage = (void *)0x03865DC0; +const void * RTTI_hkDefaultImage = (void *)0x03865DE0; +const void * RTTI_UI = (void *)0x03865E30; +const void * RTTI_Scaleform__Render__MappedTextureBase = (void *)0x03866160; +const void * RTTI_Scaleform__Render__RenderBuffer__RenderTargetData = (void *)0x038661E0; +const void * RTTI_Scaleform__Render__D3D1x__MappedTexture = (void *)0x03866228; +const void * RTTI_Scaleform__Render__D3D1x__RenderTargetData = (void *)0x03866268; +const void * RTTI_BSUIScaleformData = (void *)0x038663D8; +const void * RTTI_UIMessage = (void *)0x036F18A0; +const void * RTTI_BSGFxFunctionHandler = (void *)0x03866740; +const void * RTTI_BSGFxFunctionBase = (void *)0x0375AB10; +const void * RTTI_BSGFxShaderFXTarget = (void *)0x0375A9F0; +const void * RTTI_BSGFxDisplayObject = (void *)0x0375AA20; +const void * RTTI_IMenu = (void *)0x0377CB78; +const void * RTTI_Scaleform__SysAllocPaged = (void *)0x03866C70; +const void * RTTI_Scaleform__SysMemMapper = (void *)0x03866CA0; +const void * RTTI_Scaleform__Log = (void *)0x03866D50; +const void * RTTI_Scaleform__GFx__Translator = (void *)0x03866DC8; +const void * RTTI_Scaleform__GFx__ZlibSupportBase = (void *)0x03866DF8; +const void * RTTI_Scaleform__GFx__State = (void *)0x03866CD0; +const void * RTTI_Scaleform__GFx__ZlibSupport = (void *)0x03866E30; +const void * RTTI_BSScaleformTranslator = (void *)0x03866E60; +const void * RTTI_BSScaleformManager = (void *)0x03866E90; +const void * RTTI_BSStreamParserData = (void *)0x038670C8; +const void * RTTI_BSResourceStreamParser = (void *)0x038670F8; +const void * RTTI_MenuCursor = (void *)0x03867490; +const void * RTTI_Scaleform__File = (void *)0x038676C8; +const void * RTTI_Scaleform__MemoryFile = (void *)0x03867768; +const void * RTTI_Scaleform__GFx__FileOpenerBase = (void *)0x03867798; +const void * RTTI_Scaleform__GFx__FileOpener = (void *)0x038677D0; +const void * RTTI_BSScaleformFileOpener = (void *)0x03867800; +const void * RTTI_ScaleformFile__MemoryFile = (void *)0x03867BB8; +const void * RTTI_IDebugText = (void *)0x03867C50; +const void * RTTI_DebugText = (void *)0x03867C78; +const void * RTTI_Scaleform__RefCountVImpl = (void *)0x03871938; +const void * RTTI_Scaleform__Render__ImageBase = (void *)0x03871968; +const void * RTTI_Scaleform__Render__Image = (void *)0x03871A48; +const void * RTTI_Scaleform__Render__TextureImage = (void *)0x03871A78; +const void * RTTI_BSScaleformImageLoader = (void *)0x03871AB0; +const void * RTTI_SFRenderBufferManager = (void *)0x038722F0; +const void * RTTI_Movie = (void *)0x038A30E8; +const void * RTTI_MoviePlayer = (void *)0x0378A8C0; +const void * RTTI_ITrianglePathSplitter = (void *)0x038A3388; +const void * RTTI_IPathSmootherRayCast = (void *)0x038A33B8; +const void * RTTI_FindTriangleForLocationTraversableFilter = (void *)0x038A33E8; +const void * RTTI_FindTriangleForLocationWaterFilter = (void *)0x038A3428; +const void * RTTI_BSPathing = (void *)0x037447D8; +const void * RTTI_BSPathing__EdgeFoundVisitor = (void *)0x038A3468; +const void * RTTI_TrianglePathWaterAndLedgeSplitter = (void *)0x038A34A0; +const void * RTTI_BSNavmesh = (void *)0x03743F38; +const void * RTTI_IPathBuilderTracker = (void *)0x038A3AD8; +const void * RTTI_BSPathingRequest = (void *)0x036DF258; +const void * RTTI_NullPathBuilderTracker = (void *)0x038A3B08; +const void * RTTI_BSPathSmootherPOVSearch = (void *)0x038A3E98; +const void * RTTI_DynamicNavmesh__BuildThread = (void *)0x038A3FB8; +const void * RTTI_MovementAgent = (void *)0x036DE690; +const void * RTTI_NullMovementState = (void *)0x038A3FF0; +const void * RTTI_IMovementHandlerAgent = (void *)0x036DE6B8; +const void * RTTI_MovementHandlerArbiter = (void *)0x038A40A0; +const void * RTTI_MovementHandlerAgent = (void *)0x036DE660; +const void * RTTI_IMovementControllerRegisterInterface = (void *)0x03798BC0; +const void * RTTI_IMovementControllerDataTracker = (void *)0x038A46B0; +const void * RTTI_MovementControllerAI = (void *)0x03798B90; +const void * RTTI_BSPathingStreamSimpleBufferRead = (void *)0x038A4748; +const void * RTTI_MovementControllerNullDataTracker = (void *)0x038A4780; +const void * RTTI_IMovementPlayIdleResult = (void *)0x038A49D0; +const void * RTTI_ISelectIdleFilter = (void *)0x038A4A00; +const void * RTTI_MovementSelectIdleUtils__SelectClosestIdleToPath = (void *)0x038A4A30; +const void * RTTI_MovementSelectIdleUtils__SelectClosestIdleToMovementSelectionData = (void *)0x038A4A80; +const void * RTTI_MovementMessageFreezeDirection = (void *)0x038A4AD8; +const void * RTTI_IMovementQueryTweener = (void *)0x038A4DA0; +const void * RTTI_IMovementSetTweener = (void *)0x038A4DD0; +const void * RTTI_MovementTweenerArbiter = (void *)0x038A4E00; +const void * RTTI_MovementTweenerAgent = (void *)0x0379A228; +const void * RTTI_IMovementSetCorrectionData = (void *)0x038A5308; +const void * RTTI_IMovementQueryTweenerState = (void *)0x038A5340; +const void * RTTI_MovementTweenerAgentFixedDelta = (void *)0x038A5378; +const void * RTTI_BSNavmeshObstacleCoverData = (void *)0x038A5678; +const void * RTTI_BSNavmeshObstacleData = (void *)0x038A56B0; +const void * RTTI_BSNavmeshInfoSearch = (void *)0x038A5748; +const void * RTTI_BSPrecomputedNavmeshInfoSearch = (void *)0x038A5808; +const void * RTTI_IMovementPathManagerAgent = (void *)0x038A5878; +const void * RTTI_MovementPathManagerAgent = (void *)0x038A58A8; +const void * RTTI_IMovementSetGoal = (void *)0x038A58D8; +const void * RTTI_IMovementQueryPathingState = (void *)0x038A5900; +const void * RTTI_IMovementPathManagerDataTracker = (void *)0x038A5938; +const void * RTTI_MovementPathManagerArbiter = (void *)0x038A59F8; +const void * RTTI_MovementMessageNewPath = (void *)0x038A5A30; +const void * RTTI_MovementMessageUpdateRequestImmediate = (void *)0x038A5A90; +const void * RTTI_MovementPathManagerNullTracker = (void *)0x038A5AD0; +const void * RTTI_IMovementSetState = (void *)0x038A5DC0; +const void * RTTI_MovementAgentActorState = (void *)0x038A5DE8; +const void * RTTI_IPathFollowerAction = (void *)0x038A6130; +const void * RTTI_IMovementPlannerAgent = (void *)0x03799C00; +const void * RTTI_IMovementQueryPathFollowing = (void *)0x038A6160; +const void * RTTI_IMovementQueryPathFollowing__INodeVisitor = (void *)0x038A6198; +const void * RTTI_IMovementSetPathFollowing = (void *)0x038A61D8; +const void * RTTI_ICheckEndReachedFunctorMapper = (void *)0x038A6208; +const void * RTTI_CheckParameterReachedFunctor = (void *)0x038A6240; +const void * RTTI_CheckGoalReachedFunctor = (void *)0x038A62A8; +const void * RTTI_CheckStoppedMovingFunctor = (void *)0x038A62D8; +const void * RTTI_MovementAgentPathFollowerStandard = (void *)0x038A6308; +const void * RTTI_BSNavmeshInfoMap = (void *)0x037440C0; +const void * RTTI_BSNavmeshInfoMap__IRecursiveVisitor = (void *)0x038A65D0; +const void * RTTI_BSPrecomputedNavmeshInfoPathMap__INavmeshInfoVisitor = (void *)0x038A6680; +const void * RTTI_MovementArbitrationAverageFloat = (void *)0x038A6708; +const void * RTTI_MovementArbitrationMaxWeightPoint = (void *)0x038A67B0; +const void * RTTI_MovementArbitrationMaxWeightParameters = (void *)0x038A6878; +const void * RTTI_NullMovementSelectIdleResult = (void *)0x038A69D8; +const void * RTTI_INavmeshSearchFilterSet = (void *)0x037457B0; +const void * RTTI_BSNavmeshSearchFilters = (void *)0x03745860; +const void * RTTI_BSNavmeshSearch = (void *)0x03745808; +const void * RTTI_BSPathingLOSGridCell = (void *)0x038A6B48; +const void * RTTI_BSPathingLOSGrid = (void *)0x038A6B78; +const void * RTTI_BSPathingLOSGridMap = (void *)0x038A6BA0; +const void * RTTI_BSPathBuilder = (void *)0x038A6C98; +const void * RTTI_PathSmootherRayCastUsePreferredTris = (void *)0x038A6DE8; +const void * RTTI_PathSmootherRayCastUseTrianglePath = (void *)0x038A6E28; +const void * RTTI_IMovementSetStaticAvoider = (void *)0x038A6F48; +const void * RTTI_IMovementQueryStaticAvoider = (void *)0x038A6F78; +const void * RTTI_MovementPathManagerAgentStaticAvoider = (void *)0x038A6FB0; +const void * RTTI_BSPathEventManager = (void *)0x038A7B40; +const void * RTTI_MovementMessageDoor = (void *)0x038A7DF0; +const void * RTTI_MovementMessageApproachingDoor = (void *)0x038A7E20; +const void * RTTI_MovementMessageActivateDoor = (void *)0x038A7E58; +const void * RTTI_MovementMessageWarpToLocation = (void *)0x038A80D0; +const void * RTTI_MovementMessageWarpToMultiple = (void *)0x038A8350; +const void * RTTI_MovementMessageBlocked = (void *)0x038A85D0; +const void * RTTI_MovementMessagePlayIdle = (void *)0x038A8878; +const void * RTTI_MovementMessageSetStaticPath = (void *)0x038A8AE8; +const void * RTTI_MovementMessageJump = (void *)0x038A8DB0; +const void * RTTI_IMovementQueryDeltas = (void *)0x038A9040; +const void * RTTI_IMovementDeltaStore = (void *)0x038A9070; +const void * RTTI_MovementPostUpdateArbiter = (void *)0x038A90A0; +const void * RTTI_MovementArbiter = (void *)0x038A40D0; +const void * RTTI_IMovementPlannerSetArbitration = (void *)0x038A9390; +const void * RTTI_MovementPlannerArbiter = (void *)0x038A93C8; +const void * RTTI_MovementPlannerAgent = (void *)0x03799BD0; +const void * RTTI_MovementPathManagerAgentLoadedAreaMonitor = (void *)0x038A9A70; +const void * RTTI_IMovementQueryFlight = (void *)0x038A9DD8; +const void * RTTI_MovementAgentPathFollowerFlight = (void *)0x038A9E08; +const void * RTTI_MovementAgentPathFollowerVirtual = (void *)0x038AA098; +const void * RTTI_IMovementQueryActorAvoidance = (void *)0x038AA430; +const void * RTTI_MovementAgentActorAvoider = (void *)0x038AA468; +const void * RTTI_MovementHandlerAgentAngleGain = (void *)0x038AA710; +const void * RTTI_MovementHandlerAgentSpeedPassThrough = (void *)0x038AA998; +const void * RTTI_MovementHandlerAgentAnglePassThrough = (void *)0x038AAC50; +const void * RTTI_MovementHandlerAgentDirectionPassThrough = (void *)0x038AAF00; +const void * RTTI_MovementHandlerAgentStrafing = (void *)0x038AB188; +const void * RTTI_MovementPlannerAgentNavmeshBounds = (void *)0x038AB438; +const void * RTTI_MovementPathManagerAgentAvoidBox = (void *)0x038AB730; +const void * RTTI_MovementStateTransformOverride = (void *)0x038ABA68; +const void * RTTI_MovementStateTweener = (void *)0x038ABAA0; +const void * RTTI_MovementStateFollowPath = (void *)0x038ABD00; +const void * RTTI_MovementTweenerAgentTargetPosAndVel = (void *)0x038AC088; +const void * RTTI_MovementHandlerAgentStorePlannerOutput = (void *)0x038AC330; +const void * RTTI_MovementHandlerAgentStandard = (void *)0x038AC670; +const void * RTTI_MovementMessagePathEvent = (void *)0x038A5A60; +const void * RTTI_MovementMessagePathComplete = (void *)0x038AC8F8; +const void * RTTI_MovementMessagePathFailed = (void *)0x038AC930; +const void * RTTI_MovementMessagePathCleared = (void *)0x038AC960; +const void * RTTI_BSPathingSearchRayCast = (void *)0x038AD0B0; +const void * RTTI_ISmoothingRayValidator = (void *)0x038AD1C0; +const void * RTTI_GroundPathRayValidator = (void *)0x038AD1F0; +const void * RTTI_WaterPathRayValidator = (void *)0x038AD220; +const void * RTTI_IPathingNodeGenerator = (void *)0x038AD250; +const void * RTTI_GroundPathPathingNodeGenerator = (void *)0x038AD280; +const void * RTTI_WaterPathPathingNodeGenerator = (void *)0x038AD2B8; +const void * RTTI_BSNavmeshObstacleUndoData = (void *)0x038AD3F8; +const void * RTTI_VelocityObstacle__Utilities__BuildOpenEdgeListsVisitor = (void *)0x038AD4B0; +const void * RTTI_IPathFollowerState = (void *)0x038AD590; +const void * RTTI_ICheckEndReachedFunctor = (void *)0x038A6278; +const void * RTTI_PathFollowerStatePlayIdle = (void *)0x038AD5F8; +const void * RTTI_PathFollowerStateFollowPathToParameter = (void *)0x038AD690; +const void * RTTI_PathFollowerStateKeepLastDirection = (void *)0x038AD708; +const void * RTTI_PathFollowerStateShortcut = (void *)0x038AD788; +const void * RTTI_PathFollowerStateTurnToAngle = (void *)0x038AD7F0; +const void * RTTI_MovementArbitrationVectorAdd = (void *)0x038AD860; +const void * RTTI_bnet__BaseCallback = (void *)0x038AD958; +const void * RTTI_BSPlatform__BSTerminatedCallback = (void *)0x038ADA20; +const void * RTTI_BSPlatform__BSBethesdaPlatform = (void *)0x038ADA58; +const void * RTTI_BSPlatform__BSAsyncRequestBase = (void *)0x038ADDA0; +const void * RTTI_bnet__EmptyTerminationCB = (void *)0x038B0B48; +const void * RTTI_bnet__ExternalAuthInfo = (void *)0x038B0E60; +const void * RTTI_bnet__SteamExternalAuthInfo = (void *)0x038B0E90; +const void * RTTI_BSPlatform__BSUploadRequest = (void *)0x038ADF40; +const void * RTTI_BSPlatform__BSDownloadRequest = (void *)0x038ADF78; +const void * RTTI_bnet___impl__RefCount = (void *)0x038B5EA8; +const void * RTTI_bnet___impl__SharedMutex = (void *)0x038B5ED8; +const void * RTTI_bnet__IDynamicVariableDeleter = (void *)0x038B5F08; +const void * RTTI_bnet__ConfigurationEntry = (void *)0x038B5F40; +const void * RTTI_bnet___impl__IPool = (void *)0x038B5FC8; +const void * RTTI_bnet__ConfigurationEntryBoolean = (void *)0x038B5FF0; +const void * RTTI_bnet__ConfigurationEntryInteger = (void *)0x038B6028; +const void * RTTI_bnet__ConfigurationEntryFloat = (void *)0x038B6060; +const void * RTTI_bnet__ConfigurationEntryLong = (void *)0x038B6098; +const void * RTTI_bnet__ConfigurationEntryString = (void *)0x038B60D0; +const void * RTTI_bnet__IJobCollection = (void *)0x038B6360; +const void * RTTI_bnet___detail__Composite = (void *)0x038B6390; +const void * RTTI_std__bad_weak_ptr = (void *)0x038B63E8; +const void * RTTI_std___Ref_count_base = (void *)0x038B64F8; +const void * RTTI_bnet__IHeap = (void *)0x038B6580; +const void * RTTI_bnet__IHttpConnectionProvider = (void *)0x038B65A8; +const void * RTTI_bnet__Network = (void *)0x038B66B0; +const void * RTTI_bnet__UserQueue = (void *)0x038B6C38; +const void * RTTI_bnet__UserProcessorCollection = (void *)0x038B6C60; +const void * RTTI_bnet__JobProcessorCollection = (void *)0x038B6C98; +const void * RTTI_bnet___impl__AsyncOperation = (void *)0x038B6DC0; +const void * RTTI_bnet__SuspendFlow = (void *)0x038B6E50; +const void * RTTI_bnet__ResumeFlow = (void *)0x038B6FD8; +const void * RTTI_bnet__BaseHttpRequest = (void *)0x038B7248; +const void * RTTI_bnet__LogoutFlow = (void *)0x038B75B8; +const void * RTTI_bnet__IUgcUploader = (void *)0x038B7678; +const void * RTTI_bnet__ChunkedUploadFlow = (void *)0x038B76A0; +const void * RTTI_bnet__IDownloader = (void *)0x038B7870; +const void * RTTI_bnet__DownloadFlow = (void *)0x038B7898; +const void * RTTI_bnet__FileDownloader = (void *)0x038B7BC8; +const void * RTTI_bnet__UgcBrowseRequest = (void *)0x038B7C88; +const void * RTTI_bnet__UgcArgLessRequest = (void *)0x038B7D48; +const void * RTTI_bnet__UgcCategoryCountRequest = (void *)0x038B7D80; +const void * RTTI_bnet__UgcContentUploadRequest = (void *)0x038B7F10; +const void * RTTI_bnet__UgcImageUploadRequest = (void *)0x038B8040; +const void * RTTI_bnet__UgcAddVideoRequest = (void *)0x038B8078; +const void * RTTI_bnet__UgcContentRequest = (void *)0x038B80B0; +const void * RTTI_bnet__UgcContentUploadDetailsRequest = (void *)0x038B8160; +const void * RTTI_bnet__UgcCreationRequest = (void *)0x038B8228; +const void * RTTI_bnet__UgcEditRequest = (void *)0x038B82E8; +const void * RTTI_bnet__UgcFlagRequest = (void *)0x038B8318; +const void * RTTI_bnet__UgcListContentRequest = (void *)0x038B8348; +const void * RTTI_bnet__UgcListCategoriesRequest = (void *)0x038B8450; +const void * RTTI_bnet__UgcListDlcRequest = (void *)0x038B8518; +const void * RTTI_bnet__Ugc__ListPlatformsRequest = (void *)0x038B8678; +const void * RTTI_bnet__Ugc__ListProductsRequest = (void *)0x038B8778; +const void * RTTI_bnet__UgcListFollowedAuthorRequest = (void *)0x038B88E0; +const void * RTTI_bnet__UgcListNotificationRequest = (void *)0x038B89F0; +const void * RTTI_bnet__UgcListBlacklistedRequest = (void *)0x038B8B00; +const void * RTTI_bnet__UgcListFeaturedContentRequest = (void *)0x038B8C68; +const void * RTTI_bnet__UgcListByEntitlementIdsRequest = (void *)0x038B8CA8; +const void * RTTI_bnet__UgcNotificationAcknowledgeRequest = (void *)0x038B8CF0; +const void * RTTI_bnet__UgcPreviewUploadRequest = (void *)0x038B8D30; +const void * RTTI_bnet__UgcQueryRequest = (void *)0x038B8D68; +const void * RTTI_bnet__UgcRateRequest = (void *)0x038B8DA0; +const void * RTTI_bnet__UgcAddReleaseNoteRequest = (void *)0x038B8DD0; +const void * RTTI_bnet__UgcUpdateReleaseNoteRequest = (void *)0x038B8E78; +const void * RTTI_bnet__UgcRemoveReleaseNoteRequest = (void *)0x038B8EB0; +const void * RTTI_bnet__Ugc__RefreshEntitlementRequest = (void *)0x038B8EE8; +const void * RTTI_bnet__Job = (void *)0x038B63C0; +const void * RTTI_bnet__FreeLessHeap = (void *)0x038B8F28; +const void * RTTI_bnet__AcquirableFreeLessHeap = (void *)0x038B8F50; +const void * RTTI_bnet__NetworkLoop = (void *)0x038B8FA8; +const void * RTTI_bnet__UserProcessor = (void *)0x038B8FD0; +const void * RTTI_bnet__AcceptLegalDocumentsRequest = (void *)0x038B90B0; +const void * RTTI_bnet__ListLegalDocumentsFlow = (void *)0x038B9148; +const void * RTTI_bnet__AccountLinkingRequest = (void *)0x038B9300; +const void * RTTI_bnet__AccountQuickCreateFlow = (void *)0x038B93C8; +const void * RTTI_bnet__AnonymousAuthenticationFlow = (void *)0x038B9628; +const void * RTTI_bnet__SessionAnonymousCreateFlow = (void *)0x038B96F0; +const void * RTTI_bnet__SessionQuickCreateFlow = (void *)0x038B97C0; +const void * RTTI_bnet__AccountDeletionRequest = (void *)0x038B9890; +const void * RTTI_bnet__AddFingerprintRequest = (void *)0x038B98D0; +const void * RTTI_bnet__IsAccountLinkableRequest = (void *)0x038B9910; +const void * RTTI_bnet__UserCredentialsAuthenticationFlow = (void *)0x038B99B8; +const void * RTTI_bnet__ExternalAuthenticationFlow = (void *)0x038B9B40; +const void * RTTI_bnet__GameCodeAuthenticationFlow = (void *)0x038B9D38; +const void * RTTI_bnet__UnlinkAccountFlow = (void *)0x038B9E00; +const void * RTTI_bnet__UsernameValidationRequest = (void *)0x038B9FC8; +const void * RTTI_bnet__VerifyFingerprintRequest = (void *)0x038BA008; +const void * RTTI_bnet__VerifyLinkRequest = (void *)0x038BA048; +const void * RTTI_bnet__TwitchAccountInfoRequest = (void *)0x038BA080; +const void * RTTI_bnet__CheckEmailRequest = (void *)0x038BA148; +const void * RTTI_bnet__RecoverPasswordRequest = (void *)0x038BA178; +const void * RTTI_bnet__RecoverUsernameRequest = (void *)0x038BA1B0; +const void * RTTI_bnet__ResendVerificationRequest = (void *)0x038BA1E8; +const void * RTTI_bnet__SessionValidateRequest = (void *)0x038BA228; +const void * RTTI_bnet__AccountInfoRequest = (void *)0x038BA2F0; +const void * RTTI_bnet__RetrieveExternalAccountJob = (void *)0x038BA410; +const void * RTTI_bnet__ExternalServiceRetrieveAccountRequest = (void *)0x038BA670; +const void * RTTI_bnet__AccountUpgradeAnonymousToQuickRequest = (void *)0x038BA7B0; +const void * RTTI_bnet__GameAccountLinkFlow = (void *)0x038BA898; +const void * RTTI_bnet__GameAccountLinkConflictResolveFlow = (void *)0x038BA958; +const void * RTTI_bnet__GameAccountExternalLinkConflictResolveFlow = (void *)0x038BAA40; +const void * RTTI_bnet__GameAccountUserCredentialsAuthenticationFlow = (void *)0x038BAB30; +const void * RTTI_bnet__GameAccountExternalAuthenticationFlow = (void *)0x038BACD0; +const void * RTTI_bnet__GameAccountExternalLinkFlow = (void *)0x038BAE58; +const void * RTTI_bnet__GameAccountGameCodeAuthenticationFlow = (void *)0x038BAF20; +const void * RTTI_bnet__SessionResumeFlow = (void *)0x038BB008; +const void * RTTI_bnet__NewGameSessionFlow = (void *)0x038BB0C0; +const void * RTTI_bnet__Entitlements__ListRequest = (void *)0x038BB170; +const void * RTTI_bnet__Entitlements__ConsumeRequest = (void *)0x038BB248; +const void * RTTI_bnet__Entitlements__SearchRequest = (void *)0x038BB2F8; +const void * RTTI_bnet__EventLog__LogEventRequest = (void *)0x038BB330; +const void * RTTI_bnet__VCCS__Wallet__BalanceRequest = (void *)0x038BB408; +const void * RTTI_bnet__VCCS__Wallet__PurchaseRequest = (void *)0x038BB4C0; +const void * RTTI_bnet__UgcMtxPurchaseFlow = (void *)0x038BB588; +const void * RTTI_bnet__VCCS__Fulfillment__UpdateFirstPartyEntitlementRequest = (void *)0x038BB6E0; +const void * RTTI_bnet__VCCS__Catalog__GetItemRequest = (void *)0x038BB730; +const void * RTTI_bnet__VCCS__Catalog__ListItemsRequest = (void *)0x038BB7F0; +const void * RTTI_bnet__CMS__ListMessagesRequest = (void *)0x038BB8A8; +const void * RTTI_bnet__Status__GetExtServerStatusRequest = (void *)0x038BB9A8; +const void * RTTI_bnet__SessionLogoutRequest = (void *)0x038BBA80; +const void * RTTI_bnet__UgcCancelUploadRequest = (void *)0x038BBAB8; +const void * RTTI_bnet__UgcChunkedUploadCompletionRequest = (void *)0x038BBAF8; +const void * RTTI_bnet__UgcChunkedUploadInitiationRequest = (void *)0x038BBB40; +const void * RTTI_bnet__UgcChunkUploadRequest = (void *)0x038BBBE0; +const void * RTTI_bnet__CdpFileInfoRequest = (void *)0x038BBC18; +const void * RTTI_bnet__CdpKeyRequest = (void *)0x038BBCD0; +const void * RTTI_bnet__BaseFileDownloader = (void *)0x038B7BF8; +const void * RTTI_bnet__HttpTransport = (void *)0x038BBD98; +const void * RTTI_bnet__WinHttpTransport = (void *)0x038BBDC8; +const void * RTTI_bnet__SessionRefreshRequest = (void *)0x038BBE70; +const void * RTTI_bnet__SessionResumeRequest = (void *)0x038BBED8; +const void * RTTI_bnet__ListRequiredLegalDocumentsRequest = (void *)0x038BBFA0; +const void * RTTI_bnet__AuthenticationFlow = (void *)0x038B9400; +const void * RTTI_bnet__BeamQuickCreationRequest = (void *)0x038BC480; +const void * RTTI_bnet__AnonymousAuthRequest = (void *)0x038BC540; +const void * RTTI_bnet__SessionAnonymousAccountCreateRequest = (void *)0x038BC570; +const void * RTTI_bnet__SessionQuickCreateRequest = (void *)0x038BC5B0; +const void * RTTI_bnet__SessionAuthRequest = (void *)0x038BC5E8; +const void * RTTI_bnet__BeamUserCredentialsAuthenticationRequest = (void *)0x038BC6A0; +const void * RTTI_bnet__BeamExternalAuthenticationRequest = (void *)0x038BC728; +const void * RTTI_bnet__SessionExternalAuthRequest = (void *)0x038BC768; +const void * RTTI_bnet__Session__GameCodeAuthRequest = (void *)0x038BC7A0; +const void * RTTI_bnet__TokenUpgradeRequest = (void *)0x038BC7E0; +const void * RTTI_bnet__AccountUnlinkingRequest = (void *)0x038BC898; +const void * RTTI_bnet__RetrieveExternalAccountRequest = (void *)0x038BC8D0; +const void * RTTI_bnet__GameAccountLinkRequest = (void *)0x038BC990; +const void * RTTI_bnet__GameAccountLinkConflictResolveRequest = (void *)0x038BC9D0; +const void * RTTI_bnet__GameAccountExternalLinkConflictResolveRequest = (void *)0x038BCA20; +const void * RTTI_bnet__GameAccountAuthenticationRequest = (void *)0x038BCA70; +const void * RTTI_bnet__GameAccountExternalAuthRequest = (void *)0x038BCAB0; +const void * RTTI_bnet__GameAccountExternalLinkRequest = (void *)0x038BCAF0; +const void * RTTI_bnet__Session__GameAccountGameCodeAuthRequest = (void *)0x038BCB30; +const void * RTTI_bnet__SessionTokenRequest = (void *)0x038BBEA8; +const void * RTTI_bnet__CdpAuthenticationRequest = (void *)0x038BCBD8; +const void * RTTI_bnet__SessionResumeTokenRequest = (void *)0x038BCD00; +const void * RTTI_bnet__BeamAuthenticationRequest = (void *)0x038BC6E8; +const void * RTTI_BSScript__IFindBoundObjectFunctor = (void *)0x038BCED8; +const void * RTTI_BSScript__IVMObjectBindInterface = (void *)0x038BCF10; +const void * RTTI_BSScript__ObjectTypeInfo = (void *)0x038BD460; +const void * RTTI_BSScript__IComplexType = (void *)0x038BD490; +const void * RTTI_BSScript__IVMSaveLoadInterface = (void *)0x038BD4C0; +const void * RTTI_BSScript__IVirtualMachine = (void *)0x038BD4F8; +const void * RTTI_BSScript__ITypeLinkedCallback = (void *)0x038BD528; +const void * RTTI_BSScript__IVMDebugInterface = (void *)0x038BD560; +const void * RTTI_BSScript__ICachedErrorMessage = (void *)0x0380AD70; +const void * RTTI_BSScript__ErrorLogger = (void *)0x0380E178; +const void * RTTI_BSScript__IFunction = (void *)0x0380AD40; +const void * RTTI_BSScript__NF_util__NativeFunctionBase = (void *)0x0380AE58; +const void * RTTI_BSScript__StructTypeInfo = (void *)0x038BDFE0; +const void * RTTI_BSScript__IMemoryPagePolicy = (void *)0x038BE130; +const void * RTTI_BSScript__SimpleAllocMemoryPagePolicy = (void *)0x038BE168; +const void * RTTI_BSScript__CompiledScriptLoader = (void *)0x038BE1C0; +const void * RTTI_BSScript__Internal__VirtualMachine = (void *)0x038BED10; +const void * RTTI_BSScript__Internal__IFuncCallQuery = (void *)0x038BED88; +const void * RTTI_BSScript__Internal__RawFuncCallQuery = (void *)0x038BEDC0; +const void * RTTI_BSScript__IObjectProcessor = (void *)0x038BEE00; +const void * RTTI_BSScript__Internal__AutoPropGetFunction = (void *)0x038C0218; +const void * RTTI_BSScript__Internal__AutoPropSetFunction = (void *)0x038C0258; +const void * RTTI_BSScript__Internal__CodeTasklet = (void *)0x038C04A0; +const void * RTTI_BSScript__ByteCode__PackedInstructionStream__InstructionFunctor = (void *)0x038C2970; +const void * RTTI_BSScript__Internal__NativeFunctionStub = (void *)0x038C29C8; +const void * RTTI_BSScript__UnlinkedTypes__InstructionStream__InstructionFunctor = (void *)0x038C2A10; +const void * RTTI_BSScript__LinkerProcessor = (void *)0x038C3280; +const void * RTTI_BSScript__Internal__ScriptFunction = (void *)0x038C4140; +const void * RTTI_BSPrecomputedVisibility__MultiCellVisibilityData__TomeCollectionBuildTask = (void *)0x038C4230; +const void * RTTI_BSShaderProperty = (void *)0x03783740; +const void * RTTI_BSLightingShaderMaterial = (void *)0x038C4B70; +const void * RTTI_BSEffectShaderProperty = (void *)0x03783768; +const void * RTTI_BSLightingShaderProperty = (void *)0x038C4DE8; +const void * RTTI_BSFadeNode = (void *)0x036E2650; +const void * RTTI_BSLeafAnimNode = (void *)0x038C5008; +const void * RTTI_BSTreeNode = (void *)0x038C5068; +const void * RTTI_BSImagespaceShaderVatsTargetDebug = (void *)0x038C5318; +const void * RTTI_BSImagespaceShaderVatsTarget = (void *)0x038C53A8; +const void * RTTI_BSImagespaceShaderAlphaBlend = (void *)0x038C53E0; +const void * RTTI_BSImagespaceShaderBlur3 = (void *)0x038C5418; +const void * RTTI_BSImagespaceShaderBlur5 = (void *)0x038C5448; +const void * RTTI_BSImagespaceShaderBlur7 = (void *)0x038C5478; +const void * RTTI_BSImagespaceShaderBlur9 = (void *)0x038C54A8; +const void * RTTI_BSImagespaceShaderBlur11 = (void *)0x038C54D8; +const void * RTTI_BSImagespaceShaderBlur13 = (void *)0x038C5508; +const void * RTTI_BSImagespaceShaderBlur15 = (void *)0x038C5538; +const void * RTTI_BSImagespaceShaderNonHDRBlur3 = (void *)0x038C5568; +const void * RTTI_BSImagespaceShaderNonHDRBlur5 = (void *)0x038C55A0; +const void * RTTI_BSImagespaceShaderNonHDRBlur7 = (void *)0x038C55D8; +const void * RTTI_BSImagespaceShaderNonHDRBlur9 = (void *)0x038C5610; +const void * RTTI_BSImagespaceShaderNonHDRBlur11 = (void *)0x038C5648; +const void * RTTI_BSImagespaceShaderNonHDRBlur13 = (void *)0x038C5680; +const void * RTTI_BSImagespaceShaderNonHDRBlur15 = (void *)0x038C56B8; +const void * RTTI_BSImagespaceShaderBrightPassBlur3 = (void *)0x038C56F0; +const void * RTTI_BSImagespaceShaderBrightPassBlur5 = (void *)0x038C5728; +const void * RTTI_BSImagespaceShaderBrightPassBlur7 = (void *)0x038C5760; +const void * RTTI_BSImagespaceShaderBrightPassBlur9 = (void *)0x038C5798; +const void * RTTI_BSImagespaceShaderBrightPassBlur11 = (void *)0x038C57D0; +const void * RTTI_BSImagespaceShaderBrightPassBlur13 = (void *)0x038C5810; +const void * RTTI_BSImagespaceShaderBrightPassBlur15 = (void *)0x038C5850; +const void * RTTI_BSImagespaceShaderHDRBlurX15_320CS = (void *)0x038C5890; +const void * RTTI_BSImagespaceShaderHDRBlurX15_480CS = (void *)0x038C5900; +const void * RTTI_BSImagespaceShaderHDRBlurX15_1024CS = (void *)0x038C5940; +const void * RTTI_BSImagespaceShaderBlurX15_480CS = (void *)0x038C5980; +const void * RTTI_BSImagespaceShaderBlurX13_480CS = (void *)0x038C59B8; +const void * RTTI_BSImagespaceShaderBlurX11_480CS = (void *)0x038C59F0; +const void * RTTI_BSImagespaceShaderBlurX9_480CS = (void *)0x038C5A28; +const void * RTTI_BSImagespaceShaderBlurX7_480CS = (void *)0x038C5A60; +const void * RTTI_BSImagespaceShaderBlurX5_480CS = (void *)0x038C5A98; +const void * RTTI_BSImagespaceShaderBlurX3_480CS = (void *)0x038C5AD0; +const void * RTTI_BSImagespaceShaderBlurY15_270CS = (void *)0x038C5B08; +const void * RTTI_BSImagespaceShaderBlurY13_270CS = (void *)0x038C5B40; +const void * RTTI_BSImagespaceShaderBlurY11_270CS = (void *)0x038C5B78; +const void * RTTI_BSImagespaceShaderBlurY9_270CS = (void *)0x038C5BB0; +const void * RTTI_BSImagespaceShaderBlurY7_270CS = (void *)0x038C5BE8; +const void * RTTI_BSImagespaceShaderBlurY5_270CS = (void *)0x038C5C20; +const void * RTTI_BSImagespaceShaderBlurY3_270CS = (void *)0x038C5C58; +const void * RTTI_BSImagespaceShaderBrightPassHDRBlurY15_180CS = (void *)0x038C5C90; +const void * RTTI_BSImagespaceShaderBrightPassHDRBlurY15_270CS = (void *)0x038C5CE0; +const void * RTTI_BSImagespaceShaderBrightPassHDRBlurY15_1024CS = (void *)0x038C5D30; +const void * RTTI_BSImagespaceShaderBrightPassBlurY15_270CS = (void *)0x038C5D80; +const void * RTTI_BSImagespaceShaderBrightPassBlurY13_270CS = (void *)0x038C5DC0; +const void * RTTI_BSImagespaceShaderBrightPassBlurY11_270CS = (void *)0x038C5E00; +const void * RTTI_BSImagespaceShaderBrightPassBlurY9_270CS = (void *)0x038C5E40; +const void * RTTI_BSImagespaceShaderBrightPassBlurY7_270CS = (void *)0x038C5E80; +const void * RTTI_BSImagespaceShaderBrightPassBlurY5_270CS = (void *)0x038C5EC0; +const void * RTTI_BSImagespaceShaderBrightPassBlurY3_270CS = (void *)0x038C5F00; +const void * RTTI_BSImagespaceShaderCopy = (void *)0x038C5F40; +const void * RTTI_BSImagespaceShaderCopyScaleBias = (void *)0x038C5F70; +const void * RTTI_BSImagespaceShaderCopyVisAlpha = (void *)0x038C5FA8; +const void * RTTI_BSImagespaceShaderTextureMask = (void *)0x038C5FE0; +const void * RTTI_BSImagespaceShaderGreyScale = (void *)0x038C6018; +const void * RTTI_BSImagespaceShaderDownsampleDepth = (void *)0x038C6050; +const void * RTTI_BSImagespaceShaderCopyStencil = (void *)0x038C6088; +const void * RTTI_BSImagespaceShaderCopyWaterMask = (void *)0x038C60C0; +const void * RTTI_BSImagespaceShaderCopyShadowMapToArray = (void *)0x038C60F8; +const void * RTTI_BSImagespaceShaderDepthOfField = (void *)0x038C6138; +const void * RTTI_BSImagespaceShaderDepthOfFieldFogged = (void *)0x038C6170; +const void * RTTI_BSImagespaceShaderDepthOfFieldSplitScreen = (void *)0x038C61B0; +const void * RTTI_BSImagespaceShaderDistantBlur = (void *)0x038C61F0; +const void * RTTI_BSImagespaceShaderDistantBlurFogged = (void *)0x038C6228; +const void * RTTI_BSImagespaceShaderDoubleVision = (void *)0x038C6268; +const void * RTTI_BSImagespaceShaderFullScreenColor = (void *)0x038C62A0; +const void * RTTI_BSImagespaceShaderFXAA = (void *)0x038C62D8; +const void * RTTI_BSImagespaceShaderTemporalAA = (void *)0x038C6308; +const void * RTTI_BSImagespaceShaderTemporalAAPipboy = (void *)0x038C6340; +const void * RTTI_BSImagespaceShaderTemporalAAPowerArmorPipboy = (void *)0x038C6380; +const void * RTTI_BSImagespaceShaderGammaCorrect = (void *)0x038C63C8; +const void * RTTI_BSImagespaceShaderGammaCorrectResize = (void *)0x038C6400; +const void * RTTI_BSImagespaceShaderGammaLinearize = (void *)0x038C6440; +const void * RTTI_BSImagespaceShaderGammaCorrectLUT = (void *)0x038C6478; +const void * RTTI_BSImagespaceShaderHDRDownSample4 = (void *)0x038C64B0; +const void * RTTI_BSImagespaceShaderHDRDownSample16Lum = (void *)0x038C64E8; +const void * RTTI_BSImagespaceShaderHDRDownSample4RGB2Lum = (void *)0x038C6528; +const void * RTTI_BSImagespaceShaderHDRDownSample16 = (void *)0x038C6568; +const void * RTTI_BSImagespaceShaderHDRDownSample4LumClamp = (void *)0x038C65A0; +const void * RTTI_BSImagespaceShaderHDRDownSample16LumClamp = (void *)0x038C65E0; +const void * RTTI_BSImagespaceShaderHDRDownSample4LightAdapt = (void *)0x038C6620; +const void * RTTI_BSImagespaceShaderHDRDownSample16LightAdapt = (void *)0x038C6670; +const void * RTTI_BSImagespaceShaderHDRTonemapBlendCinematic = (void *)0x038C66C0; +const void * RTTI_BSImagespaceShaderHDRTonemapBlendCinematicFade = (void *)0x038C6710; +const void * RTTI_BSImagespaceShaderHDRDownSample4CS = (void *)0x038C6758; +const void * RTTI_BSImagespaceShaderHDRDownSample4LumCS = (void *)0x038C6798; +const void * RTTI_BSImagespaceShaderHDRDownSample16LumCS = (void *)0x038C67D8; +const void * RTTI_BSImagespaceShaderHDRDownSample64RGB2LumCS = (void *)0x038C6820; +const void * RTTI_BSImagespaceShaderHDRDownSample2LightAdaptCS = (void *)0x038C6870; +const void * RTTI_BSImagespaceShaderLocalMap = (void *)0x038C68B8; +const void * RTTI_BSImagespaceShaderLocalMapCompanion = (void *)0x038C68F0; +const void * RTTI_BSImagespaceShaderMap = (void *)0x038C6930; +const void * RTTI_BSImagespaceShaderNoiseScrollAndBlend = (void *)0x038C6960; +const void * RTTI_BSImagespaceShaderNoiseNormalmap = (void *)0x038C69A0; +const void * RTTI_BSImagespaceShaderRadialBlur = (void *)0x038C69D8; +const void * RTTI_BSImagespaceShaderRadialBlurMedium = (void *)0x038C6A10; +const void * RTTI_BSImagespaceShaderRadialBlurHigh = (void *)0x038C6A50; +const void * RTTI_BSImagespaceShaderRefraction = (void *)0x038C6A88; +const void * RTTI_BSImagespaceShaderWaterDisplacementClearSimulation = (void *)0x038C6AC0; +const void * RTTI_BSImagespaceShaderWaterDisplacementTexOffset = (void *)0x038C6B10; +const void * RTTI_BSImagespaceShaderWaterDisplacementWadingRipple = (void *)0x038C6B60; +const void * RTTI_BSImagespaceShaderWaterDisplacementRainRipple = (void *)0x038C6BB0; +const void * RTTI_BSImagespaceShaderWaterWadingHeightmap = (void *)0x038C6BF8; +const void * RTTI_BSImagespaceShaderWaterRainHeightmap = (void *)0x038C6C38; +const void * RTTI_BSImagespaceShaderWaterBlendHeightmaps = (void *)0x038C6C78; +const void * RTTI_BSImagespaceShaderWaterSmoothHeightmap = (void *)0x038C6CB8; +const void * RTTI_BSImagespaceShaderWaterDisplacementNormals = (void *)0x038C6D00; +const void * RTTI_BSImagespaceShaderPipboyScreen = (void *)0x038C6D48; +const void * RTTI_BSImagespaceShaderHUDGlass = (void *)0x038C6D80; +const void * RTTI_BSImagespaceShaderHUDGlassDropShadow = (void *)0x038C6DB8; +const void * RTTI_BSImagespaceShaderHUDGlassBlurY = (void *)0x038C6DF8; +const void * RTTI_BSImagespaceShaderHUDGlassBlurX = (void *)0x038C6E30; +const void * RTTI_BSImagespaceShaderHUDGlassMarkers = (void *)0x038C6E68; +const void * RTTI_BSImagespaceShaderHUDGlassClear = (void *)0x038C6EA0; +const void * RTTI_BSImagespaceShaderHUDGlassCopy = (void *)0x038C6ED8; +const void * RTTI_BSImagespaceShaderVLSSliceCoord = (void *)0x038C6F10; +const void * RTTI_BSImagespaceShaderVLSSliceInterp = (void *)0x038C6F48; +const void * RTTI_BSImagespaceShaderVLSSliceStencil = (void *)0x038C6F80; +const void * RTTI_BSImagespaceShaderVLSSliceScatterRay = (void *)0x038C6FB8; +const void * RTTI_BSImagespaceShaderVLSSliceScatterInterp = (void *)0x038C6FF8; +const void * RTTI_BSImagespaceShaderVLSScatterAccum = (void *)0x038C7038; +const void * RTTI_BSImagespaceShaderVLSSpotLight = (void *)0x038C7070; +const void * RTTI_BSImagespaceShaderVLSApplication = (void *)0x038C70A8; +const void * RTTI_BSImagespaceShaderVLSComposite = (void *)0x038C70E0; +const void * RTTI_BSImagespaceShaderModMenuEffect = (void *)0x038C7118; +const void * RTTI_BSImagespaceShaderModMenuGlowComposite = (void *)0x038C7150; +const void * RTTI_BSImagespaceShaderAmbientOcclusion = (void *)0x038C7190; +const void * RTTI_BSImagespaceShaderAmbientOcclusionBlur = (void *)0x038C71D0; +const void * RTTI_BSImagespaceShaderMotionBlur = (void *)0x038C7210; +const void * RTTI_BSImagespaceShaderSAOCameraZ = (void *)0x038C7248; +const void * RTTI_BSImagespaceShaderSAOMinify = (void *)0x038C7280; +const void * RTTI_BSImagespaceShaderSAORawAO = (void *)0x038C72B8; +const void * RTTI_BSImagespaceShaderSAOBlurH = (void *)0x038C72F0; +const void * RTTI_BSImagespaceShaderSAOBlurV = (void *)0x038C7328; +const void * RTTI_BSImagespaceShaderSAOCameraZCS = (void *)0x038C7360; +const void * RTTI_BSImagespaceShaderSAOCameraAndMipsZCS = (void *)0x038C7398; +const void * RTTI_BSImagespaceShaderSAOMipsZCS = (void *)0x038C73D8; +const void * RTTI_BSImagespaceShaderSAOMinifyCS = (void *)0x038C7410; +const void * RTTI_BSImagespaceShaderSAORawAOCS = (void *)0x038C7448; +const void * RTTI_BSImagespaceShaderSAOBlurHCS = (void *)0x038C7480; +const void * RTTI_BSImagespaceShaderSAOBlurVCS = (void *)0x038C74B8; +const void * RTTI_BSImagespaceShaderSAORawAOEditor = (void *)0x038C74F0; +const void * RTTI_BSImagespaceShaderSSLRBlurH = (void *)0x038C7528; +const void * RTTI_BSImagespaceShaderSSLRBlurV = (void *)0x038C7560; +const void * RTTI_BSImagespaceShaderSSLRRaytracing = (void *)0x038C7598; +const void * RTTI_BSImagespaceShaderSSLRPrepass = (void *)0x038C75D0; +const void * RTTI_BSImagespaceShaderSunbeams = (void *)0x038C7608; +const void * RTTI_BSImagespaceShaderUpsampleDynamicResolution = (void *)0x038C7640; +const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass1 = (void *)0x038C7688; +const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass2 = (void *)0x038C76C8; +const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass3 = (void *)0x038C7708; +const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass4 = (void *)0x038C7748; +const void * RTTI_BSImagespaceShaderBokehDepthOfFieldPass4Fogged = (void *)0x038C7790; +const void * RTTI_BSImagespaceShaderRainSplash = (void *)0x038C77D8; +const void * RTTI_BSImagespaceShaderRainSplashUpdate = (void *)0x038C7810; +const void * RTTI_BSImagespaceShaderRainSplashDraw = (void *)0x038C7850; +const void * RTTI_BSDeferredDecal__BSDFDecal = (void *)0x038C79B8; +const void * RTTI_ShadowSceneNode = (void *)0x038C79E8; +const void * RTTI_BSImagespaceShaderLensFlare = (void *)0x038C7B50; +const void * RTTI_BSImagespaceShaderLensFlareVisibility = (void *)0x038C7B88; +const void * RTTI_BSGrassShaderProperty = (void *)0x038C7C50; +const void * RTTI_BSLightingShaderMaterialEnvmap = (void *)0x038C7D40; +const void * RTTI_BSLightingShaderMaterialBase = (void *)0x038C4BA0; +const void * RTTI_BSLightingShaderMaterialEye = (void *)0x038C7D78; +const void * RTTI_BSLightingShaderMaterialGlowmap = (void *)0x038C7DB0; +const void * RTTI_BSLightingShaderMaterialParallax = (void *)0x038C7DE8; +const void * RTTI_BSLightingShaderMaterialParallaxOcc = (void *)0x038C7E20; +const void * RTTI_BSLightingShaderMaterialDismemberment = (void *)0x038C7E60; +const void * RTTI_BSLightingShaderMaterialFace = (void *)0x038C7EA0; +const void * RTTI_BSLightingShaderMaterialSkinTint = (void *)0x038C7ED8; +const void * RTTI_BSLightingShaderMaterialHairTint = (void *)0x038C7F10; +const void * RTTI_BSLightingShaderMaterialLandscape = (void *)0x038C7F48; +const void * RTTI_BSLightingShaderMaterialLODLandscape = (void *)0x038C7F80; +const void * RTTI_BSLightingShaderMaterialSnow = (void *)0x038C7FC0; +const void * RTTI_BSLightingShaderMaterialMultiLayerParallax = (void *)0x038C8000; +const void * RTTI_ImageSpaceEffectParam = (void *)0x038C8048; +const void * RTTI_ImageSpaceShaderParam = (void *)0x038C8078; +const void * RTTI_BSShaderAccumulator = (void *)0x038C8240; +const void * RTTI_BSWaterShaderProperty = (void *)0x038C8290; +const void * RTTI_BSBloodSplatterShaderProperty = (void *)0x038C82C0; +const void * RTTI_BSBloodSplatterShader = (void *)0x038C8300; +const void * RTTI_ImageSpaceEffectVatsTarget = (void *)0x038C8360; +const void * RTTI_NVFlex__DebrisInstanceNode = (void *)0x038C8398; +const void * RTTI_NVFlex__DebrisInstanceGroup = (void *)0x038C85D0; +const void * RTTI_NVFlex__DebrisInstanceGroupPass = (void *)0x038C8640; +const void * RTTI_BSDismember__BSShaderDismembermentExtraData = (void *)0x038C9260; +const void * RTTI_BSShaderMaterial = (void *)0x038C4BD8; +const void * RTTI_BSPackedCombinedGeomDataExtra = (void *)0x038C92C8; +const void * RTTI_BSPackedSharedGeometryBuilder = (void *)0x038C9300; +const void * RTTI_BSPackedCombinedSharedGeomDataExtra = (void *)0x038C9338; +const void * RTTI_BSGeometryListCullingProcess = (void *)0x038C9480; +const void * RTTI_BSImagespaceShaderCopyParam = (void *)0x038C94B8; +const void * RTTI_BSLight = (void *)0x038C9518; +const void * RTTI_BSDFPrePassShader = (void *)0x038C9610; +const void * RTTI_ImageSpaceEffectHDR = (void *)0x038C9878; +const void * RTTI_ImageSpaceEffectAmbientOcclusion = (void *)0x038C98F8; +const void * RTTI_ImageSpaceEffectScalableAmbientObscuranceCS = (void *)0x038C9950; +const void * RTTI_ImageSpaceEffectSunbeams = (void *)0x038C9AA0; +const void * RTTI_ImageSpaceEffectMotionBlur = (void *)0x038C9AE8; +const void * RTTI_ImageSpaceEffectScalableAmbientObscurance = (void *)0x038C9B80; +const void * RTTI_ImageSpaceEffectTemporalAA = (void *)0x038C9C50; +const void * RTTI_ImageSpaceEffectOption = (void *)0x038C9CA0; +const void * RTTI_ImageSpaceEffectBokehDepthOfField = (void *)0x038C9D40; +const void * RTTI_BSDFCompositeShader = (void *)0x038C9EA0; +const void * RTTI_BSSkyShader = (void *)0x038C9F20; +const void * RTTI_BSSkyShaderProperty = (void *)0x038C9F48; +const void * RTTI_ImageSpaceEffectRainSplash = (void *)0x038CA008; +const void * RTTI_BSBatchRenderer = (void *)0x038CA080; +const void * RTTI_BSFaceCustomizationShader = (void *)0x038CA0A8; +const void * RTTI_BSEffectShaderMaterial = (void *)0x038CA0D8; +const void * RTTI_BSEffectShader = (void *)0x038CA138; +const void * RTTI_BSDistantTreeShaderProperty = (void *)0x038CA160; +const void * RTTI_BSDistantTreeShader = (void *)0x038CA198; +const void * RTTI_BSWaterShaderMaterial = (void *)0x038CA1C8; +const void * RTTI_BSCubeMapCamera = (void *)0x038CA1F8; +const void * RTTI_BSWaterShader = (void *)0x038CA240; +const void * RTTI_ImageSpaceEffectWaterDisplacement = (void *)0x038CA268; +const void * RTTI_ImageSpaceEffectModMenu = (void *)0x038CA3E0; +const void * RTTI_BSShader = (void *)0x038C5218; +const void * RTTI_BSReloadShaderI = (void *)0x038C5238; +const void * RTTI_ImageSpaceEffectPipboyScreen = (void *)0x038CA4C8; +const void * RTTI_ImageSpaceEffectBlur = (void *)0x038CAC20; +const void * RTTI_ImageSpaceEffectFullScreenBlur = (void *)0x038CAC50; +const void * RTTI_ImageSpaceEffectGetHit = (void *)0x038CAC88; +const void * RTTI_ImageSpaceEffectRadialBlur = (void *)0x038CACE8; +const void * RTTI_BSClearZNode = (void *)0x038CAD20; +const void * RTTI_BSFogProperty = (void *)0x038CAD48; +const void * RTTI_BSLightingShader = (void *)0x038CADA8; +const void * RTTI_ImageSpaceEffectFullScreenColor = (void *)0x038CADD0; +const void * RTTI_BSInstanceGroup = (void *)0x038CAE08; +const void * RTTI_BSCombinedNode = (void *)0x038CAE30; +const void * RTTI_BSEyeCenterExtraData = (void *)0x038CAE58; +const void * RTTI_BSUtilityShader = (void *)0x038CAE98; +const void * RTTI_BSMeshLODTriShape = (void *)0x038CAEC0; +const void * RTTI_BSMultiIndexTriShape = (void *)0x038CAF18; +const void * RTTI_BSOrderedNode = (void *)0x038CAF48; +const void * RTTI_BSParticleShader = (void *)0x038CAF70; +const void * RTTI_BSDFLightShader = (void *)0x038CAFD0; +const void * RTTI_BSLightingShaderPropertyFloatController = (void *)0x038CB0D0; +const void * RTTI_BSLightingShaderPropertyUShortController = (void *)0x038CB160; +const void * RTTI_BSLightingShaderPropertyColorController = (void *)0x038CB1F0; +const void * RTTI_BSEffectShaderPropertyFloatController = (void *)0x038CB280; +const void * RTTI_BSEffectShaderPropertyColorController = (void *)0x038CB310; +const void * RTTI_BSNiAlphaPropertyTestRefController = (void *)0x038CB350; +const void * RTTI_BSPSysSimpleColorModifier = (void *)0x038CB390; +const void * RTTI_BSPSysLODModifier = (void *)0x038CB3C0; +const void * RTTI_BSImagespaceShader = (void *)0x038C5350; +const void * RTTI_BSShadowLight = (void *)0x038CB3F0; +const void * RTTI_BSShadowDirectionalLight = (void *)0x038CB4B0; +const void * RTTI_ImageSpaceEffect = (void *)0x038C5380; +const void * RTTI_ImageSpaceEffectRefraction = (void *)0x038CB7A8; +const void * RTTI_ImageSpaceEffectDepthOfField = (void *)0x038CB810; +const void * RTTI_ImageSpaceEffectDepthOfFieldSplitScreen = (void *)0x038CB848; +const void * RTTI_ImageSpaceEffectBlurCS = (void *)0x038CB8A0; +const void * RTTI_ImageSpaceEffectHDRCS = (void *)0x038CB920; +const void * RTTI_ImageSpaceEffectMap = (void *)0x038CB950; +const void * RTTI_ImageSpaceEffectNoise = (void *)0x038CBA80; +const void * RTTI_ImageSpaceEffectHUDGlass = (void *)0x038CBB50; +const void * RTTI_ImageSpaceEffectVLSLight = (void *)0x038CBB88; +const void * RTTI_ImageSpaceEffectVLS = (void *)0x038CBBB8; +const void * RTTI_ImageSpaceEffectUpsampleDynamicResolution = (void *)0x038CBBF0; +const void * RTTI_BSImagespaceComputeShader = (void *)0x038C58D0; +const void * RTTI_BSShadowFrustumLight = (void *)0x038CBC48; +const void * RTTI_BSShadowParabolicLight = (void *)0x038CBC98; +const void * RTTI_NVFlex__DebrisInstanceData = (void *)0x038CBCC8; +const void * RTTI_NVFlex__DebrisInstanceDataOwner = (void *)0x038C8608; +const void * RTTI_BSComputeShader = (void *)0x038CBCF8; +const void * RTTI_BSLODMultiIndexTriShape = (void *)0x038CBD20; +const void * RTTI_BSDirectoryMonitorThread = (void *)0x038CBD90; +const void * RTTI_BSShaderResourceManager = (void *)0x038CBDC8; +const void * RTTI_BSSystemMonitor__SocketThread = (void *)0x038CBE38; +const void * RTTI_BSSystemMonitor__MemOpsThread = (void *)0x038CBE70; +const void * RTTI_BSGameDataSystemUtility = (void *)0x038CBF60; +const void * RTTI_BSSaveDataSystemUtility = (void *)0x038CBFA8; +const void * RTTI_BSSystemUtility = (void *)0x03827828; +const void * RTTI_BSWin32GameDataSystemUtility = (void *)0x038CBFD8; +const void * RTTI_BSSysInfoSystemUtility = (void *)0x038CC010; +const void * RTTI_BSGameStreamUtility = (void *)0x038CC040; +const void * RTTI_BSAwardsSystemUtility = (void *)0x03827888; +const void * RTTI_BSCacheDriveSystemUtility = (void *)0x038CC078; +const void * RTTI_BSSystemUtilitiesStrings = (void *)0x038CC0A8; +const void * RTTI_BSMsgDialogSystemUtility = (void *)0x038CC0D8; +const void * RTTI_BSDiscBootSystemUtility = (void *)0x038CC108; +const void * RTTI_type_info = (void *)0x038CC160; +const void * RTTI_BSSocketServer = (void *)0x038CC848; +const void * RTTI_NiCollisionObject = (void *)0x03849F10; +const void * RTTI_BSLines = (void *)0x038CC890; +const void * RTTI_BSDynamicLines = (void *)0x038CC8B8; +const void * RTTI_BSParabolicCullingProcess = (void *)0x038CC8E0; diff --git a/f4se/f4se/GameReferences.cpp b/f4se/f4se/GameReferences.cpp new file mode 100644 index 0000000..b1f832a --- /dev/null +++ b/f4se/f4se/GameReferences.cpp @@ -0,0 +1,85 @@ +#include "f4se/GameReferences.h" +#include "f4se/GameExtraData.h" +#include "f4se/GameRTTI.h" + +// 1FA931E3C3B406454210A0EDC37BDD0C84C8C04A+6B +RelocPtr g_player(0x05AA4388); + +// 0BE70664D4DF11A3F88748D9CB45B23D0B4FD50C+2F +RelocAddr <_LookupREFRByHandle> LookupREFRByHandle(0x0000AB60); + +// 4369DFCC9BCC88536EEB89E5A107B60941016295+26 +RelocAddr <_CreateHandleByREFR> CreateHandleByREFR(0x0000A8A0); + +// 3A53807E195FFAEA7AA0EF7FD42D84E4EA0B755A+D0 +RelocPtr g_invalidRefHandle(0x038CCE04); + +RelocAddr <_HasDetectionLOS> HasDetectionLOS(0x0135B680); + +RelocAddr <_GetLinkedRef_Native> GetLinkedRef_Native(0x00481000); + +RelocAddr <_SetLinkedRef_Native> SetLinkedRef_Native(0x00481020); + +RelocAddr <_MoveRefrToPosition> MoveRefrToPosition(0x013FE7E0); + +UInt32 TESObjectREFR::CreateRefHandle(void) +{ + if (handleRefObject.GetRefCount() > 0) + { + UInt32 refHandle = 0; + CreateHandleByREFR(&refHandle, this); + return refHandle; + } + else + { + return *g_invalidRefHandle; + } +} + +bool Actor::GetEquippedExtraData(UInt32 slotIndex, ExtraDataList ** extraData) +{ + // Invalid slot id + if (slotIndex >= ActorEquipData::kMaxSlots) + return false; + + // This should be possible but check anyway + if (!equipData) + return false; + + // Make sure there is an item in this slot + auto item = equipData->slots[slotIndex].item; + if (!item) + return false; + + if (!inventoryList) + return false; + + // Find the equipped form from the inventory + for (UInt32 i = 0; i < inventoryList->items.count; i++) + { + BGSInventoryItem inventoryItem; + inventoryList->items.GetNthItem(i, inventoryItem); + if (inventoryItem.form != item || !inventoryItem.stack) + continue; + + // Search stacks for the equipped stack + bool ret = inventoryItem.stack->Visit([&](BGSInventoryItem::Stack * stack) + { + if (stack->flags & BGSInventoryItem::Stack::kFlagEquipped) + { + ExtraDataList * stackDataList = stack->extraData; + if (stackDataList) { + (*extraData) = stackDataList; + } + + return false; + } + + return true; + }); + if (!ret) // Stack found + break; + } + + return (*extraData) != nullptr; +} \ No newline at end of file diff --git a/f4se/f4se/GameReferences.h b/f4se/f4se/GameReferences.h new file mode 100644 index 0000000..679bbb6 --- /dev/null +++ b/f4se/f4se/GameReferences.h @@ -0,0 +1,521 @@ +#pragma once + +#include "f4se/GameForms.h" +#include "f4se/GameEvents.h" +#include "f4se/GameCustomization.h" +#include "f4se/NiObjects.h" + +class BSActiveGraphIfInactiveEvent; +class BSAnimationGraphEvent; + +class TESObjectCELL; +class NiNode; +class ExtraDataList; +class TESWorldSpace; +class BGSScene; +class TESQuest; + +typedef bool (* _CreateHandleByREFR)(UInt32 * handle, TESObjectREFR * ref); +extern RelocAddr <_CreateHandleByREFR> CreateHandleByREFR; + +typedef bool (* _LookupREFRByHandle)(UInt32 * handle, TESObjectREFR ** ref); +extern RelocAddr <_LookupREFRByHandle> LookupREFRByHandle; + +extern RelocPtr g_invalidRefHandle; + +typedef bool (* _HasDetectionLOS)(Actor* source, TESObjectREFR* target, UInt8 * unk1); +extern RelocAddr<_HasDetectionLOS> HasDetectionLOS; + +typedef TESObjectREFR* (* _GetLinkedRef_Native)(TESObjectREFR* target, BGSKeyword * keyword); +extern RelocAddr<_GetLinkedRef_Native> GetLinkedRef_Native; + +typedef void (* _SetLinkedRef_Native)(TESObjectREFR* target, TESObjectREFR* linked, BGSKeyword * keyword); +extern RelocAddr<_SetLinkedRef_Native> SetLinkedRef_Native; + +typedef void (* _MoveRefrToPosition)(TESObjectREFR* source, UInt32* pTargetHandle, TESObjectCELL* parentCell, TESWorldSpace* worldSpace, NiPoint3* postion, NiPoint3* rotation); +extern RelocAddr<_MoveRefrToPosition> MoveRefrToPosition; + +// 10 +class BSHandleRefObject : public NiRefObject +{ +public: + enum + { + kMask_RefCount = 0x3FF + }; + + UInt32 GetRefCount() const + { + return m_uiRefCount & kMask_RefCount; + } + + void DecRefHandle() + { + if((InterlockedDecrement(&m_uiRefCount) & kMask_RefCount) == 0) + DeleteThis(); + } +}; + +// 110 +class TESObjectREFR : public TESForm +{ +public: + virtual void Unk_48(); + virtual void Unk_49(); + virtual void Unk_4A(); + virtual void Unk_4B(); + virtual void Unk_4C(); + virtual void Unk_4D(); + virtual void Unk_4E(); + virtual void Unk_4F(); + virtual void Unk_50(); + virtual void Unk_51(); + virtual void Unk_52(); + virtual void Unk_53(); + virtual void Unk_54(); + virtual void Unk_55(); + virtual void Unk_56(); + virtual void Unk_57(); + virtual void Unk_58(); + virtual void Unk_59(); + virtual void Unk_5A(); + virtual void Unk_5B(); + virtual BGSScene* GetCurrentScene(); // 5C Returns the Scene this reference is currently participating in, or NULL if it isn't in a scene. + virtual void Unk_5D(); + virtual void Unk_5E(); + virtual void Unk_5F(); + virtual void Unk_60(); + virtual void Unk_61(); + virtual void Unk_62(); + virtual void Unk_63(); + virtual void Unk_64(); + virtual void Unk_65(); + virtual void Unk_66(); + virtual void Unk_67(); + virtual void Unk_68(); + virtual void Unk_69(); + virtual void Unk_6A(); + virtual void Unk_6B(); + virtual void Unk_6C(); + virtual void Unk_6D(); + virtual void Unk_6E(); + virtual void Unk_6F(); + virtual void Unk_70(); + virtual void Unk_71(); + virtual void Unk_72(); + virtual void Unk_73(); + virtual void Unk_74(); + virtual void Unk_75(); + virtual void Unk_76(); + virtual void Unk_77(); + virtual void Unk_78(); + virtual void Unk_79(); + virtual void Unk_7A(); + virtual void GetMarkerPosition(NiPoint3 * pos); + virtual void Unk_7C(); + virtual void Unk_7D(); + virtual void Unk_7E(); + virtual void Unk_7F(); + virtual void Unk_80(); + virtual void Unk_81(); + virtual void Unk_82(); + virtual void Unk_83(); + virtual void Unk_84(); + virtual void Unk_85(); + virtual void Unk_86(); + virtual void Unk_87(); + virtual void Unk_88(); + virtual void Unk_89(); + virtual void Unk_8A(); + virtual NiNode * GetActorRootNode(bool firstPerson); // 8B - Returns either first person or third person + virtual NiNode * GetObjectRootNode(); // 8C - Returns the 3rd person skeleton + virtual void Unk_8D(); + virtual void Unk_8E(); + virtual void Unk_8F(); + virtual void Unk_90(); + virtual TESRace * GetActorRace(); // 91 + virtual void Unk_92(); + virtual void Unk_93(); + virtual void Unk_94(); + virtual void Unk_95(); + virtual void Unk_96(); + virtual void Unk_97(); + virtual void Unk_98(); + virtual void Unk_99(); + virtual void Unk_9A(); + virtual void Unk_9B(); + virtual void Unk_9C(); + virtual void Unk_9D(); + virtual void Unk_9E(); + virtual void Unk_9F(); + virtual ActorEquipData ** GetEquipData(bool bFirstPerson); + virtual void Unk_A1(); + virtual void Unk_A2(); + virtual void Unk_A3(); + virtual void Unk_A4(); + virtual void Unk_A5(); + virtual void Unk_A6(); + virtual void Unk_A7(); + virtual void Unk_A8(); + virtual void Unk_A9(); + virtual void Unk_AA(); + virtual void Unk_AB(); + virtual void Unk_AC(); + virtual void Unk_AD(); + virtual void Unk_AE(); + virtual void Unk_AF(); + virtual void Unk_B0(); + virtual void Unk_B1(); + virtual void Unk_B2(); + virtual void Unk_B3(); + virtual void Unk_B4(); + virtual void Unk_B5(); + virtual void Unk_B6(); + virtual void Unk_B7(); + virtual void Unk_B8(); + virtual void Unk_B9(); + virtual void Unk_BA(); + virtual void Unk_BB(); + virtual void Unk_BC(); + virtual void Unk_BD(); + virtual void Unk_BE(); + virtual void Unk_BF(); + virtual void Unk_C0(); + virtual void Unk_C1(); + virtual void Unk_C2(); + virtual void Unk_C3(); + + enum { kTypeID = kFormType_REFR }; + + // parents + BSHandleRefObject handleRefObject; // 20 + BSTEventSink activeGraphIfInactive; // 30 + BSTEventSink animGraphEventSink; // 38 + BSTEventSink inventoryListSink; // 40 + IAnimationGraphManagerHolder animGraphHolder; // 48 + IKeywordFormBase keywordFormBase; // 50 + ActorValueOwner actorValueOwner; // 58 + void * unk60; // 60 + void * unk68; // 68 + UInt32 unk70; // 70 + UInt32 unk74; // 74 + UInt32 unk78; // 78 + UInt32 unk7C; // 7C + UInt64 unk80; // 80 + UInt64 unk88; // 88 + UInt64 unk90; // 90 + UInt64 unk98; // 98 + UInt64 unkA0; // A0 + UInt64 unkA8; // A8 + UInt64 unkB0; // B0 + TESObjectCELL * parentCell; // B8 + NiPoint3 rot; // C0, C4, C8 - Probably quat? + float unkCC; + NiPoint3 pos; // D0, D4, D8 + float unkDC; + TESForm * baseForm; // E0 + void * unkE8; // E8 + + struct LoadedData + { + UInt64 unk00; + NiNode * rootNode; + UInt64 unk10; + UInt64 unk18; + + enum + { + kFlag_PhysicsInitialized = 1 + }; + + UInt64 flags; + // ... + }; + + LoadedData * unkF0; // F0 - Root node at 0x08 + BGSInventoryList * inventoryList; // F8 + ExtraDataList * extraDataList; // 100 - ExtraData? + UInt32 unk104; // 104 + UInt32 unk108; // 108 + + UInt32 CreateRefHandle(void); + + MEMBER_FN_PREFIX(TESObjectREFR); + DEFINE_MEMBER_FN(GetReferenceName, const char *, 0x0040B760); + DEFINE_MEMBER_FN(GetWorldspace, TESWorldSpace*, 0x0040F290); + DEFINE_MEMBER_FN(GetInventoryWeight, float, 0x00400470); + DEFINE_MEMBER_FN(GetCarryWeight, float, 0x00D871F0); + // 7055D6CB4B64E11E63908512704F8871CEC025D3+11E + DEFINE_MEMBER_FN_1(ForEachAlias, void, 0x003F7960, IAliasFunctor * functor); +}; +STATIC_ASSERT(offsetof(TESObjectREFR, parentCell) == 0xB8); +STATIC_ASSERT(offsetof(TESObjectREFR, baseForm) == 0xE0); +STATIC_ASSERT(sizeof(TESObjectREFR) == 0x110); + +// 490 +class Actor : public TESObjectREFR +{ +public: + virtual void Unk_C4(); + virtual void Unk_C5(); + virtual void Unk_C6(); + virtual void Unk_C7(); + virtual void Unk_C8(); + virtual void Unk_C9(); + virtual void Unk_CA(); + virtual void Unk_CB(); + virtual void Unk_CC(); + virtual void Unk_CD(); + virtual void Unk_CE(); + virtual void Unk_CF(); + virtual void Unk_D0(); + virtual void Unk_D1(); + virtual void Unk_D2(); + virtual void Unk_D3(); + virtual void Unk_D4(); + virtual void Unk_D5(); + virtual void Unk_D6(); + virtual void Unk_D7(); + virtual void Unk_D8(); + virtual void Unk_D9(); + virtual void Unk_DA(); + virtual void Unk_DB(); + virtual void Unk_DC(); + virtual void Unk_DD(); + virtual void Unk_DE(); + virtual void Unk_DF(); + virtual void Unk_E0(); + virtual void Unk_E1(); + virtual void Unk_E2(); + virtual void Unk_E3(); + virtual void Unk_E4(); + virtual void Unk_E5(); + virtual void Unk_E6(); + virtual void Unk_E7(); + virtual void Unk_E8(); + virtual void Unk_E9(); + virtual void Unk_EA(); + virtual void Unk_EB(); + virtual void Unk_EC(); + virtual void Unk_ED(); + virtual void Unk_EE(); + virtual void Unk_EF(); + virtual void Unk_F0(); + virtual void Unk_F1(); + virtual void Unk_F2(); + virtual void Unk_F3(); + virtual void Unk_F4(); + virtual void Unk_F5(); + virtual void Unk_F6(); + virtual void Unk_F7(); + virtual void Unk_F8(); + virtual void Unk_F9(); + virtual void Unk_FA(); + virtual void Unk_FB(); + virtual void Unk_FC(); + virtual void Unk_FD(); + virtual bool IsInCombat(UInt64 unk1 = 0, UInt64 unk2 = 0); + virtual void Unk_FF(); + virtual void Unk_100(); + virtual void Unk_101(); + virtual void Unk_102(); + virtual void Unk_103(); + virtual void Unk_104(); + virtual void Unk_105(); + virtual void Unk_106(); + virtual void Unk_107(); + virtual void Unk_108(); + virtual void Unk_109(); + virtual void Unk_10A(); + virtual void Unk_10B(); + virtual void Unk_10C(); + virtual void Unk_10D(); + virtual void Unk_10E(); + virtual void Unk_10F(); + virtual void Unk_110(); + virtual void Unk_111(); + virtual void Unk_112(); + virtual void Unk_113(); + virtual void Unk_114(); + virtual void Unk_115(); + virtual void Unk_116(); + virtual void Unk_117(); + virtual void Unk_118(); + virtual void Unk_119(); + virtual void Unk_11A(); + virtual void Unk_11B(); + virtual void Unk_11C(); + virtual void Unk_11D(); + virtual void Unk_11E(); + virtual void Unk_11F(); + virtual void Unk_120(); + virtual void Unk_121(); + virtual void Unk_122(); + virtual void Unk_123(); + virtual void Unk_124(); + virtual void Unk_125(); + virtual void Unk_126(); + virtual void Unk_127(); + virtual void Unk_128(); + virtual void Unk_129(); + virtual void Unk_12A(); + virtual void Unk_12B(); + virtual void Unk_12C(); + virtual void Unk_12D(); + virtual void Unk_12E(); + virtual void Unk_12F(); + virtual void Unk_130(); + + enum { kTypeID = kFormType_ACHR }; + + MagicTarget magicTarget; // 110 + ActorState actorState; // 128 + BSTEventSink movementDataChanged; // 138 + BSTEventSink transformDelta; // 140 + BSTEventSink subGraphActivation; // 148 + BSTEventSink characterMoveFinished; // 150 + BSTEventSink nonSupportContact; // 158 + BSTEventSink characterStateChanged; // 160 + + UInt64 unk168[(0x2D0-0x168)/8]; // 168 + UInt32 actorFlags; // 2D0 + + enum ActorFlags + { + kFlag_Teammate = (1 << 26) + }; + + UInt32 unk2D4; + UInt64 unk2D8[(0x300-0x2D8)/8]; // 2D8 + + // Lots of misc data goes here, equipping, perks, etc + struct MiddleProcess + { + void * unk00; // 00 + + struct Data08 + { + UInt64 unk00[0x280 >> 3]; // 000 + + SimpleLock lock; // 280 + + struct EquipData + { + TESForm * item; // 00 + TBO_InstanceData * instanceData; // 08 + BGSEquipSlot * equipSlot; // 10 + UInt64 unk18; // 18 + EquippedWeaponData * equippedData; // 20 + }; + + EquipData * equipData; // 288 + + UInt64 unk290[(0x3A0 - 0x290) >> 3]; + UInt32 unk3A0; // 3A0 + UInt32 furnitureHandle1; // 3A4 + UInt32 furnitureHandle2; // 3A8 + UInt32 unk3AC; // 3AC + UInt64 unk3B0[(0x490 - 0x3B0) >> 3]; + + enum + { + kDirtyHeadParts = 0x04, + kDirtyGender = 0x20 + }; + UInt32 unk490; // 490 + UInt16 unk494; // 494 + UInt16 unk496; // 496 - somekind of dirty flag? + }; + + Data08 * unk08; // 08 + + MEMBER_FN_PREFIX(MiddleProcess); + DEFINE_MEMBER_FN(UpdateEquipment, void, 0x00E60860, Actor * actor, UInt32 flags); + }; + MiddleProcess * middleProcess; // 300 + UInt64 unk308[(0x338-0x308)/8]; + + struct ActorValueData + { + UInt32 avFormID; // 00 + float value; // 04 + }; + tArray actorValueData; // 338 + + struct Data350 // ActorValue related, not sure what the 3 values are + { + UInt32 avFormID; // 00 + float unk04; // 04 + float unk08; // 08 + float unk0C; // 0C + }; + + tArray unk350; // 350 + UInt64 unk368[(0x418-0x368)/8]; + TESRace * race; // 418 + UInt64 unk420; // 420 + ActorEquipData * equipData; // 428 + UInt64 unk430[(0x490-0x430)/8]; // 430 + + bool IsPlayerTeammate() + { + return (actorFlags & kFlag_Teammate) == kFlag_Teammate; + } + bool GetEquippedExtraData(UInt32 slotIndex, ExtraDataList ** extraData); + + MEMBER_FN_PREFIX(Actor); + DEFINE_MEMBER_FN(QueueUpdate, void, 0x00D8A1F0, bool bDoFaceGen, UInt32 unk2, bool DoQueue, UInt32 flags); // 0, 0, 1, 0 + DEFINE_MEMBER_FN(IsHostileToActor, bool, 0x00D91080, Actor * actor); + DEFINE_MEMBER_FN(UpdateEquipment, void, 0x00408270); +}; +STATIC_ASSERT(offsetof(Actor, equipData) == 0x428); +STATIC_ASSERT(offsetof(Actor::MiddleProcess::Data08, equipData) == 0x288); + +// E10 +class PlayerCharacter : public Actor +{ +public: + enum { kTypeID = kFormType_ACHR }; + + virtual void Unk_131(); + virtual void Unk_132(); + virtual void Unk_133(); + virtual void Unk_134(); + + BSTEventSink menuOpenClose; // 490 + BSTEventSink menuModeChange; // 498 + BSTEventSink userEventEnabled; // 4A0 + BSTEventSink hitEvent; // 4A8 + BSTEventSink perkValueEvents; // 4B0 + IMovementPlayerControlsFilter movementControlsFilter; // 4B8 + + UInt8 unk4C0[0x7D8 - 0x4C0]; // 4C0 + + struct Objective + { + void * unk00; // 0 + TESQuest * owner; // 8 + // ... + }; + + struct ObjectiveData + { + Objective *data; // 0 + UInt32 instance; // 4 + UInt32 number; // 8 + }; + + tArray objData; // 7D8 + UInt64 unk458[(0xB70 - 0x7F0) / 8]; // 7F0 + ActorEquipData * playerEquipData; // B70 - First person? + NiNode * firstPersonSkeleton; // B78 + UInt64 unkB68[(0xD00-0xB80)/8]; // B78 + tArray * tints; // D00 + UInt64 unkC90[(0xE10-0xCF8)/8]; // CF8 +}; + +extern RelocPtr g_player; + +STATIC_ASSERT(offsetof(PlayerCharacter, menuOpenClose) == 0x490); +STATIC_ASSERT(offsetof(PlayerCharacter, playerEquipData) == 0xB70); +STATIC_ASSERT(offsetof(PlayerCharacter, tints) == 0xD00); diff --git a/f4se/f4se/GameSettings.cpp b/f4se/f4se/GameSettings.cpp new file mode 100644 index 0000000..76ca543 --- /dev/null +++ b/f4se/f4se/GameSettings.cpp @@ -0,0 +1,137 @@ +#include "f4se/GameSettings.h" +#include "f4se/GameTypes.h" + +// 5B1FD95B3A1729A1781BED06D47E1A47EB6D89F2+91 +RelocPtr g_iniSettings(0x05EDB528); +// 239A2F4B85F2D36A7E4E77D681108A197210AE0B+1C3 +RelocPtr g_iniPrefSettings(0x05B5BE58); +// BB90A8EF53ACD0FD20E1375DC897E55412F59DA7+1B5 +RelocPtr g_regSettings(0x0609BDC0); +// F5934CAD9DF949394DA330793D5F9CCA9711A806+149 +RelocPtr g_gameSettings(0x058E1030); + +UInt32 Setting::GetType(void) const +{ + if(!name || !name[0]) return kType_Unknown; + + switch(name[0]) + { + case 'b': return kType_Bool; + case 'c': return kType_Unknown; + case 'h': return kType_Unknown; + case 'i': return kType_Integer; + case 'u': return kType_Unknown; + case 'f': return kType_Float; + case 'S': return kType_String; // dynamically allocated string + case 's': return kType_String; // statically allocated string + case 'r': return kType_ID6; + case 'a': return kType_ID; + } + + return kType_Unknown; +} + +bool Setting::GetDouble(double * out) const +{ + switch(GetType()) + { + case kType_Integer: *out = data.s32; break; + case kType_Float: *out = data.f32; break; + case kType_String: return false; + case kType_Bool: *out = data.u8 ? 1 : 0; break; + case kType_ID6: *out = data.u32 >> 8; break; + case kType_ID: *out = data.u32; break; + default: return false; + case kType_Unknown: return false; + } + + return true; +} + +bool Setting::SetDouble(double value) +{ + switch(GetType()) + { + case kType_Integer: data.s32 = value; break; + case kType_Float: data.f32 = value; break; + case kType_String: return false; + case kType_Bool: data.u8 = value ? 1 : 0; break; + case kType_ID6: data.u32 = ((UInt32)value) << 8; break; + case kType_ID: data.u32 = value; break; + default: return false; + case kType_Unknown: return false; + } + + return true; +} + +char * FormHeap_Strdup(const char * src) +{ + UInt32 len = strlen(src) + 1; + char * result = (char *)Heap_Allocate(len); + memcpy(result, src, len); + + return result; +} + +bool Setting::SetString(const char * value) +{ + if(GetType() == kType_String) + { + if(name[0] == 'S') + Heap_Free(data.s); + + data.s = FormHeap_Strdup(value); + + // mark string as dynamically allocated + if(name[0] != 'S') + { + name = FormHeap_Strdup(name); + name[0] = 'S'; + } + + return true; + } + else + { + return false; + } +} + +Setting * GetINISetting(const char * name) +{ + Setting * setting = (*g_iniSettings)->Get(name); + if(!setting) + setting = (*g_iniPrefSettings)->Get(name); + + return setting; +} + +Setting * GetGameSetting(const char * name) +{ + Setting * setting = nullptr; + Setting ** setting2 = &setting; + BSAutoFixedString searchName(name); + (*g_gameSettings)->Get(&searchName, &setting2); + return setting; +} + +Setting * SettingCollectionList::Get(const char * name) +{ + Node * node = data; + do + { + Setting * setting = node->data; + if(setting) { + BSAutoFixedString searchName(name); + BSAutoFixedString settingName(setting->name); + if(searchName == settingName) { + return setting; + } + } + + node = node->next; + } while(node); + + return nullptr; +} diff --git a/f4se/f4se/GameSettings.h b/f4se/f4se/GameSettings.h new file mode 100644 index 0000000..4e773ae --- /dev/null +++ b/f4se/f4se/GameSettings.h @@ -0,0 +1,141 @@ +#pragma once + +#include "f4se_common/Utilities.h" +#include "f4se/GameTypes.h" + +// 18 +class Setting +{ +public: + virtual ~Setting(); + + enum + { + kType_Unknown = 0, + kType_Integer, + kType_Float, + kType_String, + kType_Bool, + kType_ID6, // need to find an example of this + kType_ID, + }; + + union Data + { + UInt32 u32; + SInt32 s32; + float f32; + UInt8 u8; // used for bool + char * s; + }; + +// void ** _vtbl; // 00 + Data data; // 08 + char * name; // 10 + + UInt32 GetType(void) const; + bool GetDouble(double * out) const; + bool SetDouble(double value); + bool SetString(const char * value); +}; + +// 118 +class SettingCollection +{ +public: + virtual ~SettingCollection(); + + virtual void Unk_01() = 0; + virtual void Unk_02() = 0; + virtual void Unk_03() = 0; + virtual void Unk_04() = 0; + virtual bool Unk_05(); + virtual bool Unk_06(); + virtual bool Unk_07(); + virtual bool Unk_08(); // return unk110 != 0; + virtual bool Unk_09(); // return unk110 != 0; + +// void ** _vtbl; // 000 + char unk004[260]; // 008 + UInt32 pad10C; // 10C + void * unk110; // 110 +}; + + +// 130 +class SettingCollectionMap : public SettingCollection +{ +public: + virtual ~SettingCollectionMap(); + + class CollectionMap + { + public: + MEMBER_FN_PREFIX(CollectionMap); + DEFINE_MEMBER_FN(GetSetting, void, 0x0001E290, BSFixedString * name, Setting *** setting); + }; + + void * unk118; // 118 + void * unk120; // 120 + CollectionMap * collectionMap; // 128 + + void Get(BSFixedString * name, Setting *** setting) + { + CALL_MEMBER_FN(collectionMap, GetSetting)(name, setting); + } +}; + +// 130 +class GameSettingCollection : public SettingCollectionMap +{ +public: + virtual ~GameSettingCollection(); +}; + +// 128 +class SettingCollectionList : public SettingCollection +{ +public: + virtual ~SettingCollectionList(); + + // this is almost certainly a templated linked list type + struct Node + { + Setting * data; + Node * next; + }; + + void * unk118; // 118 + Node * data; // 120 + + Setting * Get(const char * name); +}; + +// 128 +class INISettingCollection : public SettingCollectionList +{ +public: + virtual ~INISettingCollection(); +}; + +// 128 +class INIPrefSettingCollection : public INISettingCollection +{ +public: + virtual ~INIPrefSettingCollection(); +}; + +// 128 +class RegSettingCollection : public SettingCollectionList +{ +public: + virtual ~RegSettingCollection(); +}; + +extern RelocPtr g_iniSettings; +extern RelocPtr g_iniPrefSettings; +extern RelocPtr g_regSettings; +extern RelocPtr g_gameSettings; + +extern Setting * GetINISetting(const char * name); +extern Setting * GetGameSetting(const char * name); diff --git a/f4se/f4se/GameStreams.cpp b/f4se/f4se/GameStreams.cpp new file mode 100644 index 0000000..bbe4b11 --- /dev/null +++ b/f4se/f4se/GameStreams.cpp @@ -0,0 +1,79 @@ +#include "f4se/GameStreams.h" + +RelocAddr <_CreateFileStream> CreateFileStream(0x00535A60); + +BSResourceNiBinaryStream::BSResourceNiBinaryStream() +{ + // +} + +BSResourceNiBinaryStream::BSResourceNiBinaryStream(const char * path) +{ + CALL_MEMBER_FN(this, Construct)(path, 0, 0, 0); +} + +BSResourceNiBinaryStream::~BSResourceNiBinaryStream() +{ + CALL_MEMBER_FN(this, Destroy)(); +} + +bool BSResourceNiBinaryStream::IsValid(void) +{ + return CALL_MEMBER_FN(this, IsValid)(); +} + +void BSResourceNiBinaryStream::Seek(SInt64 delta) +{ + CALL_MEMBER_FN(this, Seek)(delta); +} + +UInt64 BSResourceNiBinaryStream::GetOffset(void) +{ + return offset; +} + +SInt64 BSResourceNiBinaryStream::Unk_04(void * unk1) +{ + return CALL_MEMBER_FN(this, Unk_04)(unk1); +} + +UInt32 BSResourceNiBinaryStream::Read(void * buf, UInt64 length) +{ + return CALL_MEMBER_FN(this, Read)(buf, length); +} + +UInt32 BSResourceNiBinaryStream::Write(void * buf, UInt64 length) +{ + return CALL_MEMBER_FN(this, Write)(buf, length); +} + +UInt32 BSResourceNiBinaryStream::ReadLine(char * dst, UInt32 dstLen, UInt32 terminator) +{ + return CALL_MEMBER_FN(this, ReadLine)(dst, dstLen, terminator); +} + +UInt32 BSResourceNiBinaryStream::ReadLine_w(wchar_t * dst, UInt32 dstLen, UInt32 terminator) +{ + wchar_t * iter = dst; + + if(dstLen == 0) + return 0; + + for(UInt32 i = 0; i < dstLen - 1; i++) + { + wchar_t data; + + if(Read(&data, sizeof(data)) != sizeof(data)) + break; + + if(data == terminator) + break; + + *iter++ = data; + } + + // null terminate + *iter = 0; + + return iter - dst; +} diff --git a/f4se/f4se/GameStreams.h b/f4se/f4se/GameStreams.h new file mode 100644 index 0000000..09ed73c --- /dev/null +++ b/f4se/f4se/GameStreams.h @@ -0,0 +1,68 @@ +#pragma once + +#include "f4se/GameAPI.h" +#include "f4se_common/Utilities.h" + +// class NiBinaryStream +// class NiFile : public NiBinaryStream +// class NiMemStream : public NiBinaryStream +// class OutputInfoStream : public NiBinaryStream +// class BSResourceNiBinaryStream : public NiBinaryStream +// class BSResourceStreamParser : public BSResourceNiBinaryStream +// class BSStreamParserData + +class NiBinaryStream +{ +public: + virtual ~NiBinaryStream() { }; + + virtual bool IsValid(void) = 0; + virtual void Seek(SInt64 delta) = 0; + virtual UInt64 GetOffset(void) { return 0; } + virtual SInt64 Unk_04(void * unk1) = 0; + virtual UInt32 Read(void * buf, UInt64 length) = 0; + + // void ** _vtbl; // 00 + void * unk08; // 08 +}; + +// 30 +class BSResourceNiBinaryStream : public NiBinaryStream +{ +public: + BSResourceNiBinaryStream(); + BSResourceNiBinaryStream(const char * path); + virtual ~BSResourceNiBinaryStream(); + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) + + // stub implementations + virtual bool IsValid(void); + virtual void Seek(SInt64 delta); + virtual UInt64 GetOffset(void); + virtual SInt64 Unk_04(void * unk1); + virtual UInt32 Read(void * buf, UInt64 length); + virtual UInt32 Write(void * buf, UInt64 length); + + void * unk10; // 10 + void * unk14; // 18 + UInt64 offset; // 20 + UInt32 unk28; // 28 + UInt32 unk2C; // 2C + + MEMBER_FN_PREFIX(BSResourceNiBinaryStream); + DEFINE_MEMBER_FN(Construct, BSResourceNiBinaryStream *, 0x01B93740, const char * filePath, UInt8 unk1, UInt64 unk2, SInt8 unk3); // unk1 = 0, unk2 = 0, unk3 = 0 + DEFINE_MEMBER_FN(ReadLine, UInt32, 0x01B93C40, char * dst, UInt32 dstLen, UInt32 terminator); + DEFINE_MEMBER_FN(Destroy, void, 0x01B93A00); // ??_7BSResourceNiBinaryStream@@6B@ first entry + DEFINE_MEMBER_FN(IsValid, bool, 0x01B93B20, void); + DEFINE_MEMBER_FN(Seek, void, 0x01B93B30, SInt64); + DEFINE_MEMBER_FN(Unk_04, SInt64, 0x01B93BA0, void * unk1); + DEFINE_MEMBER_FN(Read, UInt32, 0x01B93D00, void * buf, UInt64 length); + DEFINE_MEMBER_FN(Write, UInt32, 0x01B93D90, void * buf, UInt64 length); + + UInt32 ReadLine(char * dst, UInt32 dstLen, UInt32 terminator); + UInt32 ReadLine_w(wchar_t * dst, UInt32 dstLen, UInt32 terminator); // length in characters +}; + +typedef BSResourceNiBinaryStream* (* _CreateFileStream)(const char * filePath); +extern RelocAddr <_CreateFileStream> CreateFileStream; diff --git a/f4se/f4se/GameThreads.cpp b/f4se/f4se/GameThreads.cpp new file mode 100644 index 0000000..6394865 --- /dev/null +++ b/f4se/f4se/GameThreads.cpp @@ -0,0 +1 @@ +#include "f4se/GameThreads.h" diff --git a/f4se/f4se/GameThreads.h b/f4se/f4se/GameThreads.h new file mode 100644 index 0000000..d77caaf --- /dev/null +++ b/f4se/f4se/GameThreads.h @@ -0,0 +1,53 @@ +#pragma once + +class ITaskDelegate +{ +public: + virtual ~ITaskDelegate() { } + + virtual void Run() = 0; +}; + +// 50 +class BSThread +{ +public: + virtual ~BSThread(); + + virtual UInt64 Run() + { + return 0; + } + + CRITICAL_SECTION cs; // 08 + /* + LONG LockCount; // 08 + LONG RecursionCount; // 0C + HANDLE OwningThread; // 10 + HANDLE LockSemaphore; // 18 + ULONG_PTR SpinCount; // 20 + */ + UInt64 unk28; // 28 + HANDLE handle; // 30 + UInt64 unk38; // 38 + UInt64 unk40; // 40 + UInt8 unk48; // 48 + UInt8 unk49[7]; // 49 +}; + +class VMInitThread : public BSThread +{ +public: + virtual UInt64 Run(); + + HANDLE eventHandle; // 50 +}; + +// 58 +class InitGameDataThread : public BSThread +{ +public: + virtual UInt64 Run(); + + HANDLE eventHandle; // 50 +}; diff --git a/f4se/f4se/GameTypes.cpp b/f4se/f4se/GameTypes.cpp new file mode 100644 index 0000000..911745a --- /dev/null +++ b/f4se/f4se/GameTypes.cpp @@ -0,0 +1,232 @@ +#include "f4se/GameTypes.h" + +const char * BSString::Get(void) +{ + return m_data ? m_data : ""; +} + +StringCache::Ref::Ref() +{ + CALL_MEMBER_FN(this, ctor)(""); +} + +StringCache::Ref::Ref(const char * buf) +{ + CALL_MEMBER_FN(this, ctor)(buf); +} + +StringCache::Ref::Ref(const wchar_t * buf) +{ + CALL_MEMBER_FN(this, ctor_w)(buf); +} + +void StringCache::Ref::Release() +{ + CALL_MEMBER_FN(this, Release)(); +} + +bool StringCache::Ref::operator==(const char * lhs) const +{ + Ref tmp(lhs); + bool res = data == tmp.data; + CALL_MEMBER_FN(&tmp, Release)(); + return res; +} + +void SimpleLock::Lock(UInt32 pauseAttempts) +{ + SInt32 myThreadID = GetCurrentThreadId(); + + _mm_lfence(); + if (threadID == myThreadID) + { + InterlockedIncrement(&lockCount); + } + else + { + UInt32 attempts = 0; + if (InterlockedCompareExchange(&lockCount, 1, 0)) + { + do + { + ++attempts; + _mm_pause(); + if (attempts >= pauseAttempts) { + UInt32 spinCount = 0; + while (InterlockedCompareExchange(&lockCount, 1, 0)) + Sleep(++spinCount < kFastSpinThreshold ? 1 : 0); + break; + } + } while (InterlockedCompareExchange(&lockCount, 1, 0)); + _mm_lfence(); + } + + threadID = myThreadID; + _mm_sfence(); + } +} + +void SimpleLock::Release(void) +{ + SInt32 myThreadID = GetCurrentThreadId(); + + _mm_lfence(); + if (threadID == myThreadID) + { + if (lockCount == 1) + { + threadID = 0; + _mm_mfence(); + InterlockedCompareExchange(&lockCount, 0, 1); + } + else + { + InterlockedDecrement(&lockCount); + } + } +} + +/* +// This implementation isn't quite right when compared to the came code, something is off +void BSReadWriteLock::LockForRead() +{ + SInt32 myThreadID = GetCurrentThreadId(); + + if (threadID == myThreadID) + { + InterlockedIncrement(&lockValue); + } + else + { + UInt32 lockCount = lockValue & kLockCountMask; + UInt32 spinCount = 0; + UInt32 lockResult = InterlockedCompareExchange(&lockValue, lockCount + 1, lockCount); + while (lockResult != lockCount + 1) + { + if ((lockResult & kLockWrite) != 0) + { + Sleep(++spinCount < kFastSpinThreshold ? 0 : 1); + lockResult = lockValue; + } + + lockCount = lockValue & kLockCountMask; + lockResult = InterlockedCompareExchange(&lockValue, lockCount + 1, lockCount); + } + + threadID = myThreadID; + } +} +*/ + +/* +void BSReadWriteLock::LockForWrite() +{ + SInt32 myThreadID = GetCurrentThreadId(); + + if (threadID == myThreadID) + { + InterlockedIncrement(&lockValue); + } + else + { + UInt32 spinCount = 0; + while (InterlockedCompareExchange(&lockValue, UInt32(1 | kLockWrite), 0) != UInt32(1 | kLockWrite)) + Sleep(++spinCount < kFastSpinThreshold ? 0 : 1); + + threadID = myThreadID; + _mm_mfence(); + } +} +*/ + +void BSReadWriteLock::LockForReadAndWrite() +{ + SInt32 myThreadID = GetCurrentThreadId(); + + if (threadID == myThreadID) + { + InterlockedIncrement(&lockValue); + } + else + { + UInt32 spinCount = 0; + while (InterlockedCompareExchange(&lockValue, 1, 0) != 1) + Sleep(++spinCount >= kFastSpinThreshold ? 1 : 0); + + threadID = myThreadID; + _mm_mfence(); + } +} + +bool BSReadWriteLock::TryLockForWrite() +{ + SInt32 myThreadID = GetCurrentThreadId(); + + bool result = false; + if (threadID == myThreadID) + { + InterlockedIncrement(&lockValue); + result = true; + } + else + { + result = InterlockedCompareExchange(&lockValue, UInt32(1 | kLockWrite), 0) == UInt32(1 | kLockWrite); + if (result) + { + threadID = myThreadID; + _mm_mfence(); + } + } + return result; +} +bool BSReadWriteLock::TryLockForRead() +{ + SInt32 myThreadID = GetCurrentThreadId(); + + bool result = false; + if (threadID == myThreadID) + { + InterlockedIncrement(&lockValue); + result = true; + } + else + { + UInt32 lockCount = lockValue & kLockCountMask; + UInt32 lockResult = InterlockedCompareExchange(&lockValue, lockCount + 1, lockCount); + while ((lockResult & kLockWrite) == 0) + { + if (lockResult == lockCount) + break; + + lockCount = lockResult & kLockCountMask; + lockResult = InterlockedCompareExchange(&lockValue, lockCount + 1, lockCount); + } + + result = ~(lockResult >> 31) & 1; + } + + return result; +} + +void BSReadWriteLock::Unlock() +{ + SInt32 myThreadID = GetCurrentThreadId(); + if (threadID == myThreadID) + { + UInt32 lockCount = lockValue - 1; + if (lockValue == kLockWrite) + { + threadID = 0; + _mm_mfence(); + InterlockedExchange(&lockValue, 0); + } + else + { + InterlockedDecrement(&lockValue); + } + } + else + { + InterlockedDecrement(&lockValue); + } +} \ No newline at end of file diff --git a/f4se/f4se/GameTypes.h b/f4se/f4se/GameTypes.h new file mode 100644 index 0000000..1450af5 --- /dev/null +++ b/f4se/f4se/GameTypes.h @@ -0,0 +1,1280 @@ +#pragma once + +#include "f4se_common/Utilities.h" +#include "f4se/GameAPI.h" + +class TESForm; + +// 04 or 08 depending alignment +struct BSIntrusiveRefCounted +{ +public: + volatile SInt32 m_refCount; // 00 +}; + +// 04 +struct BSNonReentrantSpinLock +{ +public: + volatile UInt32 uiLock; // 00 +}; + +class SimpleLock +{ + enum + { + kFastSpinThreshold = 10000 + }; + + volatile SInt32 threadID; // 00 + volatile SInt32 lockCount; // 04 + +public: + SimpleLock() : threadID(0), lockCount(0) {} + + void Lock(UInt32 pauseAttempts = 0); + void Release(void); +}; +STATIC_ASSERT(sizeof(SimpleLock) == 0x8); + +// 08 +class BSReadWriteLock +{ + enum + { + kFastSpinThreshold = 10000, + kLockWrite = 0x80000000, + kLockCountMask = 0xFFFFFFF + }; + + volatile SInt32 threadID; // 00 + volatile SInt32 lockValue; // 04 + +public: + BSReadWriteLock() : threadID(0), lockValue(0) {} + + //void LockForRead(); + //void LockForWrite(); + + DEFINE_MEMBER_FN_0(LockForRead, void, 0x01B10930); + DEFINE_MEMBER_FN_0(LockForWrite, void, 0x01B109B0); + + void LockForReadAndWrite(); + + bool TryLockForWrite(); + bool TryLockForRead(); + + void Unlock(); +}; +STATIC_ASSERT(sizeof(BSReadWriteLock) == 0x8); + +class BSReadLocker +{ +public: + BSReadLocker(BSReadWriteLock * lock) { m_lock = lock; m_lock->LockForRead(); } + ~BSReadLocker() { m_lock->Unlock(); } + +protected: + BSReadWriteLock * m_lock; +}; + +class BSWriteLocker +{ +public: + BSWriteLocker(BSReadWriteLock * lock) { m_lock = lock; m_lock->LockForWrite(); } + ~BSWriteLocker() { m_lock->Unlock(); } + +protected: + BSReadWriteLock * m_lock; +}; + +class BSReadAndWriteLocker +{ +public: + BSReadAndWriteLocker(BSReadWriteLock * lock) { m_lock = lock; m_lock->LockForReadAndWrite(); } + ~BSReadAndWriteLocker() { m_lock->Unlock(); } + +protected: + BSReadWriteLock * m_lock; +}; + +class SimpleLocker +{ +public: + SimpleLocker(SimpleLock * dataHolder) { m_lock = dataHolder; m_lock->Lock(); } + ~SimpleLocker() { m_lock->Release(); } + +protected: + SimpleLock * m_lock; +}; + +// 80808 +class StringCache +{ +public: + // 18+ + struct Entry + { + enum + { + kState_RefcountMask = 0x3FFF, + kState_External = 0x4000, + kState_Wide = 0x8000 + }; + + Entry * next; // 00 + UInt32 state; // 08 - refcount, hash, flags + UInt32 length; // 0C + Entry * externData; // 10 + char data[0]; // 18 + + bool IsWide() + { + Entry * iter = this; + + while(iter->state & kState_External) + iter = iter->externData; + + if((iter->state & kState_Wide) == kState_Wide) + return true; + + return false; + } + + template + T * Get() + { + Entry * iter = this; + + while(iter->state & kState_External) + iter = iter->externData; + + return (T*)iter->data; + } + }; + + struct Ref + { + Entry * data; + + MEMBER_FN_PREFIX(Ref); + // D3703E13297FD78BE317E0223C90DAB9021465DD+6F + DEFINE_MEMBER_FN(ctor, Ref *, 0x01B41D40, const char * buf); + // 34CA732E6B3C7BCD20DEFC8B3711427E5285FF82+AA + DEFINE_MEMBER_FN(ctor_w, Ref *, 0x01B42B60, const wchar_t * buf); + // 489C5F60950D108691FCB6CB0026101275BE474A+79 + DEFINE_MEMBER_FN(Set, Ref *, 0x01B41E70, const char * buf); + DEFINE_MEMBER_FN(Set_w, Ref *, 0x01B443C0, const wchar_t * buf); + + DEFINE_MEMBER_FN(Release, void, 0x01B42FD0); + + Ref(); + Ref(const char * buf); + Ref(const wchar_t * buf); + + void Release(); + + bool operator==(const char * lhs) const; + bool operator==(const Ref& lhs) const { return data == lhs.data; } + bool operator<(const Ref& lhs) const { return data < lhs.data; } + + const char * c_str() const { return operator const char *(); } + operator const char *() const { return data ? data->Get() : nullptr; } + + const wchar_t * wc_str() { return operator const wchar_t *(); } + operator const wchar_t *() { return data ? data->Get() : nullptr; } + }; + + // 10 + struct Lock + { + UInt32 unk00; // 00 - set to 80000000 when locked + UInt32 pad04; // 04 + UInt64 pad08; // 08 + }; + + Entry * lut[0x10000]; // 00000 + Lock lock[0x80]; // 80000 + UInt8 isInit; // 80800 +}; + +typedef StringCache::Ref BSFixedString; +typedef StringCache::Ref BSFixedStringW; + +class BSAutoFixedString : public BSFixedString +{ +public: + BSAutoFixedString() : BSFixedString() { } + BSAutoFixedString(const char * buf) : BSFixedString(buf) { } + + ~BSAutoFixedString() + { + Release(); + } +}; + +// 10 +class BSString +{ +public: + BSString() :m_data(NULL), m_dataLen(0), m_bufLen(0) { } + ~BSString() + { + if(m_data) { + Heap_Free(m_data); + m_data = nullptr; + m_dataLen = 0; + m_bufLen = 0; + } + }; + + const char * Get(void); + +private: + char * m_data; // 00 + UInt16 m_dataLen; // 08 + UInt16 m_bufLen; // 0A + UInt32 pad0C; // 0C +}; + + +// Container types + +// 18 +template +class tArray +{ +public: + T* entries; // 00 + UInt32 capacity; // 08 + UInt32 pad0C; // 0C + UInt32 count; // 10 + UInt32 pad14; // 14 + + tArray() : entries(NULL), capacity(0), count(0), pad0C(0), pad14(0) { } + + T& operator[](UInt64 index) + { + return entries[index]; + } + + void Clear() + { + Heap_Free(entries); + entries = NULL; + capacity = 0; + count = 0; + } + + bool Allocate(UInt32 numEntries) + { + entries = (T *)Heap_Allocate(sizeof(T) * numEntries); + if(!entries) return false; + + for(UInt32 i = 0; i < numEntries; i++) + new (&entries[i]) T; + + capacity = numEntries; + count = numEntries; + + return true; + } + + bool Resize(UInt32 numEntries) + { + if(numEntries == capacity) + return false; + + if(!entries) { + Allocate(numEntries); + return true; + } + if(numEntries < capacity) { + // Delete the truncated entries + for(UInt32 i = numEntries; i < capacity; i++) + delete &entries[i]; + } + + T * newBlock = (T *)Heap_Allocate(sizeof(T) * numEntries); // Create a new block + memmove_s(newBlock, sizeof(T) * numEntries, entries, sizeof(T) * numEntries); // Move the old memory to the new block + if(numEntries > capacity) { // Fill in new remaining entries + for(UInt32 i = capacity; i < numEntries; i++) + new (&entries[i]) T; + } + Heap_Free(entries); // Free the old block + entries = newBlock; // Assign the new block + capacity = numEntries; // Capacity is now the number of total entries in the block + count = min(capacity, count); // Count stays the same, or is truncated to capacity + return true; + } + + bool Push(const T & entry) + { + if(!entries || count + 1 > capacity) { + if(!Grow(nGrow)) + return false; + } + + entries[count] = entry; + count++; + return true; + }; + + bool Insert(UInt32 index, const T & entry) + { + if(!entries) + return false; + + UInt32 lastSize = count; + if(count + 1 > capacity) // Not enough space, grow + { + if(!Grow(nGrow)) + return false; + } + + if(index != lastSize) // Not inserting onto the end, need to move everything down + { + UInt32 remaining = count - index; + memmove_s(&entries[index + 1], sizeof(T) * remaining, &entries[index], sizeof(T) * remaining); // Move the rest up + } + + entries[index] = entry; + count++; + return true; + }; + + bool Remove(UInt32 index) + { + if(!entries || index >= count) + return false; + + // This might not be right for pointer types... + (&entries[index])->~T(); + + if(index + 1 < count) { + UInt32 remaining = count - index; + memmove_s(&entries[index], sizeof(T) * remaining, &entries[index + 1], sizeof(T) * remaining); // Move the rest up + } + count--; + + if(capacity > count + nShrink) + Shrink(); + + return true; + } + + bool Shrink() + { + if(!entries || count == capacity) return false; + + try { + UInt32 newSize = count; + T * oldArray = entries; + T * newArray = (T *)Heap_Allocate(sizeof(T) * newSize); // Allocate new block + memmove_s(newArray, sizeof(T) * newSize, entries, sizeof(T) * newSize); // Move the old block + entries = newArray; + capacity = count; + Heap_Free(oldArray); // Free the old block + return true; + } + catch(...) { + return false; + } + + return false; + } + + bool Grow(UInt32 numEntries) + { + if(!entries) { + entries = (T *)Heap_Allocate(sizeof(T) * numEntries); + count = 0; + capacity = numEntries; + return true; + } + + try { + UInt32 oldSize = capacity; + UInt32 newSize = oldSize + numEntries; + T * oldArray = entries; + T * newArray = (T *)Heap_Allocate(sizeof(T) * newSize); // Allocate new block + if(oldArray) + memmove_s(newArray, sizeof(T) * newSize, entries, sizeof(T) * capacity); // Move the old block + entries = newArray; + capacity = newSize; + + if(oldArray) + Heap_Free(oldArray); // Free the old block + + for(UInt32 i = oldSize; i < newSize; i++) // Allocate the rest of the free blocks + new (&entries[i]) T; + + return true; + } + catch(...) { + return false; + } + + return false; + } + + bool GetNthItem(UInt64 index, T& pT) const + { + if (index < count) { + pT = entries[index]; + return true; + } + return false; + } + + SInt64 GetItemIndex(T & pFind) const + { + for (UInt64 n = 0; n < count; n++) { + T& pT = entries[n]; + if (pT == pFind) + return n; + } + return -1; + } + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) +}; + +template +class tMutexArray : public tArray +{ +public: + SimpleLock lock; // 18 +}; + +typedef tArray UnkArray; +typedef tArray UnkFormArray; + +// 10 +template +class tList +{ + enum { + eListCount = -3, + eListEnd = -2, + eListInvalid = -1, + }; + struct _Node + { + + T * item; // 00 + _Node* next; // 04 + + T * Item() const { return item; } + _Node* Next() const { return next; } + + T * Remove(_Node * pred) + { + T * pRemoved = item; + _Node * pNext = next; + + // become the next entry and return my item + if (pNext) + { + item = pNext->item; + next = pNext->next; + FormHeap_Free(pNext); + } + // tail? + else + { + item = NULL; + next = NULL; + + // has precedessor, so tail != head + if (pred) + { + pred->next = NULL; + FormHeap_Free(this); + } + } + return pRemoved; + } + }; + + _Node m_listHead; // 00 + +private: + + _Node * Head() const { return const_cast<_Node*>(&m_listHead); } + + _Node * Tail() const + { + _Node * node = const_cast<_Node*>(&m_listHead); + + if (node) + while (node->next) node = node->next; + + return node; + } + + template + UInt32 FreeNodes(Op &compareOp) + { + UInt32 numFreed = 0; + + _Node * pPred = NULL; + _Node * pCur = Head(); + + while (pCur) + { + if (pCur->next) + { + if (compareOp.Accept(pCur->item)) + { + pCur->Remove(pPred); + numFreed++; + } + else + { + pPred = pCur; + pCur = pCur->next; + } + } + // List Tail? + else + { + if (compareOp.Accept(pCur->item)) + { + pCur->Remove(pPred); + numFreed++; + } + break; + } + } + + return numFreed; + } + + struct NodePos + { + NodePos(): node(NULL), index(eListInvalid) {} + + _Node* node; + SInt32 index; + }; + + + NodePos GetNthNode(SInt32 index) const + { + NodePos pos; + SInt32 n = 0; + _Node* pCur = Head(); + + while (pCur && pCur->Item()) + { + if (n == index) break; + if (eListEnd == index && !pCur->Next()) break; + pCur = pCur->Next(); + ++n; + } + + pos.node = pCur; + pos.index = n; + + return pos; + } + +public: + + // Allocate list as a single empty node + static tList * Create(void) + { + tList * p = (tList *)FormHeap_Allocate(sizeof(tList)); + ASSERT(p); + + p->m_listHead.item = NULL; + p->m_listHead.next = NULL; + + return p; + } + + void Delete(void) + { + RemoveAll(); + FormHeap_Free(&m_listHead); + } + + class Iterator + { + _Node * m_cur; + public: + Iterator() : m_cur(NULL) {} + Iterator(_Node* node) : m_cur(node) { } + Iterator operator++() { if (!End()) m_cur = m_cur->Next(); return *this;} + bool End() { return m_cur == NULL; } + const T * operator->() { return (m_cur) ? m_cur->Item() : NULL; } + const T * operator*() { return (m_cur) ? m_cur->Item() : NULL; } + const Iterator& operator=(const Iterator& rhs) { + m_cur = rhs.m_cur; + return *this; + } + T * Get() { return (m_cur) ? m_cur->Item() : NULL; } + }; + + const Iterator Begin() const { return Iterator(Head()); } + + void Insert(T * item) + { + // add new node if we aren't empty + if (m_listHead.item) + { + // copy head in to new node + _Node * node = (_Node *)FormHeap_Allocate(sizeof(_Node)); + ASSERT(node); + + node->item = m_listHead.item; + node->next = m_listHead.next; + + m_listHead.next = node; + } + + m_listHead.item = item; + } + + void Push(T * item) + { + _Node * tail = Tail(); + + // add new node if we aren't empty + if (tail->item) + { + _Node * node = (_Node *)FormHeap_Allocate(sizeof(_Node)); + ASSERT(node); + + tail->next = node; + + node->item = item; + node->next = NULL; + } + else + { + tail->item = item; + } + } + + T * AddFront(void) + { + T * item = (T *)FormHeap_Allocate(sizeof(T)); + if(!item) + return NULL; + + new (item) T; + + Insert(item); + return item; + } + + T * AddBack(void) + { + T * item = (T *)FormHeap_Allocate(sizeof(T)); + if(!item) + return NULL; + + new (item) T; + + Push(item); + return item; + } + + void Append(Iterator source) + { + while (!source.End()) + { + Push(source.Get()); + ++source; + } + } + + UInt32 Count() const + { + NodePos pos = GetNthNode(eListCount); + return (pos.index > 0) ? pos.index : 0; + }; + + T * GetNthItem(SInt32 n) const + { + NodePos pos = GetNthNode(n); + return (pos.index == n && pos.node) ? pos.node->Item() : NULL; + } + + T * GetLastItem() const + { + NodePos pos = GetNthNode(eListEnd); + return pos.node->Item(); + } + + SInt32 AddAt(T * item, SInt32 index) + { + if (!m_listHead.item) { + m_listHead.item = item; + return 0; + } + + NodePos pos = GetNthNode(index); + _Node* pTargetNode = pos.node; + _Node* newNode = (_Node*)FormHeap_Allocate(sizeof(newNode)); + if (newNode && pTargetNode) { + if (index == eListEnd) { + pTargetNode->next = newNode; + newNode->item = item; + newNode->next = NULL; + } else { + newNode->item = pTargetNode->item; + newNode->next = pTargetNode->next; + pTargetNode->item = item; + pTargetNode->next = newNode; + } + return pos.index; + } + return eListInvalid; + } + + template + void Visit(Op& op, _Node* prev = NULL) const { + const _Node* pCur = (prev) ? prev->next : Head(); + bool bContinue = true; + while (pCur && bContinue) { + bContinue = op.Accept(pCur->Item()); + if (bContinue) { + pCur = pCur->next; + } + } + } + + template + T * Find(Op& op) const + { + const _Node* pCur = Head(); + + bool bFound = false; + while (pCur && !bFound) + { + if (!pCur->Item()) + pCur = pCur->Next(); + else + { + bFound = op.Accept(pCur->Item()); + if (!bFound) + pCur = pCur->Next(); + } + } + return (bFound && pCur) ? pCur->Item() : NULL; + } + + template + Iterator Find(Op& op, Iterator prev) const + { + Iterator curIt = (prev.End()) ? Begin() : ++prev; + bool bFound = false; + + while(!curIt.End() && !bFound) { + const T * pCur = *curIt; + if (pCur) { + bFound = op.Accept(pCur); + } + if (!bFound) { + ++curIt; + } + } + return curIt; + } + + const _Node* FindString(char* str, Iterator prev) const + { + return Find(StringFinder_CI(str), prev); + } + + template + UInt32 CountIf(Op& op) const + { + UInt32 count = 0; + const _Node* pCur = Head(); + while (pCur) + { + if (pCur->Item() && op.Accept(pCur->Item())) + count++; + pCur = pCur->Next(); + } + return count; + } + + class AcceptAll { + public: + bool Accept(T * item) { + return true; + } + }; + + void RemoveAll() + { + FreeNodes(AcceptAll()); + } + + T * RemoveNth(SInt32 n) + { + T* pRemoved = NULL; + if (n == 0) { + pRemoved = m_listHead.RemoveMe(); + } else if (n > 0) { + NodePos nodePos = GetNthNode(n); + if (nodePos.node && nodePos.index == n) { + pRemoved = nodePos.node->RemoveMe(); + } + } + return pRemoved; + }; + + T * ReplaceNth(SInt32 n, T* item) + { + T* pReplaced = NULL; + NodePos nodePos = GetNthNode(n); + if (nodePos.node && nodePos.index == n) { + pReplaced = nodePos.node->item; + nodePos.node->item = item; + } + return pReplaced; + } + + template + UInt32 RemoveIf(Op& op) + { + return FreeNodes(op); + } + + template + SInt32 GetIndexOf(Op& op) + { + SInt32 idx = 0; + const _Node* pCur = Head(); + while (pCur && pCur->Item() && !op.Accept(pCur->Item())) + { + idx++; + pCur = pCur->Next(); + } + + if (pCur && pCur->Item()) + return idx; + else + return -1; + } + + class AcceptEqual { + public: + T * item; + + AcceptEqual(T * a_item) : item(a_item) {} + + bool Accept(T * a_item) { + return *item == *a_item; + } + }; + + bool Contains(T * item) const + { + return Find(AcceptEqual(item)) != NULL; + } + + void Dump(void) + { + _MESSAGE("tList:"); + _MESSAGE("> count: %d", Count()); + + const _Node* pCur = Head(); + UInt32 i = 0; + while (pCur) + { + _MESSAGE("* %d :", i); + //_MESSAGE("\t\titem: %08X", pCur->item); + if (pCur->item) + _MESSAGE("\t\t*item: %d", *pCur->item); + _MESSAGE("\t\tnext: %08X", pCur->next); + + i++; + pCur = pCur->next; + } + } +}; + +STATIC_ASSERT(sizeof(tList) == 0x10); + +// 30 +template +class tHashSet +{ + class _Entry + { + public: + Item item; + _Entry * next; + + _Entry() : next(NULL) {} + + bool IsFree() const { return next == NULL; } + void SetFree() { next = NULL; } + + void Dump(void) + { + item.Dump(); + _MESSAGE("\t\tnext: %016I64X", next); + } + }; + + // When creating a new tHashSet, init sentinel pointer with address of this entry + static _Entry sentinel; + + void * unk00; // 000 + UInt32 unk_000; // 008 + UInt32 m_size; // 00C + UInt32 m_freeCount; // 010 + UInt32 m_freeOffset; // 014 + _Entry * m_eolPtr; // 018 + UInt64 unk_018; // 020 + _Entry * m_entries; // 028 + + + _Entry * GetEntry(UInt32 hash) const + { + return (_Entry*) (((uintptr_t) m_entries) + sizeof(_Entry) * (hash & (m_size - 1))); + } + + _Entry * GetEntryAt(UInt32 index) const + { + return (_Entry*) (((uintptr_t) m_entries) + sizeof(_Entry) * index); + } + + _Entry * NextFreeEntry(void) + { + _Entry * result = NULL; + + if (m_freeCount == 0) + return NULL; + + do + { + m_freeOffset = (m_size - 1) & (m_freeOffset - 1); + _Entry * entry = GetEntryAt(m_freeOffset); + + if (entry->IsFree()) + result = entry; + } + while (!result); + + m_freeCount--; + + return result; + } + + enum InsertResult + { + kInsert_Duplicate = -1, + kInsert_OutOfSpace = 0, + kInsert_Success = 1 + }; + + InsertResult Insert(Item * item) + { + if (! m_entries) + return kInsert_OutOfSpace; + + Key k = (Key)*item; + _Entry * targetEntry = GetEntry(Item::GetHash(&k)); + + // Case 1: Target entry is free + if (targetEntry->IsFree()) + { + targetEntry->item = *item; + targetEntry->next = m_eolPtr; + --m_freeCount; + + return kInsert_Success; + } + + // -- Target entry is already in use + + // Case 2: Item already included + _Entry * p = targetEntry; + do + { + if (p->item == *item) + return kInsert_Duplicate; + p = p->next; + } + while (p != m_eolPtr); + + // -- Either hash collision or bucket overlap + + _Entry * freeEntry = NextFreeEntry(); + // No more space? + if (!freeEntry) + return kInsert_OutOfSpace; + + // Original position of the entry that currently uses the target position + k = (Key)targetEntry->item; + p = GetEntry(Item::GetHash(&k)); + + // Case 3a: Hash collision - insert new entry between target entry and successor + if (targetEntry == p) + { + freeEntry->item = *item; + freeEntry->next = targetEntry->next; + targetEntry->next = freeEntry; + + return kInsert_Success; + } + // Case 3b: Bucket overlap + else + { + while (p->next != targetEntry) + p = p->next; + + freeEntry->item = targetEntry->item; + freeEntry->next = targetEntry->next; + + p->next = freeEntry; + targetEntry->item = *item; + targetEntry->next = m_eolPtr; + + return kInsert_Success; + } + } + + bool CopyEntry(_Entry * sourceEntry) + { + if (! m_entries) + return false; + + Key k = (Key)sourceEntry->item; + _Entry * targetEntry = GetEntry(Item::GetHash(&k)); + + // Case 1: Target location is unused + if (!targetEntry->next) + { + targetEntry->item = sourceEntry->item; + targetEntry->next = m_eolPtr; + --m_freeCount; + + return true; + } + + // Target location is in use. Either hash collision or bucket overlap. + + _Entry * freeEntry = NextFreeEntry(); + k = (Key)targetEntry->item; + _Entry * p = GetEntry(Item::GetHash(&k)); + + // Case 2a: Hash collision - insert new entry between target entry and successor + if (targetEntry == p) + { + freeEntry->item = sourceEntry->item; + freeEntry->next = targetEntry->next; + targetEntry->next = freeEntry; + + return true; + } + + // Case 2b: Bucket overlap - forward until hash collision is found, then insert + while (p->next != targetEntry) + p = p->next; + + // Source entry takes position of target entry - not completely understood yet + freeEntry->item = targetEntry->item; + freeEntry->next = targetEntry->next; + p->next = freeEntry; + targetEntry->item = sourceEntry->item; + targetEntry->next = m_eolPtr; + + return true; + } + + void Grow(void) + { + UInt32 oldSize = m_size; + UInt32 newSize = oldSize ? 2*oldSize : 8; + + _Entry * oldEntries = m_entries; + _Entry * newEntries = (_Entry*)Heap_Allocate(newSize * sizeof(_Entry)); + ASSERT(newEntries); + + m_entries = newEntries; + m_size = m_freeCount = m_freeOffset = newSize; + + // Initialize new table data (clear next pointers) + _Entry * p = newEntries; + for (UInt32 i = 0; i < newSize; i++, p++) + p->SetFree(); + + // Copy old entries, free old table data + if (oldEntries) + { + _Entry * p = oldEntries; + for (UInt32 i = 0; i < oldSize; i++, p++) + if (p->next) + CopyEntry(p); + Heap_Free(oldEntries); + } + } + +public: + + tHashSet() : m_size(0), m_freeCount(0), m_freeOffset(0), m_entries(NULL), m_eolPtr(&sentinel) { } + ~tHashSet() { if(m_entries) Heap_Free(m_entries); } + + UInt32 Size() const { return m_size; } + UInt32 FreeCount() const { return m_freeCount; } + UInt32 FillCount() const { return m_size - m_freeCount; } + + Item * Find(Key * key) const + { + if (!m_entries) + return NULL; + + _Entry * entry = GetEntry(Item::GetHash(key)); + if (! entry->next) + return NULL; + + while (!(entry->item == *key)) + { + entry = entry->next; + if (entry == m_eolPtr) + return NULL; + } + + return &entry->item; + } + + bool Add(Item * item) + { + InsertResult result; + + for (result = Insert(item); result == kInsert_OutOfSpace; result = Insert(item)) + Grow(); + + return result == kInsert_Success; + } + + bool Remove(Key * key) + { + if ( !m_entries) + return false; + + _Entry * entry = GetEntry(Item::GetHash(key)); + if (! entry->next) + return NULL; + + _Entry * prevEntry = NULL; + while (! (entry->item == *key)) + { + prevEntry = entry; + entry = entry->next; + if (entry == m_eolPtr) + return false; + } + + // Remove tail? + _Entry * nextEntry = entry->next; + if (nextEntry == m_eolPtr) + { + if (prevEntry) + prevEntry->next = m_eolPtr; + entry->next = NULL; + } + else + { + entry->item = nextEntry->item; + entry->next = nextEntry->next; + nextEntry->next = NULL; + } + + ++m_freeCount; + return true; + } + + void Clear(void) + { + if (m_entries) + { + _Entry * p = m_entries; + for (UInt32 i = 0; i < m_size; i++, p++) + p->next = NULL; + } + else + { + m_size = 0; + } + m_freeCount = m_freeOffset = m_size; + } + + template + void ForEach(T& functor) + { + if (!m_entries) + return; + + if(m_size == m_freeCount) // The whole thing is free + return; + + _Entry * cur = m_entries; + _Entry * end = GetEntryAt(m_size); // one index beyond the entries data to check if we reached that point + + if (cur == end) + return; + + if (cur->IsFree()) + { + // Forward to first non-free entry + do cur++; + while (cur != end && cur->IsFree()); + } + + do + { + if (! functor(&cur->item)) + return; + + // Forward to next non-free entry + do cur++; + while (cur != end && cur->IsFree()); + + } while (cur != end); + } + + void Dump(void) + { + _MESSAGE("tHashSet:"); + _MESSAGE("> size: %d", Size()); + _MESSAGE("> free: %d", FreeCount()); + _MESSAGE("> filled: %d", FillCount()); + if (m_entries) + { + _Entry * p = m_entries; + for (UInt32 i = 0; i < m_size; i++, p++) { + _MESSAGE("* %d %s:", i, p->IsFree()?"(free)" : ""); + if (!p->IsFree()) + p->Dump(); + } + } + } + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) +}; + +template +typename tHashSet::_Entry tHashSet::sentinel = tHashSet::_Entry(); + +template +class SafeDataHolder +{ +protected: + SimpleLock m_lock; +public: + T m_data; + + void Lock(void) { m_lock.Lock(); } + void Release(void) { m_lock.Release(); } +}; diff --git a/f4se/f4se/GameUtilities.cpp b/f4se/f4se/GameUtilities.cpp new file mode 100644 index 0000000..7be6094 --- /dev/null +++ b/f4se/f4se/GameUtilities.cpp @@ -0,0 +1,4 @@ +#include "f4se/GameUtilities.h" + +RelocAddr <_CalculateCRC32_64> CalculateCRC32_64(0x01B10830); +RelocAddr <_CalculateCRC32_32> CalculateCRC32_32(0x01B107A0); diff --git a/f4se/f4se/GameUtilities.h b/f4se/f4se/GameUtilities.h new file mode 100644 index 0000000..37eb239 --- /dev/null +++ b/f4se/f4se/GameUtilities.h @@ -0,0 +1,9 @@ +#pragma once + +#include "f4se_common/Relocation.h" + +typedef void (* _CalculateCRC32_64)(UInt32 * out, UInt64 data, UInt32 previous); +extern RelocAddr <_CalculateCRC32_64> CalculateCRC32_64; + +typedef void (* _CalculateCRC32_32)(UInt32 * out, UInt32 data, UInt32 previous); +extern RelocAddr <_CalculateCRC32_32> CalculateCRC32_32; diff --git a/f4se/f4se/GameWorkshop.cpp b/f4se/f4se/GameWorkshop.cpp new file mode 100644 index 0000000..cf6d24b --- /dev/null +++ b/f4se/f4se/GameWorkshop.cpp @@ -0,0 +1,11 @@ +#include "f4se/GameWorkshop.h" + +RelocAddr <_LinkPower> LinkPower_Internal(0x001F6E20); // Power related natives +RelocAddr <_LinkPower2> LinkPower2_Internal(0x00201B10); // Usually paired with LinkPower +RelocAddr <_GetObjectAtConnectPoint> GetObjectAtConnectPoint(0x001FF360); // Acquires objects that are touching attach points +RelocAddr <_LinkPower3> LinkPower3_Internal(0x001F6890); // Wire related calls +RelocAddr <_LinkPower4> LinkPower4_Internal(0x00204610); +RelocAddr <_SetWireEndpoints> SetWireEndpoints_Internal(0x00200E50); +RelocAddr <_FinalizeWireLink> FinalizeWireLink(0x00200B50); +// B040A6E939E55D7B97BC6BE389FC17F455E63F06+83 +RelocAddr <_ScrapReference> ScrapReference(0x002084C0); diff --git a/f4se/f4se/GameWorkshop.h b/f4se/f4se/GameWorkshop.h new file mode 100644 index 0000000..db1edcc --- /dev/null +++ b/f4se/f4se/GameWorkshop.h @@ -0,0 +1,40 @@ +#pragma once + +#include "f4se/GameTypes.h" + +class TESObjectREFR; +class LocationData; +class BSExtraData; +class NiPoint3; +class bhkWorld; + +typedef void(*_LinkPower)(BSExtraData* workshopExtraData, TESObjectREFR* akRef1, TESObjectREFR* akRef2, TESObjectREFR* akWireRef); // Wire optional +extern RelocAddr <_LinkPower> LinkPower_Internal; + +typedef void(*_LinkPower2)(TESObjectREFR* akRef, BSExtraData* workshopExtraData); +extern RelocAddr <_LinkPower2> LinkPower2_Internal; + +// Wire related calls +typedef void(*_LinkPower3)(BSExtraData* workshopExtraData, TESObjectREFR* akWireRef); +extern RelocAddr <_LinkPower3> LinkPower3_Internal; + +typedef void(*_LinkPower4)(TESObjectREFR* akWireRef); +extern RelocAddr <_LinkPower4> LinkPower4_Internal; + +typedef void(*_SetWireEndpoints)(TESObjectREFR* akRef1, SInt32 unk2, TESObjectREFR* akRef2, SInt32 unk4, TESObjectREFR* akWireRef); // unk2 and unk4 always 0 - Adds the ExtraData +extern RelocAddr <_SetWireEndpoints> SetWireEndpoints_Internal; + +typedef void(*_FinalizeWireLink)(LocationData* locationData, TESObjectREFR* akWireRef, TESObjectREFR* akRef1, int unk4, TESObjectREFR* akRef2, int unk6); // unk4 and unk6 always 0 +extern RelocAddr <_FinalizeWireLink> FinalizeWireLink; + +typedef TESObjectREFR * (*_GetObjectAtConnectPoint)(TESObjectREFR * source, NiPoint3 * connectPos, bhkWorld * world, float unk1); +extern RelocAddr <_GetObjectAtConnectPoint> GetObjectAtConnectPoint; + +struct MaterialsReturned +{ + TESForm * form; + UInt32 amount; +}; + +typedef void(*_ScrapReference)(LocationData* locationData, TESObjectREFR** akRef, tArray * materials); +extern RelocAddr <_ScrapReference> ScrapReference; diff --git a/f4se/f4se/Hooks_Camera.cpp b/f4se/f4se/Hooks_Camera.cpp new file mode 100644 index 0000000..bb6e4e4 --- /dev/null +++ b/f4se/f4se/Hooks_Camera.cpp @@ -0,0 +1,64 @@ +#include "Hooks_Camera.h" + +#include "xbyak/xbyak.h" +#include "f4se_common/BranchTrampoline.h" +#include "f4se_common/Relocation.h" + +#include "f4se/PapyrusEvents.h" +#include "f4se/GameCamera.h" + +void Hooks_Camera_Init() +{ + +} + +typedef void (* _SetCameraState)(TESCamera * camera, TESCameraState * newState); +RelocAddr <_SetCameraState> SetCameraState(0x0082E930); +_SetCameraState SetCameraState_Original = nullptr; + +void SetCameraState_Hook(TESCamera * camera, TESCameraState * newCameraState) +{ + if(camera == (*g_playerCamera)) + { + SInt32 oldState = (*g_playerCamera)->GetCameraStateId(camera->cameraState); + SInt32 newState = (*g_playerCamera)->GetCameraStateId(newCameraState); + + g_cameraEventRegs.ForEach( + [&oldState, &newState](const EventRegistration & reg) + { + SendPapyrusEvent2(reg.handle, reg.scriptName, "OnPlayerCameraState", oldState, newState); + } + ); + } + + SetCameraState_Original(camera, newCameraState); +} + +void Hooks_Camera_Commit() +{ + // hook global tinting of objects + { + struct SetCameraState_Code : Xbyak::CodeGenerator { + SetCameraState_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr[rsp+0x08], rbx); + push(rdi); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(SetCameraState.GetUIntPtr() + 6); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + SetCameraState_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + SetCameraState_Original = (_SetCameraState)codeBuf; + + g_branchTrampoline.Write6Branch(SetCameraState.GetUIntPtr(), (uintptr_t)SetCameraState_Hook); + } +} diff --git a/f4se/f4se/Hooks_Camera.h b/f4se/f4se/Hooks_Camera.h new file mode 100644 index 0000000..e013a9e --- /dev/null +++ b/f4se/f4se/Hooks_Camera.h @@ -0,0 +1,4 @@ +#pragma once + +void Hooks_Camera_Init(); +void Hooks_Camera_Commit(); diff --git a/f4se/f4se/Hooks_Debug.cpp b/f4se/f4se/Hooks_Debug.cpp new file mode 100644 index 0000000..560b40e --- /dev/null +++ b/f4se/f4se/Hooks_Debug.cpp @@ -0,0 +1,141 @@ +#include "Hooks_Debug.h" +#include "common/IInterlockedLong.h" +#include "common/IFileStream.h" +#include "f4se_common/Utilities.h" +#include "f4se_common/SafeWrite.h" +#include +#include + +static HMODULE s_dbgHelpDLL = NULL; + +typedef BOOL (__stdcall * _MiniDumpWriteDump)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType, CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); +static _MiniDumpWriteDump s_dbgHelpWriteDump = NULL; + +static LPTOP_LEVEL_EXCEPTION_FILTER s_oldExceptionFilter = NULL; +static SYSTEMTIME s_launchTime; +static char s_crashDumpPath[MAX_PATH]; + +static IInterlockedLong s_inExceptionFilter; + +// ignore these crash points to make dumps less spammy +const char * IsKnownCrash(EXCEPTION_POINTERS * info) +{ + return NULL; +} + +LONG WINAPI ExceptionFilter(EXCEPTION_POINTERS * info) +{ + if(s_inExceptionFilter.Claim()) + { + bool writeDump = true; + const char * crashReason = IsKnownCrash(info); + + if(crashReason) + { + _ERROR("Fallout 4 has crashed in a known crash location (%s). No crashdump will be written in release builds.", crashReason); + +#if NDEBUG + // skip writing crashdumps for these in release + writeDump = false; +#endif + } + + if(writeDump) + { + _ERROR("Fallout 4 has crashed. A minidump containing debugging information is being written to %s.", s_crashDumpPath); + + HANDLE dumpFile = CreateFile(s_crashDumpPath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if(dumpFile != INVALID_HANDLE_VALUE) + { + MINIDUMP_EXCEPTION_INFORMATION exceptionInfo; + + exceptionInfo.ThreadId = GetCurrentThreadId(); + exceptionInfo.ExceptionPointers = info; + exceptionInfo.ClientPointers = FALSE; + + BOOL result = s_dbgHelpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), dumpFile, MiniDumpNormal, &exceptionInfo, NULL, NULL); + if(result) + { + _ERROR("Minidump written."); + } + else + { + _ERROR("Unable to write minidump."); + } + + CloseHandle(dumpFile); + } + else + { + _ERROR("Unable to open minidump. (%08X)", GetLastError()); + } + } + + s_inExceptionFilter.Release(); + } + else + { + _ERROR("top-level exception filter hit multiple times"); + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_Hook(__in LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter) +{ + _MESSAGE("SetUnhandledExceptionFilter_Hook: %016I64X", lpTopLevelExceptionFilter); + + return NULL; +} + +void Hooks_Debug_Init() +{ + // can be enabled once we patch all dlls + // something else is overriding the exception filter + if(false) + { + _MESSAGE("minidumps enabled"); + + GetSystemTime(&s_launchTime); + + // try to get dbghelp + s_dbgHelpDLL = LoadLibrary("dbghelp.dll"); + if(s_dbgHelpDLL) + { + s_dbgHelpWriteDump = (_MiniDumpWriteDump)GetProcAddress(s_dbgHelpDLL, "MiniDumpWriteDump"); + if(!s_dbgHelpWriteDump) + _WARNING("dbghelp missing MiniDumpWriteDump, upgrade to dbghelp 5.1 or later"); + } + else + { + _MESSAGE("no dbghelp"); + } + + // we want to catch crashes from hook commit, apply exception filter in Init function + if(s_dbgHelpDLL && s_dbgHelpWriteDump) + { + // precalculate as much as possible + char myDocumentsPath[MAX_PATH]; + ASSERT(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, myDocumentsPath))); + + sprintf_s(s_crashDumpPath, sizeof(s_crashDumpPath), "%s\\My Games\\Fallout4\\F4SE\\Crashdumps\\%04d-%02d-%02d_%02d.%02d.%02d.dmp", myDocumentsPath, + s_launchTime.wYear, s_launchTime.wMonth, s_launchTime.wDay, + s_launchTime.wHour, s_launchTime.wMinute, s_launchTime.wSecond); + + IFileStream::MakeAllDirs(s_crashDumpPath); + + // replace previous exception filter + s_oldExceptionFilter = SetUnhandledExceptionFilter(ExceptionFilter); + _MESSAGE("old exception filter = %08X", s_oldExceptionFilter); + + // disable game overwriting exception filter + uintptr_t thunkAddress = (uintptr_t)GetIATAddr(GetModuleHandle(NULL), "kernel32.dll", "SetUnhandledExceptionFilter"); + SafeWrite64(thunkAddress, (uintptr_t)SetUnhandledExceptionFilter_Hook); + } + } +} + +void Hooks_Debug_Commit() +{ + +} diff --git a/f4se/f4se/Hooks_Debug.h b/f4se/f4se/Hooks_Debug.h new file mode 100644 index 0000000..82d1681 --- /dev/null +++ b/f4se/f4se/Hooks_Debug.h @@ -0,0 +1,4 @@ +#pragma once + +void Hooks_Debug_Init(); +void Hooks_Debug_Commit(); diff --git a/f4se/f4se/Hooks_GameData.cpp b/f4se/f4se/Hooks_GameData.cpp new file mode 100644 index 0000000..9f9e7aa --- /dev/null +++ b/f4se/f4se/Hooks_GameData.cpp @@ -0,0 +1,45 @@ +#include "Hooks_GameData.h" +#include "f4se/GameThreads.h" +#include "f4se/GameData.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/SafeWrite.h" +#include "f4se_common/BranchTrampoline.h" +#include "xbyak/xbyak.h" +#include "f4se/PluginManager.h" + +typedef UInt64 (* _InitGameDataThread_Run_Original)(InitGameDataThread * thread); +RelocAddr <_InitGameDataThread_Run_Original> InitGameDataThread_Run_Original(0x00D53580); + +typedef void (* _GameDataReady_Original)(bool isReady); +// writes to g_isGameDataReady + +// 936C94CC97C598DFE8A81979F95A75088F8B44C2+3AF +RelocAddr <_GameDataReady_Original> GameDataReady_Original(0x0082FA50); + +UInt64 InitGameDataRun_Hook(InitGameDataThread * thread) +{ + UInt64 res = InitGameDataThread_Run_Original(thread); + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_GameLoaded, (void*)NULL, 0, NULL); + return res; +} + +void GameDataReady_Hook(bool isReady) +{ + // This message is before so if the listener reads from the global it will be the previous value + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_GameDataReady, (void*)isReady, 1, NULL); + (*g_isGameDataReady) = isReady; +} + +void Hooks_GameData_Init() +{ + +} + +void Hooks_GameData_Commit() +{ + // ??_7InitGameDataThread@?A0x1eb8b661@@6B@ + RelocAddr InitGameDataThread_Run(0x02D3BF18 + 8); + + SafeWrite64(InitGameDataThread_Run.GetUIntPtr(), (UInt64)InitGameDataRun_Hook); + g_branchTrampoline.Write6Branch(GameDataReady_Original.GetUIntPtr(), (uintptr_t)GameDataReady_Hook); +} diff --git a/f4se/f4se/Hooks_GameData.h b/f4se/f4se/Hooks_GameData.h new file mode 100644 index 0000000..76a8cb3 --- /dev/null +++ b/f4se/f4se/Hooks_GameData.h @@ -0,0 +1,4 @@ +#pragma once + +void Hooks_GameData_Init(); +void Hooks_GameData_Commit(); diff --git a/f4se/f4se/Hooks_Gameplay.cpp b/f4se/f4se/Hooks_Gameplay.cpp new file mode 100644 index 0000000..991ede3 --- /dev/null +++ b/f4se/f4se/Hooks_Gameplay.cpp @@ -0,0 +1,92 @@ +#include "Hooks_Gameplay.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/BranchTrampoline.h" +#include "f4se_common/SafeWrite.h" +#include "f4se_common/f4se_version.h" +#include "f4se_common/Utilities.h" +#include "common/IFileStream.h" +#include "xbyak/xbyak.h" + +// DDB041F109C6211C69F6E5F20EEC28E4448B796B+53 +RelocAddr g_gameVersion(0x02C5D6F8); + +RelocAddr kHook_ShowVersion_Offset(0x00BBC4B0 + 0x53 + 3); +RelocAddr kHook_ShowVersion_OffsetBase(0x00BBC4B0 + 0x5A); + +static char s_patchedGameVersion[256]; + +static void InitVersionHook() +{ + char * originalGameVersion = g_gameVersion; + + sprintf_s(s_patchedGameVersion, sizeof(s_patchedGameVersion), "(F4SE %d.%d.%d rel %d) %s", + F4SE_VERSION_INTEGER, F4SE_VERSION_INTEGER_MINOR, F4SE_VERSION_INTEGER_BETA, F4SE_VERSION_RELEASEIDX, originalGameVersion); +} + +static void CommitVersionHook() +{ + size_t bufLen = strlen(s_patchedGameVersion) + 1; + + char * versionBuf = (char *)g_branchTrampoline.Allocate(bufLen); + memcpy(versionBuf, s_patchedGameVersion, bufLen); + + SafeWrite32(kHook_ShowVersion_Offset, uintptr_t(versionBuf) - kHook_ShowVersion_OffsetBase); +} + +static char * s_customControlMap = nullptr; + +RelocAddr kHook_CustomControlMap_Offset(0x01B2A1D0); +RelocAddr kHook_CustomControlMap_Return(0x01B2A1D0 + 5); + +static void InitControlMap() +{ + std::string controlMapPath = GetRuntimeDirectory() + "Data\\F4SE\\CustomControlMap.txt"; + + IFileStream src; + if(src.Open(controlMapPath.c_str())) + { + _MESSAGE("using custom control map: %s", controlMapPath.c_str()); + + s_customControlMap = new char[src.GetLength() + 1]; + src.ReadBuf(s_customControlMap, src.GetLength()); + s_customControlMap[src.GetLength()] = 0; + } +} + +static void CommitControlMap() +{ + if(s_customControlMap) + { + struct CustomControlMap_Code : Xbyak::CodeGenerator { + CustomControlMap_Code(void * buf, char * controlMapPtr) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label addrLabel; + + mov(ptr [rsp + 0x10], rbx); + mov(rdx, ptr [rip + addrLabel]); + jmp(kHook_CustomControlMap_Return); + + L(addrLabel); + dq(uintptr_t(controlMapPtr)); + } + }; + + void * codeBuf = g_branchTrampoline.StartAlloc(); + CustomControlMap_Code code(codeBuf, s_customControlMap); + g_branchTrampoline.EndAlloc(code.getCurr()); + + ASSERT(SafeWriteJump(kHook_CustomControlMap_Offset, uintptr_t(codeBuf))); + } +} + +void Hooks_Gameplay_Init() +{ + InitVersionHook(); + InitControlMap(); +} + +void Hooks_Gameplay_Commit() +{ + CommitVersionHook(); + CommitControlMap(); +} diff --git a/f4se/f4se/Hooks_Gameplay.h b/f4se/f4se/Hooks_Gameplay.h new file mode 100644 index 0000000..6567664 --- /dev/null +++ b/f4se/f4se/Hooks_Gameplay.h @@ -0,0 +1,4 @@ +#pragma once + +void Hooks_Gameplay_Init(); +void Hooks_Gameplay_Commit(); diff --git a/f4se/f4se/Hooks_Input.cpp b/f4se/f4se/Hooks_Input.cpp new file mode 100644 index 0000000..de38988 --- /dev/null +++ b/f4se/f4se/Hooks_Input.cpp @@ -0,0 +1,281 @@ +#include "f4se_common/Utilities.h" +#include "f4se_common/SafeWrite.h" + +#include "f4se_common/Relocation.h" +#include "f4se_common/BranchTrampoline.h" +#include "xbyak/xbyak.h" + +#include "f4se/InputMap.h" +#include "f4se/GameInput.h" +#include "f4se/GameMenus.h" + +#include "f4se/PapyrusEvents.h" +#include "f4se/PapyrusUtilities.h" + +#define HOOK_RAW_INPUT 0 +#define LOG_INPUT_HOOK 0 + +typedef void (* _CreateMenuControlHandlers)(MenuControls * mem); +RelocAddr <_CreateMenuControlHandlers> CreateMenuControlHandlers(0x012A80F0); +_CreateMenuControlHandlers CreateMenuControlHandlers_Original = nullptr; + +typedef void (* _CreatePlayerControlHandlers)(PlayerControls * mem); +RelocAddr <_CreatePlayerControlHandlers> CreatePlayerControlHandlers(0x00F44CC0); +_CreatePlayerControlHandlers CreatePlayerControlHandlers_Original = nullptr; + +#if HOOK_RAW_INPUT + +typedef BOOL (WINAPI * _RegisterRawInputDevices)(RAWINPUTDEVICE * devices, UINT numDevices, UINT structSize); +_RegisterRawInputDevices Original_RegisterRawInputDevices = nullptr; + +typedef UINT (WINAPI * _GetRawInputData)(HRAWINPUT rawinput, UINT cmd, void * data, UINT * dataSize, UINT headerSize); +_GetRawInputData Original_GetRawInputData = nullptr; + +BOOL WINAPI Hook_RegisterRawInputDevices(RAWINPUTDEVICE * devices, UINT numDevices, UINT structSize) +{ +#if LOG_INPUT_HOOK + _MESSAGE("RegisterRawInputDevices %08X %08X", numDevices, structSize); + for(UINT i = 0; i < numDevices; i++) + { + RAWINPUTDEVICE * dev = &devices[i]; + + _MESSAGE("%04X %04X %08X %08X", dev->usUsagePage, dev->usUsage, dev->dwFlags, dev->hwndTarget); + } +#endif + + BOOL result = Original_RegisterRawInputDevices(devices, numDevices, structSize); + + return result; +} + +UINT WINAPI Hook_GetRawInputData(HRAWINPUT rawinput, UINT cmd, void * data, UINT * dataSize, UINT headerSize) +{ + UINT result = Original_GetRawInputData(rawinput, cmd, data, dataSize, headerSize); + +#if LOG_INPUT_HOOK + + _MESSAGE("GetRawInputData %08X %08X %08X", cmd, *dataSize, headerSize); + + if(data) + { + RAWINPUT * input = (RAWINPUT *)data; + + _MESSAGE("hdr: %08X %08X %08X %08X", input->header.dwType, input->header.dwSize, input->header.hDevice, input->header.wParam); + + if(cmd == RID_INPUT) + { + switch(input->header.dwType) + { + case RIM_TYPEHID: + { + _MESSAGE("hid: %08X %08X", input->data.hid.dwSizeHid, input->data.hid.dwCount); + } + break; + + case RIM_TYPEKEYBOARD: + { + RAWKEYBOARD * kbd = &input->data.keyboard; + + _MESSAGE("kbd: %04X %04X %04X %04X %08X %08X", + kbd->MakeCode, kbd->Flags, kbd->Reserved, kbd->VKey, kbd->Message, kbd->ExtraInformation); + } + break; + + case RIM_TYPEMOUSE: + { + RAWMOUSE * mse = &input->data.mouse; + + _MESSAGE("mse: %04X %08X %08X %d %d %08X", + mse->usFlags, mse->ulButtons, mse->ulRawButtons, mse->lLastX, mse->lLastY, mse->ulExtraInformation); + } + break; + } + } + } + +#endif + + return result; +} + +#endif + +void HandleButtonEvent(ButtonEvent * inputEvent) +{ + UInt32 keyCode; + UInt32 deviceType = inputEvent->deviceType; + UInt32 keyMask = inputEvent->keyMask; + + // Mouse + if (deviceType == InputEvent::kDeviceType_Mouse) + keyCode = InputMap::kMacro_MouseButtonOffset + keyMask; + // Gamepad + else if (deviceType == InputEvent::kDeviceType_Gamepad) + keyCode = InputMap::GamepadMaskToKeycode(keyMask); + // Keyboard + else + keyCode = keyMask; + + // Valid scancode? + if (keyCode >= InputMap::kMaxMacros) + return; + + BSFixedString control = *inputEvent->GetControlID(); + float timer = inputEvent->timer; + + bool isDown = inputEvent->isDown == 1.0f && timer == 0.0f; + bool isUp = inputEvent->isDown == 0.0f && timer != 0.0f; + + if (isDown) + { + g_inputKeyEventRegs.ForEach( + keyCode, + [&keyCode](const EventRegistration & reg) + { + SendPapyrusEvent1(reg.handle, reg.scriptName, "OnKeyDown", keyCode); + } + ); + g_inputControlEventRegs.ForEach( + control, + [&control](const EventRegistration & reg) + { + SendPapyrusEvent1(reg.handle, reg.scriptName, "OnControlDown", control); + } + ); + } + else if (isUp) + { + g_inputKeyEventRegs.ForEach( + keyCode, + [&keyCode, &timer](const EventRegistration & reg) + { + SendPapyrusEvent2(reg.handle, reg.scriptName, "OnKeyUp", keyCode, timer); + } + ); + g_inputControlEventRegs.ForEach( + control, + [&control, &timer](const EventRegistration & reg) + { + SendPapyrusEvent2(reg.handle, reg.scriptName, "OnControlUp", control, timer); + } + ); + } +} + +class F4SEInputHandler : public PlayerInputHandler +{ +public: + F4SEInputHandler() : PlayerInputHandler() { } + + virtual void OnButtonEvent(ButtonEvent * inputEvent) + { + if((*g_ui)->numPauseGame) + { + return; + } + + HandleButtonEvent(inputEvent); + } +}; + +class F4SEInputMenuHandler : public BSInputEventUser +{ +public: + F4SEInputMenuHandler() : BSInputEventUser(true) { } + + virtual void OnButtonEvent(ButtonEvent * inputEvent) + { + if(!(*g_ui)->numPauseGame) + { + return; + } + + HandleButtonEvent(inputEvent); + } +}; + + +F4SEInputHandler g_inputHandler; +F4SEInputMenuHandler g_inputMenuHandler; + +void CreatePlayerControlHandlers_Hook(PlayerControls * playerControls) +{ + // Process F4SE handlers first so no events will be blocked + playerControls->inputEvents1.Push(&g_inputHandler); + + CreatePlayerControlHandlers_Original(playerControls); +} + +void CreateMenuControlHandlers_Hook(MenuControls * menuControls) +{ + CreateMenuControlHandlers_Original(menuControls); + menuControls->inputEvents.Push(&g_inputMenuHandler); +} + +void Hooks_Input_Init() +{ + // +} + +void Hooks_Input_Commit() +{ +#if HOOK_RAW_INPUT + void ** iat = (void **)GetIATAddr(GetModuleHandle(NULL), "user32.dll", "RegisterRawInputDevices"); + Original_RegisterRawInputDevices = (_RegisterRawInputDevices)*iat; + SafeWrite64((uintptr_t)iat, (UInt64)Hook_RegisterRawInputDevices); + + iat = (void **)GetIATAddr(GetModuleHandle(NULL), "user32.dll", "GetRawInputData"); + Original_GetRawInputData = (_GetRawInputData)*iat; + SafeWrite64((uintptr_t)iat, (UInt64)Hook_GetRawInputData); +#endif + + // hook adding control handlers to PlayerControls + { + struct CreatePlayerControlHandlers_Code : Xbyak::CodeGenerator { + CreatePlayerControlHandlers_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr[rsp+0x08], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(CreatePlayerControlHandlers.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + CreatePlayerControlHandlers_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + CreatePlayerControlHandlers_Original = (_CreatePlayerControlHandlers)codeBuf; + + g_branchTrampoline.Write5Branch(CreatePlayerControlHandlers.GetUIntPtr(), (uintptr_t)CreatePlayerControlHandlers_Hook); + } + + // hook adding control handlers to MenuControls + { + struct CreateMenuControlHandlers_Code : Xbyak::CodeGenerator { + CreateMenuControlHandlers_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr[rsp+0x08], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(CreateMenuControlHandlers.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + CreateMenuControlHandlers_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + CreateMenuControlHandlers_Original = (_CreateMenuControlHandlers)codeBuf; + + g_branchTrampoline.Write5Branch(CreateMenuControlHandlers.GetUIntPtr(), (uintptr_t)CreateMenuControlHandlers_Hook); + } +} diff --git a/f4se/f4se/Hooks_Input.h b/f4se/f4se/Hooks_Input.h new file mode 100644 index 0000000..c364740 --- /dev/null +++ b/f4se/f4se/Hooks_Input.h @@ -0,0 +1,4 @@ +#pragma once + +void Hooks_Input_Init(); +void Hooks_Input_Commit(); diff --git a/f4se/f4se/Hooks_Memory.cpp b/f4se/f4se/Hooks_Memory.cpp new file mode 100644 index 0000000..3c243e6 --- /dev/null +++ b/f4se/f4se/Hooks_Memory.cpp @@ -0,0 +1,100 @@ +#include "Hooks_Memory.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/BranchTrampoline.h" +#include "xbyak/xbyak.h" +#include "GameMemory.h" +#include "GameAPI.h" +#include + +#if 0 + +static RelocAddr kHook_IMemoryStoreCtor_Offset(0x00000000); +static RelocAddr kHook_IMemoryStoreCtor_VTable(0x00000000); + +static RelocAddr kHook_IMemoryStoreDtor_Offset(0x00000000); + +typedef std::set HeapList; +static HeapList s_heaps; + +void Hooks_Memory_Init() +{ + // +} + +static IMemoryStore * Hook_IMemoryStoreCtor(IMemoryStore * obj) +{ + s_heaps.insert(obj); + + // write the vtable + void ** rawObj = (void **)obj; + + *rawObj = kHook_IMemoryStoreCtor_VTable; + + return obj; +} + +static void Hook_IMemoryStoreDtor(IMemoryStoreBase * obj) +{ + // this should update the vtable, but it shouldn't matter + + s_heaps.erase(obj); +} + +void Hooks_Memory_Commit() +{ + g_branchTrampoline.Write6Branch(kHook_IMemoryStoreCtor_Offset, uintptr_t(Hook_IMemoryStoreCtor)); + g_branchTrampoline.Write6Branch(kHook_IMemoryStoreDtor_Offset, uintptr_t(Hook_IMemoryStoreDtor)); +} + +std::string FriendlyMemSize(UInt64 size) +{ + char result[256]; + + if(!size) + sprintf_s(result, sizeof(result), "0"); + else if(size < 1024) + sprintf_s(result, sizeof(result), "%dB", size); + else if(size < 1024 * 1024) + sprintf_s(result, sizeof(result), "%dKB", size / 1024); + else if(size < 1024 * 1024 * 1024) + sprintf_s(result, sizeof(result), "%dMB", size / (1024 * 1024)); + else if(size < 1024LL * 1024 * 1024 * 1024) + sprintf_s(result, sizeof(result), "%dGB", size / (1024 * 1024 * 1024)); + else + sprintf_s(result, sizeof(result), "%dTB", size / (1024LL * 1024 * 1024 * 1024)); + + return result; +} + +void PrintHeapStatus() +{ + Console_Print("%d heaps", s_heaps.size()); + + _MESSAGE("%d heaps", s_heaps.size()); + + for(HeapList::iterator iter = s_heaps.begin(); iter != s_heaps.end(); ++iter) + { + IMemoryStoreBase * heap = *iter; + + _MESSAGE("[ heap %016I64X ]", heap); + gLog.Indent(); + + void ** rawObj = (void **)heap; + + _MESSAGE("vtbl: %016I64X", *rawObj); + + IMemoryStore::MemInfo info = { 0 }; + heap->GetStats(&info); + + _MESSAGE("name: %s", info.debugName); + _MESSAGE("allocated: %016I64X (%s)", info.bytesAllocated, FriendlyMemSize(info.bytesAllocated).c_str()); + _MESSAGE("alloc chunk size: %016I64X (%s)", info.allocChunkSize2, FriendlyMemSize(info.allocChunkSize2).c_str()); + _MESSAGE("max size: %016I64X (%s)", info.maxSize, FriendlyMemSize(info.maxSize).c_str()); + _MESSAGE("heap overhead: %08X (%s)", info.unk20, FriendlyMemSize(info.unk20).c_str()); + _MESSAGE("remaining: %016I64X (%s)", info.remaining, FriendlyMemSize(info.remaining).c_str()); + + gLog.Outdent(); + } +} + +#endif diff --git a/f4se/f4se/Hooks_Memory.h b/f4se/f4se/Hooks_Memory.h new file mode 100644 index 0000000..c16745f --- /dev/null +++ b/f4se/f4se/Hooks_Memory.h @@ -0,0 +1,6 @@ +#pragma once + +void Hooks_Memory_Init(); +void Hooks_Memory_Commit(); + +void PrintHeapStatus(); diff --git a/f4se/f4se/Hooks_ObScript.cpp b/f4se/f4se/Hooks_ObScript.cpp new file mode 100644 index 0000000..3fe2ed5 --- /dev/null +++ b/f4se/f4se/Hooks_ObScript.cpp @@ -0,0 +1,54 @@ +#include "Hooks_ObScript.h" +#include "ObScript.h" +#include "GameAPI.h" +#include "GameReferences.h" +#include "f4se_common/SafeWrite.h" +#include "f4se_common/f4se_version.h" + +// currently ForceRSXCrash, could also use ToggleESRAM and several others +static ObScriptCommand * s_hijackedCommand = nullptr; + +void Hooks_ObScript_Init() +{ + // instead of hooking the entire classic scripting system, we're just hijacking some unused commands + + for(ObScriptCommand * iter = g_firstConsoleCommand; iter->opcode < (kObScript_NumConsoleCommands + kObScript_ConsoleOpBase); ++iter) + { + if(!strcmp(iter->longName, "ForceRSXCrash")) + { + s_hijackedCommand = iter; + break; + } + } + + if(!s_hijackedCommand) + { + _ERROR("couldn't find obscript command to use"); + } +} + +bool GetF4SEVersion_Execute(void * paramInfo, void * scriptData, TESObjectREFR * thisObj, void * containingObj, void * scriptObj, void * locals, double * result, void * opcodeOffsetPtr) +{ + _MESSAGE("GetF4SEVersion_Execute"); + + Console_Print("F4SE version: %d.%d.%d, release idx %d, runtime %08X", + F4SE_VERSION_INTEGER, F4SE_VERSION_INTEGER_MINOR, F4SE_VERSION_INTEGER_BETA, + F4SE_VERSION_RELEASEIDX, RUNTIME_VERSION); + + return true; +} + +void Hooks_ObScript_Commit() +{ + ObScriptCommand cmd = *s_hijackedCommand; + + cmd.longName = "GetF4SEVersion"; + cmd.shortName = ""; + cmd.helpText = ""; + cmd.needsParent = 0; + cmd.numParams = 0; + cmd.execute = GetF4SEVersion_Execute; + cmd.flags = 0; + + SafeWriteBuf((uintptr_t)s_hijackedCommand, &cmd, sizeof(cmd)); +} diff --git a/f4se/f4se/Hooks_ObScript.h b/f4se/f4se/Hooks_ObScript.h new file mode 100644 index 0000000..85d9051 --- /dev/null +++ b/f4se/f4se/Hooks_ObScript.h @@ -0,0 +1,4 @@ +#pragma once + +void Hooks_ObScript_Init(); +void Hooks_ObScript_Commit(); diff --git a/f4se/f4se/Hooks_Papyrus.cpp b/f4se/f4se/Hooks_Papyrus.cpp new file mode 100644 index 0000000..cac61f4 --- /dev/null +++ b/f4se/f4se/Hooks_Papyrus.cpp @@ -0,0 +1,338 @@ +#include "Hooks_Papyrus.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/BranchTrampoline.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusEvents.h" + +#include "f4se/PapyrusF4SE.h" +#include "f4se/PapyrusForm.h" +#include "f4se/PapyrusMath.h" +#include "f4se/PapyrusActor.h" +#include "f4se/PapyrusActorBase.h" +#include "f4se/PapyrusHeadPart.h" +#include "f4se/PapyrusObjectReference.h" +#include "f4se/PapyrusGame.h" +#include "f4se/PapyrusScriptObject.h" +#include "f4se/PapyrusDelayFunctors.h" +#include "f4se/PapyrusUtility.h" +#include "f4se/PapyrusUI.h" +#include "f4se/PapyrusEquipSlot.h" +#include "f4se/PapyrusWeapon.h" +#include "f4se/PapyrusObjectMod.h" +#include "f4se/PapyrusInstanceData.h" +#include "f4se/PapyrusWaterType.h" +#include "f4se/PapyrusCell.h" +#include "f4se/PapyrusPerk.h" +#include "f4se/PapyrusLocation.h" +#include "f4se/PapyrusEncounterZone.h" +#include "f4se/PapyrusInput.h" +#include "f4se/PapyrusDefaultObject.h" +#include "f4se/PapyrusConstructibleObject.h" +#include "f4se/PapyrusComponent.h" +#include "f4se/PapyrusMiscObject.h" +#include "f4se/PapyrusMaterialSwap.h" +#include "f4se/PapyrusFavoritesManager.h" +#include "f4se/PapyrusArmorAddon.h" +#include "f4se/PapyrusArmor.h" + +#include "f4se/Serialization.h" + +#include "xbyak/xbyak.h" + +RelocAddr RegisterPapyrusFunctions_Start(0x013E6BC0 + 0x461); +RelocAddr DelayFunctorQueue_Start(0x01377CD0 + 0x6A); + +typedef bool (* _SaveRegistrationHandles)(void * unk1, void * vm, void * handleReaderWriter, void * saveStorageWrapper); +RelocAddr <_SaveRegistrationHandles> SaveRegistrationHandles(0x01474AD0); +_SaveRegistrationHandles SaveRegistrationHandles_Original = nullptr; + +typedef bool (* _LoadRegistrationHandles)(void * unk1, void * vm, void * handleReaderWriter, UInt16 version, void * loadStorageWrapper, void * unk2); +RelocAddr <_LoadRegistrationHandles> LoadRegistrationHandles(0x01474B60); +_LoadRegistrationHandles LoadRegistrationHandles_Original = nullptr; + +typedef void (* _RevertGlobalData)(void * vm); +RelocAddr <_RevertGlobalData> RevertGlobalData(0x013762F0); +_RevertGlobalData RevertGlobalData_Original = nullptr; + +typedef std::list PapyrusPluginList; +static PapyrusPluginList s_pap_plugins; + +bool RegisterPapyrusPlugin(F4SEPapyrusInterface::RegisterFunctions callback) +{ + s_pap_plugins.push_back(callback); + return true; +} + +void GetExternalEventRegistrations(const char * eventName, void * data, F4SEPapyrusInterface::RegistrantFunctor functor) +{ + g_externalEventRegs.ForEach(BSFixedString(eventName), [&functor, &data](const EventRegistration & reg) + { + functor(reg.handle, reg.scriptName.c_str(), reg.params.callbackName.c_str(), data); + }); +} + +void Hooks_Papyrus_Init() +{ + +} + +void RegisterPapyrusFunctions_Hook(VirtualMachine ** vmPtr) +{ + _MESSAGE("RegisterPapyrusFunctions_Hook"); + + VirtualMachine * vm = (*vmPtr); + + // ScriptObject + papyrusScriptObject::RegisterFuncs(vm); + + // EquipSlot + papyrusEquipSlot::RegisterFuncs(vm); + + // WaterType + papyrusWaterType::RegisterFuncs(vm); + + // MaterialSwap + papyrusMaterialSwap::RegisterFuncs(vm); + + // F4SE + papyrusF4SE::RegisterFuncs(vm); + + // Math + papyrusMath::RegisterFuncs(vm); + + // Form + papyrusForm::RegisterFuncs(vm); + + // ObjectReference + papyrusObjectReference::RegisterFuncs(vm); + + // ActorBase + papyrusActorBase::RegisterFuncs(vm); + + // Actor + papyrusActor::RegisterFuncs(vm); + + // HeadPart + papyrusHeadPart::RegisterFuncs(vm); + + // Game + papyrusGame::RegisterFuncs(vm); + + // Utility + papyrusUtility::RegisterFuncs(vm); + + // UI + papyrusUI::RegisterFuncs(vm); + + // Weapon + papyrusWeapon::RegisterFuncs(vm); + + // ObjectMod + papyrusObjectMod::RegisterFuncs(vm); + + // InstanceData + papyrusInstanceData::RegisterFuncs(vm); + + // Cell + papyrusCell::RegisterFuncs(vm); + + // Perk + papyrusPerk::RegisterFuncs(vm); + + // EncounterZone + papyrusEncounterZone::RegisterFuncs(vm); + + // Location + papyrusLocation::RegisterFuncs(vm); + + // Input + papyrusInput::RegisterFuncs(vm); + + // DefaultObject + papyrusDefaultObject::RegisterFuncs(vm); + + // ConstructibleObject + papyrusConstructibleObject::RegisterFuncs(vm); + + // Component + papyrusComponent::RegisterFuncs(vm); + + // MiscObject + papyrusMiscObject::RegisterFuncs(vm); + + // FavoritesManager + papyrusFavoritesManager::RegisterFuncs(vm); + + // ArmorAddon + papyrusArmorAddon::RegisterFuncs(vm); + + // Armor + papyrusArmor::RegisterFuncs(vm); + + GetEventDispatcher()->AddEventSink(&g_furnitureEventSink); + + // Plugins + for(PapyrusPluginList::iterator iter = s_pap_plugins.begin(); iter != s_pap_plugins.end(); ++iter) + { + (*iter)(vm); + } +} + +bool SaveRegistrationHandles_Hook(void * unk1, void * vm, void * handleReaderWriter, void * loadStorageWrapper) +{ + bool ret = SaveRegistrationHandles_Original(unk1, vm, handleReaderWriter, loadStorageWrapper); + Serialization::HandleSaveGlobalData(); + return ret; +} + +bool LoadRegistrationHandles_Hook(void * unk1, void * vm, void * handleReaderWriter, UInt16 version, void * loadStorageWrapper, void * unk2) +{ + bool ret = LoadRegistrationHandles_Original(unk1, vm, handleReaderWriter, version, loadStorageWrapper, unk2); + Serialization::HandleLoadGlobalData(); + return ret; +} + +void RevertGlobalData_Hook(void * vm) +{ + RevertGlobalData_Original(vm); + Serialization::HandleRevertGlobalData(); +} + +LONGLONG DelayFunctorQueue_Hook(float budget) +{ + F4SEDelayFunctorManagerInstance().OnPreTick(); + + LARGE_INTEGER startTime; + QueryPerformanceCounter(&startTime); + + // Sharing budget with papyrus queue + F4SEDelayFunctorManagerInstance().OnTick(startTime.QuadPart, budget); + + return startTime.QuadPart; +} + +void Hooks_Papyrus_Commit() +{ + struct RegisterPapyrusFunctions_Code : Xbyak::CodeGenerator { + RegisterPapyrusFunctions_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + call((void *)&RegisterPapyrusFunctions_Hook); + mov(rcx, rbx); + add(rsp, 0x20); + pop(rbx); + ret(); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + RegisterPapyrusFunctions_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + g_branchTrampoline.Write6Branch(RegisterPapyrusFunctions_Start, uintptr_t(code.getCode())); + + // save registration handles hook + { + struct SaveRegistrationHandles_Code : Xbyak::CodeGenerator { + SaveRegistrationHandles_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr [rsp+0x10], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(SaveRegistrationHandles.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + SaveRegistrationHandles_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + SaveRegistrationHandles_Original = (_SaveRegistrationHandles)codeBuf; + + g_branchTrampoline.Write5Branch(SaveRegistrationHandles.GetUIntPtr(), (uintptr_t)SaveRegistrationHandles_Hook); + } + + + // load registration handles hook + { + struct LoadRegistrationHandles_Code : Xbyak::CodeGenerator { + LoadRegistrationHandles_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr [rsp+0x10], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(LoadRegistrationHandles.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + LoadRegistrationHandles_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + LoadRegistrationHandles_Original = (_LoadRegistrationHandles)codeBuf; + + g_branchTrampoline.Write5Branch(LoadRegistrationHandles.GetUIntPtr(), (uintptr_t)LoadRegistrationHandles_Hook); + } + + // revert global data hook + { + struct RevertGlobalData_Code : Xbyak::CodeGenerator { + RevertGlobalData_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + push(rdi); + push(r15); + sub(rsp, 0x38); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(RevertGlobalData.GetUIntPtr() + 8); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + RevertGlobalData_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + RevertGlobalData_Original = (_RevertGlobalData)codeBuf; + + g_branchTrampoline.Write6Branch(RevertGlobalData.GetUIntPtr(), (uintptr_t)RevertGlobalData_Hook); + } + + // hook ProcessVMTick + { + struct DelayFunctorQueue_Code : Xbyak::CodeGenerator { + DelayFunctorQueue_Code(void * buf, uintptr_t funcAddr) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label funcLabel; + Xbyak::Label retnLabel; + + movss(xmm0, ptr[rsp + 0x98 + 0x28]); + call(ptr [rip + funcLabel]); + jmp(ptr [rip + retnLabel]); + + L(funcLabel); + dq(funcAddr); + + L(retnLabel); + dq(DelayFunctorQueue_Start.GetUIntPtr() + 0x5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + DelayFunctorQueue_Code code(codeBuf, (uintptr_t)DelayFunctorQueue_Hook); + g_localTrampoline.EndAlloc(code.getCurr()); + + g_branchTrampoline.Write5Branch(DelayFunctorQueue_Start.GetUIntPtr(), uintptr_t(code.getCode())); + } +} diff --git a/f4se/f4se/Hooks_Papyrus.h b/f4se/f4se/Hooks_Papyrus.h new file mode 100644 index 0000000..6545685 --- /dev/null +++ b/f4se/f4se/Hooks_Papyrus.h @@ -0,0 +1,9 @@ +#pragma once + +#include "f4se/PluginAPI.h" + +void Hooks_Papyrus_Init(); +void Hooks_Papyrus_Commit(); + +bool RegisterPapyrusPlugin(F4SEPapyrusInterface::RegisterFunctions); +void GetExternalEventRegistrations(const char * eventName, void * data, F4SEPapyrusInterface::RegistrantFunctor functor); diff --git a/f4se/f4se/Hooks_SaveLoad.cpp b/f4se/f4se/Hooks_SaveLoad.cpp new file mode 100644 index 0000000..0871efe --- /dev/null +++ b/f4se/f4se/Hooks_SaveLoad.cpp @@ -0,0 +1,164 @@ +#include "Hooks_SaveLoad.h" +#include "GameForms.h" + +#include "f4se_common/BranchTrampoline.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/SafeWrite.h" + +#include "f4se/PluginManager.h" +#include "f4se/Serialization.h" + +#include "xbyak/xbyak.h" + +class BGSSaveLoadGame; + +typedef void (* _SaveGame)(BGSSaveLoadGame * saveLoadMgr, const char * name, UInt8 unk1); +RelocAddr <_SaveGame> SaveGame(0x00CDE4F0); +_SaveGame SaveGame_Original = nullptr; + +typedef bool (* _LoadGame)(BGSSaveLoadGame * saveLoadMgr, const char * name, UInt8 unk1, void * unk2); +RelocAddr <_LoadGame> LoadGame(0x00CDE9F0); +_LoadGame LoadGame_Original = nullptr; + +RelocAddr NewGame_Enter(0x012A23E0 + 0x5A); + +typedef void (* _DeleteSaveGame)(BGSSaveLoadGame * saveLoadMgr, const char * name, UInt32 unk1, UInt8 unk2); +RelocAddr <_DeleteSaveGame> DeleteSaveGame(0x00CECF50); +_DeleteSaveGame DeleteSaveGame_Original = nullptr; + +void SaveGame_Hook(BGSSaveLoadGame * saveLoadMgr, const char * saveName, UInt8 unk1) +{ +#ifdef _DEBUG + _MESSAGE("Executing BGSSaveLoadGame::SaveGame_Hook. saveName: %s", saveName); +#endif + + Serialization::SetSaveName(saveName); + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_PreSaveGame, (void*)saveName, strlen(saveName), NULL); + SaveGame_Original(saveLoadMgr, saveName, unk1); + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_PostSaveGame, (void*)saveName, strlen(saveName), NULL); + Serialization::SetSaveName(NULL); +} + +bool LoadGame_Hook(BGSSaveLoadGame * saveLoadMgr, const char * saveName, UInt8 unk1, void * unk2) +{ +#ifdef _DEBUG + _MESSAGE("Executing BGSSaveLoadGame::LoadGame_Hook. saveName: %s", saveName); +#endif + + Serialization::SetSaveName(saveName); + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_PreLoadGame, (void*)saveName, strlen(saveName), NULL); + bool result = LoadGame_Original(saveLoadMgr, saveName, unk1, unk2); + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_PostLoadGame, (void*)result, 1, NULL); + Serialization::SetSaveName(NULL); + return result; +} + +void DeleteSaveGame_Hook(BGSSaveLoadGame * saveLoadMgr, const char * saveName, UInt32 unk1, UInt8 unk2) +{ +#ifdef _DEBUG + _MESSAGE("Executing BGSSaveLoadGame::DeleteSaveGame_Hook. saveName: %s", saveName); +#endif + + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_DeleteGame, (void*)saveName, strlen(saveName), NULL); + DeleteSaveGame_Original(saveLoadMgr, saveName, unk1, unk2); + Serialization::HandleDeleteSave(saveName); +} + +class TESQuest; + +UInt8 TESQuest::NewGame_Hook(UInt8 * unk1, UInt8 unk2) +{ +#ifdef _DEBUG + _MESSAGE("Executing TESQuest::NewGame_Hook."); +#endif + UInt8 ret = CALL_MEMBER_FN(this, NewGame_Internal)(unk1, unk2); + PluginManager::Dispatch_Message(0, F4SEMessagingInterface::kMessage_NewGame, (void*)this, sizeof(void*), NULL); + return ret; +} + +void Hooks_SaveLoad_Init() +{ + +} + +void Hooks_SaveLoad_Commit() +{ + // hook save game + { + struct SaveGame_Code : Xbyak::CodeGenerator { + SaveGame_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(rax, rsp); + mov(ptr[rax+0x18], r8b); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(SaveGame.GetUIntPtr() + 7); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + SaveGame_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + SaveGame_Original = (_SaveGame)codeBuf; + + g_branchTrampoline.Write6Branch(SaveGame.GetUIntPtr(), (uintptr_t)SaveGame_Hook); + } + + // hook load game + { + struct LoadGame_Code : Xbyak::CodeGenerator { + LoadGame_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr[rsp+0x18], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(LoadGame.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + LoadGame_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + LoadGame_Original = (_LoadGame)codeBuf; + + g_branchTrampoline.Write5Branch(LoadGame.GetUIntPtr(), (uintptr_t)LoadGame_Hook); + } + + // new game + g_branchTrampoline.Write5Call(NewGame_Enter, GetFnAddr(&TESQuest::NewGame_Hook)); + + // delete save game + { + struct DeleteSaveGame_Code : Xbyak::CodeGenerator { + DeleteSaveGame_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr[rsp+0x08], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(DeleteSaveGame.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + DeleteSaveGame_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + DeleteSaveGame_Original = (_DeleteSaveGame)codeBuf; + + g_branchTrampoline.Write5Branch(DeleteSaveGame.GetUIntPtr(), (uintptr_t)DeleteSaveGame_Hook); + } +} diff --git a/f4se/f4se/Hooks_SaveLoad.h b/f4se/f4se/Hooks_SaveLoad.h new file mode 100644 index 0000000..81ced9a --- /dev/null +++ b/f4se/f4se/Hooks_SaveLoad.h @@ -0,0 +1,4 @@ +#pragma once + +void Hooks_SaveLoad_Init(); +void Hooks_SaveLoad_Commit(); diff --git a/f4se/f4se/Hooks_Scaleform.cpp b/f4se/f4se/Hooks_Scaleform.cpp new file mode 100644 index 0000000..3688060 --- /dev/null +++ b/f4se/f4se/Hooks_Scaleform.cpp @@ -0,0 +1,696 @@ +#include "Hooks_Scaleform.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/BranchTrampoline.h" +#include "xbyak/xbyak.h" + +#include "f4se_common/f4se_version.h" +#include "common/IDirectoryIterator.h" +#include "f4se/PluginManager.h" + +#include "Translation.h" + +#include "ScaleformMovie.h" +#include "ScaleformValue.h" +#include "ScaleformCallbacks.h" +#include "ScaleformState.h" +#include "ScaleformLoader.h" +#include "ScaleformTranslator.h" + +#include "PapyrusEvents.h" +#include "PapyrusScaleformAdapter.h" +#include "GameInput.h" +#include "NiTextures.h" + +#include "CustomMenu.h" + +#include + +class BSScaleformManager; + +typedef BSScaleformManager * (* _BSScaleformManager_Ctor)(BSScaleformManager * mem); +RelocAddr <_BSScaleformManager_Ctor> BSScaleformManager_Ctor(0x02110430); +_BSScaleformManager_Ctor BSScaleformManager_Ctor_Original = nullptr; + +typedef UInt32 (* _BSScaleformTint)(BSGFxShaderFXTarget * value, float * colors, float multiplier); +RelocAddr <_BSScaleformTint> BSScaleformTint(0x020F2990); +_BSScaleformTint BSScaleformTint_Original = nullptr; + +RelocAddr ScaleformInitHook_Start(0x02110AD0 + 0x188); + +RelocAddr IMenuCreateHook_Start(0x02042430 + 0x90A); + +// D7C709A779249EBC0C50BB992E9FD088A33B282F+76 +RelocAddr SetMenuName(0x01B41ED0); + +//// plugin API +struct ScaleformPluginInfo +{ + const char * name; + F4SEScaleformInterface::RegisterCallback callback; +}; + +typedef std::list PluginList; +static PluginList s_plugins; + +bool g_logScaleform = false; + +static std::unordered_map> s_mountedTextures; +static BSReadWriteLock s_mountedTexturesLock; + +bool RegisterScaleformPlugin(const char * name, F4SEScaleformInterface::RegisterCallback callback) +{ + // check for a name collision + for(PluginList::iterator iter = s_plugins.begin(); iter != s_plugins.end(); ++iter) + { + if(!strcmp(iter->name, name)) + { + _WARNING("scaleform plugin name collision: %s", iter->name); + return false; + } + } + + ScaleformPluginInfo info; + + info.name = name; + info.callback = callback; + + s_plugins.push_back(info); + + return true; +} + +void Hooks_Scaleform_Init() +{ + // +} + +class F4SEScaleform_VisitMembers : public GFxValue::ObjectInterface::ObjVisitor +{ +public: + F4SEScaleform_VisitMembers(GFxMovieRoot * root, GFxValue * result) : m_root(root), m_result(result) { } + virtual void Visit(const char * member, GFxValue * value) override + { + GFxValue str; + m_root->CreateString(&str, member); + m_result->PushBack(&str); + } + + GFxMovieRoot * m_root; + GFxValue * m_result; +}; + +class F4SEScaleform_GetMembers : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args* args) + { + if(args->numArgs >= 1) + { + args->movie->movieRoot->CreateArray(args->result); + F4SEScaleform_VisitMembers dm(args->movie->movieRoot, args->result); + args->args[0].VisitMembers(&dm); + } + } +}; + +class F4SEScaleform_AllowTextInput : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args * args) + { + ASSERT(args->numArgs >= 1); + ASSERT(args->args[0].GetType() == GFxValue::kType_Bool); + + (*g_inputMgr)->AllowTextInput(args->args[0].GetBool()); + } +}; + +class F4SEScaleform_SendExternalEvent : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args * args) + { + ASSERT(args->numArgs >= 1); + ASSERT(args->args[0].GetType() == GFxValue::kType_String); + + BSFixedString eventName(args->args[0].GetString()); + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + + g_externalEventRegs.ForEach( + eventName, + [&args, &vm](const EventRegistration & reg) + { + VMValue receiver; + if(GetIdentifier(&receiver, reg.handle, ®.scriptName, vm)) { + VMValue packedArgs; + UInt32 length = args->numArgs - 1; + VMValue::ArrayData * arrayData = nullptr; + + vm->CreateArray(&packedArgs, length, &arrayData); + + packedArgs.type.value = VMValue::kType_VariableArray; + packedArgs.data.arr = arrayData; + + for(UInt32 i = 0; i < length; i++) + { + VMValue * var = new VMValue; + PlatformAdapter::ConvertScaleformValue(var, &args->args[i + 1], vm); + arrayData->arr.entries[i].SetVariable(var); + } + + if(receiver.IsIdentifier()) { + VMIdentifier * identifier = receiver.data.id; + if(identifier) { + CallFunctionNoWait_Internal(vm, 0, identifier, ®.params.callbackName, &packedArgs); + } + } + } + } + ); + } +}; + +class F4SEScaleform_CallFunctionNoWait : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args * args) + { + ASSERT(args->numArgs >= 2); + ASSERT(args->args[0].GetType() == GFxValue::kType_Object); + ASSERT(args->args[1].GetType() == GFxValue::kType_String); + + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + VMValue receiver; + PlatformAdapter::ConvertScaleformValue(&receiver, &args->args[0], vm); + BSFixedString functionName(args->args[1].GetString()); + + args->result->SetBool(false); + if(receiver.IsIdentifier()) + { + VMValue packedArgs; + UInt32 length = args->numArgs - 2; + VMValue::ArrayData * arrayData = nullptr; + vm->CreateArray(&packedArgs, length, &arrayData); + + packedArgs.type.value = VMValue::kType_VariableArray; + packedArgs.data.arr = arrayData; + + for(UInt32 i = 0; i < length; i++) + { + VMValue * var = new VMValue; + PlatformAdapter::ConvertScaleformValue(var, &args->args[i + 2], vm); + arrayData->arr.entries[i].SetVariable(var); + } + + VMIdentifier * identifier = receiver.data.id; + if(identifier) { + CallFunctionNoWait_Internal(vm, 0, identifier, &functionName, &packedArgs); + args->result->SetBool(true); + } + } + } +}; + +class F4SEScaleform_CallGlobalFunctionNoWait : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args * args) + { + ASSERT(args->numArgs >= 2); + ASSERT(args->args[0].GetType() == GFxValue::kType_String); + ASSERT(args->args[1].GetType() == GFxValue::kType_String); + + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + + BSFixedString className(args->args[0].GetString()); + BSFixedString functionName(args->args[1].GetString()); + + args->result->SetBool(false); + + VMValue packedArgs; + UInt32 length = args->numArgs - 2; + VMValue::ArrayData * arrayData = nullptr; + vm->CreateArray(&packedArgs, length, &arrayData); + + packedArgs.type.value = VMValue::kType_VariableArray; + packedArgs.data.arr = arrayData; + + for (UInt32 i = 0; i < length; i++) + { + VMValue * var = new VMValue; + PlatformAdapter::ConvertScaleformValue(var, &args->args[i + 2], vm); + arrayData->arr.entries[i].SetVariable(var); + } + + CallGlobalFunctionNoWait_Internal(vm, 0, 0, &className, &functionName, &packedArgs); + + args->result->SetBool(true); + } +}; + +class F4SEScaleform_GetDirectoryListing : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args * args) + { + ASSERT(args->numArgs >= 1); + ASSERT(args->args[0].GetType() == GFxValue::kType_String); + + const char * directory = args->args[0].GetString(); + const char * match = nullptr; + if(args->numArgs >= 2) + match = args->args[1].GetString(); + + args->movie->movieRoot->CreateArray(args->result); + + for(IDirectoryIterator iter(directory, match); !iter.Done(); iter.Next()) + { + std::string path = iter.GetFullPath(); + WIN32_FIND_DATA * fileData = iter.Get(); + + GFxValue fileInfo; + args->movie->movieRoot->CreateObject(&fileInfo); + + GFxValue filePath; + args->movie->movieRoot->CreateString(&filePath, path.c_str()); + fileInfo.SetMember("nativePath", &filePath); + + GFxValue fileName; + args->movie->movieRoot->CreateString(&fileName, fileData->cFileName); + fileInfo.SetMember("name", &filePath); + + SYSTEMTIME sysTime; + FileTimeToSystemTime(&fileData->ftLastWriteTime, &sysTime); + + GFxValue date; + GFxValue params[7]; + params[0].SetNumber(sysTime.wYear); + params[1].SetNumber(sysTime.wMonth - 1); // Flash Month is 0-11, System time is 1-12 + params[2].SetNumber(sysTime.wDay); + params[3].SetNumber(sysTime.wHour); + params[4].SetNumber(sysTime.wMinute); + params[5].SetNumber(sysTime.wSecond); + params[6].SetNumber(sysTime.wMilliseconds); + args->movie->movieRoot->CreateObject(&date, "Date", params, 7); + fileInfo.SetMember("lastModified", &date); + + FileTimeToSystemTime(&fileData->ftCreationTime, &sysTime); + params[0].SetNumber(sysTime.wYear); + params[1].SetNumber(sysTime.wMonth - 1); // Flash Month is 0-11, System time is 1-12 + params[2].SetNumber(sysTime.wDay); + params[3].SetNumber(sysTime.wHour); + params[4].SetNumber(sysTime.wMinute); + params[5].SetNumber(sysTime.wSecond); + params[6].SetNumber(sysTime.wMilliseconds); + args->movie->movieRoot->CreateObject(&date, "Date", params, 7); + fileInfo.SetMember("creationDate", &date); + + fileInfo.SetMember("isDirectory", &GFxValue((fileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)); + fileInfo.SetMember("isHidden", &GFxValue((fileData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN)); + args->result->PushBack(&fileInfo); + } + } +}; + +class F4SEScaleform_MountImage : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args * args) + { + ASSERT(args->numArgs >= 3); + ASSERT(args->args[0].GetType() == GFxValue::kType_String); + ASSERT(args->args[1].GetType() == GFxValue::kType_String); + ASSERT(args->args[2].GetType() == GFxValue::kType_String); + + const char * menuName = args->args[0].GetString(); + const char * filePath = args->args[1].GetString(); + const char * imageName = args->args[2].GetString(); + + NiTexture * texture = nullptr; + LoadTextureByPath(filePath, true, texture, 0, 0, 0); + + bool result = false; + if(texture) + { + BSReadAndWriteLocker locker(&s_mountedTexturesLock); + + auto & textures = s_mountedTextures[menuName]; + auto sit = textures.find(texture); + if(sit != textures.end()) + { + // Already mounted + result = true; + } + else + { + auto imageLoader = (*g_scaleformManager)->imageLoader; + if(imageLoader) + { + texture->name = imageName; + texture->IncRef(); + textures.insert(texture); + result = imageLoader->MountImage(&texture); + } + } + + // LoadTextureByPath increases refcount + texture->DecRef(); + } + + args->result->SetBool(result); + } +}; + +class F4SEScaleform_UnmountImage : public GFxFunctionHandler +{ +public: + virtual void Invoke(Args * args) + { + ASSERT(args->numArgs >= 2); + ASSERT(args->args[0].GetType() == GFxValue::kType_String); + ASSERT(args->args[1].GetType() == GFxValue::kType_String); + + const char * menuName = args->args[0].GetString(); + const char * filePath = args->args[1].GetString(); + + NiTexture * texture = nullptr; + LoadTextureByPath(filePath, true, texture, 0, 0, 0); + + bool result = false; + if(texture) + { + BSReadAndWriteLocker locker(&s_mountedTexturesLock); + auto it = s_mountedTextures.find(menuName); + if(it != s_mountedTextures.end()) + { + auto sit = it->second.find(texture); + if(sit != it->second.end()) + { + auto imageLoader = (*g_scaleformManager)->imageLoader; + if(imageLoader) + { + result = imageLoader->UnmountImage(&texture); + it->second.erase(texture); + texture->DecRef(); + } + } + + if(it->second.empty()) + { + s_mountedTextures.erase(it); + } + } + + // LoadTextureByPath increases refcount + texture->DecRef(); + } + + args->result->SetBool(result); + } +}; + +class F4SEOpenCloseHandler : public BSTEventSink +{ +public: + virtual ~F4SEOpenCloseHandler() { }; + virtual EventResult ReceiveEvent(MenuOpenCloseEvent * evn, void * dispatcher) override + { + // Unmount textures if the menu is being destroyed + if(!evn->isOpen) + { + BSReadAndWriteLocker locker(&s_mountedTexturesLock); + auto it = s_mountedTextures.find(evn->menuName.c_str()); + if(it != s_mountedTextures.end()) + { + auto imageLoader = (*g_scaleformManager)->imageLoader; + if(imageLoader) + { + for(auto & texture : it->second) + { + imageLoader->UnmountImage(&const_cast(texture)); + texture->DecRef(); + } + } + + it->second.clear(); + s_mountedTextures.erase(it); + } + } + + return kEvent_Continue; + }; +}; + +F4SEOpenCloseHandler g_menuOpenCloseHandler; + +void ScaleformInitHook_Install(GFxMovieView * view) +{ + GFxValue root; + GFxMovieRoot * movieRoot = view->movieRoot; + + bool result = movieRoot->GetVariable(&root, "root"); + if(!result) + { + _ERROR("couldn't get root"); + return; + } + + GFxValue f4se; + movieRoot->CreateObject(&f4se); + + RegisterFunction(&f4se, movieRoot, "GetMembers"); + RegisterFunction(&f4se, movieRoot, "AllowTextInput"); + RegisterFunction(&f4se, movieRoot, "SendExternalEvent"); + RegisterFunction(&f4se, movieRoot, "CallFunctionNoWait"); + RegisterFunction(&f4se, movieRoot, "CallGlobalFunctionNoWait"); + RegisterFunction(&f4se, movieRoot, "GetDirectoryListing"); + RegisterFunction(&f4se, movieRoot, "MountImage"); + RegisterFunction(&f4se, movieRoot, "UnmountImage"); + + GFxValue version; + movieRoot->CreateObject(&version); + version.SetMember("major", &GFxValue(F4SE_VERSION_INTEGER)); + version.SetMember("minor", &GFxValue(F4SE_VERSION_INTEGER_MINOR)); + version.SetMember("beta", &GFxValue(F4SE_VERSION_INTEGER_BETA)); + version.SetMember("releaseIdx", &GFxValue(F4SE_VERSION_RELEASEIDX)); + f4se.SetMember("version", &version); + + // plugins + GFxValue plugins; + movieRoot->CreateObject(&plugins); + + for(PluginList::iterator iter = s_plugins.begin(); iter != s_plugins.end(); ++iter) + { + GFxValue plugin; + movieRoot->CreateObject(&plugin); + + iter->callback(view, &plugin); + + plugins.SetMember(iter->name, &plugin); + } + + f4se.SetMember("plugins", &plugins); + + root.SetMember("f4se", &f4se); + + if(root.IsObject()) + { + GFxValue funcObj; + if(root.HasMember("onF4SEObjCreated") && root.GetMember("onF4SEObjCreated", &funcObj) && funcObj.IsFunction()) + { + root.Invoke("onF4SEObjCreated", nullptr, &f4se, 1); + } + + GFxValue child; + GFxValue numChildren; + if(root.GetMember("numChildren", &numChildren)) + { + for(SInt32 i = 0; i < numChildren.GetInt(); ++i) + { + GFxValue index(i); + root.Invoke("getChildAt", &child, &index, 1); + + if(child.HasMember("onF4SEObjCreated") && child.GetMember("onF4SEObjCreated", &funcObj) && funcObj.IsFunction()) + { + child.Invoke("onF4SEObjCreated", nullptr, &f4se, 1); + } + } + } + } + + GFxValue dispatchEvent; + GFxValue eventArgs[3]; + movieRoot->CreateString(&eventArgs[0], "F4SE::Initialized"); + eventArgs[1] = GFxValue(true); + eventArgs[2] = GFxValue(false); + movieRoot->CreateObject(&dispatchEvent, "flash.events.Event", eventArgs, 3); + movieRoot->Invoke("root.dispatchEvent", nullptr, &dispatchEvent, 1); +} + +BSScaleformManager * BSScaleformManager_Ctor_Hook(BSScaleformManager * mgr) +{ + BSScaleformManager * result = BSScaleformManager_Ctor_Original(mgr); + + BSScaleformTranslator * translator = (BSScaleformTranslator*)result->stateBag->GetStateAddRef(GFxState::kInterface_Translator); + if(translator) { + Translation::ImportTranslationFiles(translator); + } + + if(g_logScaleform) + { + GFxLogState * logger = (GFxLogState*)result->stateBag->GetStateAddRef(GFxState::kInterface_Log); + logger->logger = new F4SEGFxLogger(); + } + + if(*g_ui) + { + (*g_ui)->menuOpenCloseEventSource.AddEventSink(&g_menuOpenCloseHandler); + } + + return result; +} + +UInt32 BSScaleformTint_Hook(BSGFxShaderFXTarget * value, float * colors, float multiplier) +{ + if(value->HasMember("onApplyColorChange")) + { + GFxValue result; + GFxValue args[4]; + args[0] = GFxValue(colors[0]); + args[1] = GFxValue(colors[1]); + args[2] = GFxValue(colors[2]); + args[3] = GFxValue(multiplier); + value->Invoke("onApplyColorChange", &result, args, 4); + if(result.IsArray() && result.GetArraySize() >= 4) + { + result.GetElement(0, &args[0]); + result.GetElement(1, &args[1]); + result.GetElement(2, &args[2]); + result.GetElement(3, &args[3]); + + colors[0] = args[0].GetNumber(); + colors[1] = args[1].GetNumber(); + colors[2] = args[2].GetNumber(); + multiplier = args[3].GetNumber(); + } + } + + return BSScaleformTint_Original(value, colors, multiplier); +} + +void Hooks_Scaleform_Commit() +{ + UInt32 logScaleform = 0; + if(GetConfigOption_UInt32("Interface", "bEnableGFXLog", &logScaleform)) + { + if(logScaleform) + { + g_logScaleform = true; + } + } + + // hook creation of each menu + { + struct ScaleformInitHook_Code : Xbyak::CodeGenerator { + ScaleformInitHook_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + mov(eax, 1); + lock(); xadd(ptr [rcx + 8], eax); + jmp((void *)&ScaleformInitHook_Install); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + ScaleformInitHook_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + g_branchTrampoline.Write5Call(ScaleformInitHook_Start, uintptr_t(code.getCode())); + } + + // hook creation of BSScaleformManager + { + struct BSScaleformManager_Code : Xbyak::CodeGenerator { + BSScaleformManager_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + push(rbp); + push(rbx); + push(rsi); + push(rdi); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(BSScaleformManager_Ctor.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + BSScaleformManager_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + BSScaleformManager_Ctor_Original = (_BSScaleformManager_Ctor)codeBuf; + + g_branchTrampoline.Write5Branch(BSScaleformManager_Ctor.GetUIntPtr(), (uintptr_t)BSScaleformManager_Ctor_Hook); + } + + // hook global tinting of objects + { + struct BSScaleformTint_Code : Xbyak::CodeGenerator { + BSScaleformTint_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr[rsp+0x18], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(BSScaleformTint.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + BSScaleformTint_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + BSScaleformTint_Original = (_BSScaleformTint)codeBuf; + + g_branchTrampoline.Write5Branch(BSScaleformTint.GetUIntPtr(), (uintptr_t)BSScaleformTint_Hook); + } + + // Hook menu construction + { + struct MenuConstruction_Code : Xbyak::CodeGenerator { + MenuConstruction_Code(void * buf, uintptr_t originFuncAddr, uintptr_t funcAddr) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel, funcLabel1, funcLabel2; + + // Put the original call back + call(ptr [rip + funcLabel1]); + + // Pull the IMenu off the stack and call our new function + mov(rcx, ptr[rsp+0x388-0x348]); + call(ptr [rip + funcLabel2]); + + // Jump back to the original location + jmp(ptr [rip + retnLabel]); + + L(funcLabel1); + dq(originFuncAddr); + + L(funcLabel2); + dq(funcAddr); + + L(retnLabel); + dq(IMenuCreateHook_Start.GetUIntPtr() + 0x5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + MenuConstruction_Code code(codeBuf, SetMenuName.GetUIntPtr(), (uintptr_t)LoadCustomMenu_Hook); + g_localTrampoline.EndAlloc(code.getCurr()); + g_branchTrampoline.Write5Branch(IMenuCreateHook_Start.GetUIntPtr(), uintptr_t(code.getCode())); + } +} diff --git a/f4se/f4se/Hooks_Scaleform.h b/f4se/f4se/Hooks_Scaleform.h new file mode 100644 index 0000000..7354b58 --- /dev/null +++ b/f4se/f4se/Hooks_Scaleform.h @@ -0,0 +1,9 @@ +#pragma once + +#include "f4se/PluginAPI.h" + +void Hooks_Scaleform_Init(); +void Hooks_Scaleform_Commit(); + +bool RegisterScaleformPlugin(const char * name, F4SEScaleformInterface::RegisterCallback callback); +void Hooks_OpenCloseHandler(); \ No newline at end of file diff --git a/f4se/f4se/Hooks_Threads.cpp b/f4se/f4se/Hooks_Threads.cpp new file mode 100644 index 0000000..9a11154 --- /dev/null +++ b/f4se/f4se/Hooks_Threads.cpp @@ -0,0 +1,103 @@ +#include "f4se/Hooks_Threads.h" +#include "common/ICriticalSection.h" +#include "f4se/GameThreads.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/BranchTrampoline.h" +#include "xbyak/xbyak.h" + +#include + +ICriticalSection s_taskQueueLock; +std::queue s_tasks; + +ICriticalSection s_uiQueueLock; +std::queue s_uiQueue; + +typedef bool (* _MessageQueueProcessTask)(void * messageQueue, float timeout, UInt32 unk1); +RelocAddr <_MessageQueueProcessTask> MessageQueueProcessTask(0x00D57FA0); +_MessageQueueProcessTask MessageQueueProcessTask_Original = nullptr; + +RelocAddr ProcessEventQueue_HookTarget(0x02042430 + 0xE90); +typedef void (* _ProcessEventQueue_Internal)(void * unk1); +RelocAddr <_ProcessEventQueue_Internal> ProcessEventQueue_Internal(0x0211CF80); + +bool MessageQueueProcessTask_Hook(void * messageQueue, float timeout, UInt32 unk1) +{ + bool result = MessageQueueProcessTask_Original(messageQueue, timeout, unk1); + + s_taskQueueLock.Enter(); + while (!s_tasks.empty()) + { + ITaskDelegate * cmd = s_tasks.front(); + s_tasks.pop(); + cmd->Run(); + delete cmd; + } + s_taskQueueLock.Leave(); + + return result; +} + +void TaskInterface::AddTask(ITaskDelegate * task) +{ + s_taskQueueLock.Enter(); + s_tasks.push(task); + s_taskQueueLock.Leave(); +} + +void ProcessEventQueue_Hook(void * unk1) +{ + s_uiQueueLock.Enter(); + while (!s_uiQueue.empty()) + { + ITaskDelegate * cmd = s_uiQueue.front(); + s_uiQueue.pop(); + cmd->Run(); + delete cmd; + } + s_uiQueueLock.Leave(); + + ProcessEventQueue_Internal(unk1); +} + +void TaskInterface::AddUITask(ITaskDelegate * task) +{ + s_uiQueueLock.Enter(); + s_uiQueue.push(task); + s_uiQueueLock.Leave(); +} + +void Hooks_Threads_Init(void) +{ + +} + +void Hooks_Threads_Commit(void) +{ + // hook message queue to pump our own messages + { + struct MessageQueueProcessTask_Code : Xbyak::CodeGenerator { + MessageQueueProcessTask_Code(void * buf) : Xbyak::CodeGenerator(4096, buf) + { + Xbyak::Label retnLabel; + + mov(ptr[rsp+0x10], rbx); + + jmp(ptr [rip + retnLabel]); + + L(retnLabel); + dq(MessageQueueProcessTask.GetUIntPtr() + 5); + } + }; + + void * codeBuf = g_localTrampoline.StartAlloc(); + MessageQueueProcessTask_Code code(codeBuf); + g_localTrampoline.EndAlloc(code.getCurr()); + + MessageQueueProcessTask_Original = (_MessageQueueProcessTask)codeBuf; + + g_branchTrampoline.Write5Branch(MessageQueueProcessTask.GetUIntPtr(), (uintptr_t)MessageQueueProcessTask_Hook); + } + + g_branchTrampoline.Write5Call(ProcessEventQueue_HookTarget.GetUIntPtr(), (uintptr_t)ProcessEventQueue_Hook); +} diff --git a/f4se/f4se/Hooks_Threads.h b/f4se/f4se/Hooks_Threads.h new file mode 100644 index 0000000..0224b67 --- /dev/null +++ b/f4se/f4se/Hooks_Threads.h @@ -0,0 +1,12 @@ +#pragma once + +class ITaskDelegate; + +void Hooks_Threads_Init(void); +void Hooks_Threads_Commit(void); + +namespace TaskInterface +{ + void AddTask(ITaskDelegate * task); + void AddUITask(ITaskDelegate * task); +} diff --git a/f4se/f4se/InputMap.cpp b/f4se/f4se/InputMap.cpp new file mode 100644 index 0000000..9a85e1c --- /dev/null +++ b/f4se/f4se/InputMap.cpp @@ -0,0 +1,50 @@ +#include "InputMap.h" +#include + +UInt32 InputMap::GamepadMaskToKeycode(UInt32 keyMask) +{ + switch (keyMask) + { + case XINPUT_GAMEPAD_DPAD_UP: return kGamepadButtonOffset_DPAD_UP; + case XINPUT_GAMEPAD_DPAD_DOWN: return kGamepadButtonOffset_DPAD_DOWN; + case XINPUT_GAMEPAD_DPAD_LEFT: return kGamepadButtonOffset_DPAD_LEFT; + case XINPUT_GAMEPAD_DPAD_RIGHT: return kGamepadButtonOffset_DPAD_RIGHT; + case XINPUT_GAMEPAD_START: return kGamepadButtonOffset_START; + case XINPUT_GAMEPAD_BACK: return kGamepadButtonOffset_BACK; + case XINPUT_GAMEPAD_LEFT_THUMB: return kGamepadButtonOffset_LEFT_THUMB; + case XINPUT_GAMEPAD_RIGHT_THUMB: return kGamepadButtonOffset_RIGHT_THUMB; + case XINPUT_GAMEPAD_LEFT_SHOULDER: return kGamepadButtonOffset_LEFT_SHOULDER; + case XINPUT_GAMEPAD_RIGHT_SHOULDER: return kGamepadButtonOffset_RIGHT_SHOULDER; + case XINPUT_GAMEPAD_A: return kGamepadButtonOffset_A; + case XINPUT_GAMEPAD_B: return kGamepadButtonOffset_B; + case XINPUT_GAMEPAD_X: return kGamepadButtonOffset_X; + case XINPUT_GAMEPAD_Y: return kGamepadButtonOffset_Y; + case 0x9: return kGamepadButtonOffset_LT; + case 0xA: return kGamepadButtonOffset_RT; + default: return kMaxMacros; // Invalid + } +} + +UInt32 InputMap::GamepadKeycodeToMask(UInt32 keyCode) +{ + switch (keyCode) + { + case kGamepadButtonOffset_DPAD_UP: return XINPUT_GAMEPAD_DPAD_UP; + case kGamepadButtonOffset_DPAD_DOWN: return XINPUT_GAMEPAD_DPAD_DOWN; + case kGamepadButtonOffset_DPAD_LEFT: return XINPUT_GAMEPAD_DPAD_LEFT; + case kGamepadButtonOffset_DPAD_RIGHT: return XINPUT_GAMEPAD_DPAD_RIGHT; + case kGamepadButtonOffset_START: return XINPUT_GAMEPAD_START; + case kGamepadButtonOffset_BACK: return XINPUT_GAMEPAD_BACK; + case kGamepadButtonOffset_LEFT_THUMB: return XINPUT_GAMEPAD_LEFT_THUMB; + case kGamepadButtonOffset_RIGHT_THUMB: return XINPUT_GAMEPAD_RIGHT_THUMB; + case kGamepadButtonOffset_LEFT_SHOULDER: return XINPUT_GAMEPAD_LEFT_SHOULDER; + case kGamepadButtonOffset_RIGHT_SHOULDER: return XINPUT_GAMEPAD_RIGHT_SHOULDER; + case kGamepadButtonOffset_A: return XINPUT_GAMEPAD_A; + case kGamepadButtonOffset_B: return XINPUT_GAMEPAD_B; + case kGamepadButtonOffset_X: return XINPUT_GAMEPAD_X; + case kGamepadButtonOffset_Y: return XINPUT_GAMEPAD_Y; + case kGamepadButtonOffset_LT: return 0x9; + case kGamepadButtonOffset_RT: return 0xA; + default: return 0xFF; // Invalid + } +} diff --git a/f4se/f4se/InputMap.h b/f4se/f4se/InputMap.h new file mode 100644 index 0000000..ac8e20f --- /dev/null +++ b/f4se/f4se/InputMap.h @@ -0,0 +1,47 @@ +#pragma once + +namespace InputMap +{ + +enum +{ + // first 256 for keyboard, then 8 mouse buttons, then mouse wheel up, wheel down, then 16 gamepad buttons + kMacro_KeyboardOffset = 0, // not actually used, just for self-documentation + kMacro_NumKeyboardKeys = 256, + + kMacro_MouseButtonOffset = kMacro_NumKeyboardKeys, // 256 + kMacro_NumMouseButtons = 8, + + kMacro_MouseWheelOffset = kMacro_MouseButtonOffset + kMacro_NumMouseButtons, // 264 + kMacro_MouseWheelDirections = 2, + + kMacro_GamepadOffset = kMacro_MouseWheelOffset + kMacro_MouseWheelDirections, // 266 + kMacro_NumGamepadButtons = 16, + + kMaxMacros = kMacro_GamepadOffset + kMacro_NumGamepadButtons // 282 +}; + +enum +{ + kGamepadButtonOffset_DPAD_UP = kMacro_GamepadOffset, // 266 + kGamepadButtonOffset_DPAD_DOWN, + kGamepadButtonOffset_DPAD_LEFT, + kGamepadButtonOffset_DPAD_RIGHT, + kGamepadButtonOffset_START, + kGamepadButtonOffset_BACK, + kGamepadButtonOffset_LEFT_THUMB, + kGamepadButtonOffset_RIGHT_THUMB, + kGamepadButtonOffset_LEFT_SHOULDER, + kGamepadButtonOffset_RIGHT_SHOULDER, + kGamepadButtonOffset_A, + kGamepadButtonOffset_B, + kGamepadButtonOffset_X, + kGamepadButtonOffset_Y, + kGamepadButtonOffset_LT, + kGamepadButtonOffset_RT // 281 +}; + +UInt32 GamepadMaskToKeycode(UInt32 keyMask); +UInt32 GamepadKeycodeToMask(UInt32 keyCode); + +} diff --git a/f4se/f4se/InternalSerialization.cpp b/f4se/f4se/InternalSerialization.cpp new file mode 100644 index 0000000..d6dd367 --- /dev/null +++ b/f4se/f4se/InternalSerialization.cpp @@ -0,0 +1,326 @@ +#include +#include + +#include "common/IFileStream.h" + +#include "f4se/PluginAPI.h" +#include "f4se/GameData.h" +#include "f4se/InternalSerialization.h" +#include "f4se/Serialization.h" +#include "f4se/PapyrusEvents.h" +#include "f4se/PapyrusObjects.h" +#include "f4se/PapyrusDelayFunctors.h" + +#include "f4se/CustomMenu.h" + +// Internal + +static std::unordered_map s_savedModIndexMap; + +void LoadModList(const F4SESerializationInterface * intfc) +{ + _MESSAGE("Loading mod list:"); + + char name[0x104] = { 0 }; + UInt16 nameLen = 0; + + UInt8 numSavedMods = 0; + intfc->ReadRecordData(&numSavedMods, sizeof(numSavedMods)); + for (UInt32 i = 0; i < numSavedMods; i++) + { + intfc->ReadRecordData(&nameLen, sizeof(nameLen)); + intfc->ReadRecordData(&name, nameLen); + name[nameLen] = 0; + + const ModInfo * modInfo = (*g_dataHandler)->LookupModByName(name); + if (modInfo) { + UInt32 newIndex = modInfo->GetPartialIndex(); + s_savedModIndexMap[i] = newIndex; + _MESSAGE("\t(%d -> %d)\t%s", i, newIndex, &name); + } + else { + s_savedModIndexMap[i] = 0xFF; + } + } +} + +void LoadLightModList(const F4SESerializationInterface * intfc, bool fixedSize) +{ + _MESSAGE("Loading light mod list:"); + + char name[0x104] = { 0 }; + UInt16 nameLen = 0; + + UInt32 modCount = 0; + if(fixedSize) + { + UInt16 numMods = 0; + intfc->ReadRecordData(&numMods, sizeof(numMods)); + modCount = numMods; + } + else + { + UInt8 numMods = 0; + intfc->ReadRecordData(&numMods, sizeof(numMods)); + modCount = numMods; + } + + for (UInt32 i = 0; i < modCount; i++) + { + intfc->ReadRecordData(&nameLen, sizeof(nameLen)); + intfc->ReadRecordData(&name, nameLen); + name[nameLen] = 0; + + const ModInfo * modInfo = (*g_dataHandler)->LookupModByName(name); + if (modInfo) { + UInt32 newIndex = modInfo->GetPartialIndex(); + s_savedModIndexMap[0xFE000 | i] = newIndex; + _MESSAGE("\t(%d -> %d)\t%s", i & 0xFF, newIndex & 0xFFF, &name); + } + else { + s_savedModIndexMap[0xFE000 | i] = 0xFF; + } + } +} + +void SavePluginsList(const F4SESerializationInterface * intfc) +{ + DataHandler * dhand = (*g_dataHandler); + + struct IsActiveFunctor + { + bool Accept(ModInfo * modInfo) + { + return modInfo && modInfo->IsActive(); + } + }; + struct LoadedModVisitor + { + LoadedModVisitor(std::function func) : modInfoVisitor(func) { } + bool Accept(ModInfo * modInfo) + { + return modInfoVisitor(modInfo); + } + std::function modInfoVisitor; + }; + + UInt16 modCount = dhand->modList.modInfoList.CountIf(IsActiveFunctor()); + + intfc->OpenRecord('PLGN', 0); + intfc->WriteRecordData(&modCount, sizeof(modCount)); + + _MESSAGE("Saving plugin list:"); + + + dhand->modList.modInfoList.Visit(LoadedModVisitor([&](ModInfo* modInfo) + { + if (modInfo && modInfo->IsActive()) + { + intfc->WriteRecordData(&modInfo->modIndex, sizeof(modInfo->modIndex)); + if (modInfo->modIndex == 0xFE) { + intfc->WriteRecordData(&modInfo->lightIndex, sizeof(modInfo->lightIndex)); + } + + UInt16 nameLen = strlen(modInfo->name); + intfc->WriteRecordData(&nameLen, sizeof(nameLen)); + intfc->WriteRecordData(modInfo->name, nameLen); + if (modInfo->modIndex != 0xFE) + { + _MESSAGE("\t[%d]\t%s", modInfo->modIndex, &modInfo->name); + } + else + { + _MESSAGE("\t[FE:%d]\t%s", modInfo->lightIndex, &modInfo->name); + } + } + return true; // Continue + })); +} + +void LoadPluginList(const F4SESerializationInterface * intfc) +{ + DataHandler * dhand = (*g_dataHandler); + + _MESSAGE("Loading plugin list:"); + + char name[0x104] = { 0 }; + UInt16 nameLen = 0; + + UInt16 modCount = 0; + intfc->ReadRecordData(&modCount, sizeof(modCount)); + for (UInt32 i = 0; i < modCount; i++) + { + UInt8 modIndex = 0xFF; + UInt16 lightModIndex = 0xFFFF; + intfc->ReadRecordData(&modIndex, sizeof(modIndex)); + if (modIndex == 0xFE) { + intfc->ReadRecordData(&lightModIndex, sizeof(lightModIndex)); + } + + intfc->ReadRecordData(&nameLen, sizeof(nameLen)); + intfc->ReadRecordData(&name, nameLen); + name[nameLen] = 0; + + UInt32 newIndex = 0xFF; + UInt32 oldIndex = modIndex == 0xFE ? (0xFE000 | lightModIndex) : modIndex; + + const ModInfo * modInfo = dhand->LookupModByName(name); + if (modInfo) { + newIndex = modInfo->GetPartialIndex(); + } + + s_savedModIndexMap[oldIndex] = newIndex; + + _MESSAGE("\t(%d -> %d)\t%s", oldIndex, newIndex, name); + } +} + +UInt32 ResolveModIndex(UInt32 modIndex) +{ + auto it = s_savedModIndexMap.find(modIndex); + if (it != s_savedModIndexMap.end()) + { + return it->second; + } + + return 0xFF; +} + +//// Callbacks + +void Core_RevertCallback(const F4SESerializationInterface * intfc) +{ + g_inputKeyEventRegs.Clear(); + g_inputControlEventRegs.Clear(); + g_externalEventRegs.Clear(); + g_cameraEventRegs.Clear(); + g_furnitureEventRegs.Clear(); + + F4SEDelayFunctorManagerInstance().OnRevert(); + F4SEObjectStorageInstance().ClearAndRelease(); + + // Unregister all custom menus + g_customMenuLock.LockForReadAndWrite(); + for(auto & menuData : g_customMenuData) + { + BSFixedString menuName(menuData.first.c_str()); + if(!(*g_ui)->UnregisterMenu(menuName)) { + _DMESSAGE("Failed to unregister %s, instance still exists", menuName.c_str()); + } + } + g_customMenuData.clear(); + g_customMenuLock.Unlock(); +} + +void Core_SaveCallback(const F4SESerializationInterface * intfc) +{ + using namespace Serialization; + + SavePluginsList(intfc); + + _MESSAGE("Saving key input event registrations..."); + g_inputKeyEventRegs.Save(intfc, 'KEYR', InternalEventVersion::kCurrentVersion); + + _MESSAGE("Saving control input event registrations..."); + g_inputControlEventRegs.Save(intfc, 'CTLR', InternalEventVersion::kCurrentVersion); + + _MESSAGE("Saving external event registrations..."); + g_externalEventRegs.Save(intfc, 'EXEV', InternalEventVersion::kCurrentVersion); + + _MESSAGE("Saving camera event registrations..."); + g_cameraEventRegs.Save(intfc, 'CAMR', InternalEventVersion::kCurrentVersion); + + _MESSAGE("Saving furniture event registrations..."); + g_furnitureEventRegs.Save(intfc, 'FRNR', InternalEventVersion::kCurrentVersion); + + _MESSAGE("Saving SKSEPersistentObjectStorage data..."); + SaveClassHelper(intfc, 'OBMG', F4SEObjectStorageInstance()); + + _MESSAGE("Saving SKSEDelayFunctorManager data..."); + SaveClassHelper(intfc, 'DFMG', F4SEDelayFunctorManagerInstance()); +} + +void Core_LoadCallback(const F4SESerializationInterface * intfc) +{ + UInt32 type, version, length; + + while (intfc->GetNextRecordInfo(&type, &version, &length)) + { + switch (type) + { + // Plugins list + case 'PLGN': + LoadPluginList(intfc); + break; + + // Mod list (DEPRECATED) + case 'MODS': + LoadModList(intfc); + break; + + // Light Mod list (legacy) (DEPRECATED) + case 'LMOD': + LoadLightModList(intfc, false); + break; + + // Light mod list (16-bit size) (DEPRECATED) + case 'LIMD': + LoadLightModList(intfc, true); + break; + + // Key input events + case 'KEYR': + _MESSAGE("Loading key input event registrations..."); + g_inputKeyEventRegs.Load(intfc, InternalEventVersion::kCurrentVersion); + break; + + // Control input events + case 'CTLR': + _MESSAGE("Loading control input event registrations..."); + g_inputControlEventRegs.Load(intfc, InternalEventVersion::kCurrentVersion); + break; + + // External events + case 'EXEV': + _MESSAGE("Loading external event registrations..."); + g_externalEventRegs.Load(intfc, InternalEventVersion::kCurrentVersion); + break; + + // Camera events + case 'CAMR': + _MESSAGE("Loading camera event registrations..."); + g_cameraEventRegs.Load(intfc, InternalEventVersion::kCurrentVersion); + break; + + // Furniture events + case 'FRNR': + _MESSAGE("Loading furniture event registrations..."); + g_furnitureEventRegs.Load(intfc, InternalEventVersion::kCurrentVersion); + break; + + // SKSEPersistentObjectStorage + case 'OBMG': + _MESSAGE("Loading F4SEPersistentObjectStorage data..."); + F4SEObjectStorageInstance().Load(intfc, version); + break; + + // SKSEDelayFunctorManager + case 'DFMG': + _MESSAGE("Loading F4SEDelayFunctorManager data..."); + F4SEDelayFunctorManagerInstance().Load(intfc, version); + break; + + default: + _MESSAGE("Unhandled chunk type in Core_LoadCallback: %08X (%.4s)", type, &type); + continue; + } + } +} + +void Init_CoreSerialization_Callbacks() +{ + Serialization::SetUniqueID(0, 0); + Serialization::SetRevertCallback(0, Core_RevertCallback); + Serialization::SetSaveCallback(0, Core_SaveCallback); + Serialization::SetLoadCallback(0, Core_LoadCallback); +} diff --git a/f4se/f4se/InternalSerialization.h b/f4se/f4se/InternalSerialization.h new file mode 100644 index 0000000..e6f4c57 --- /dev/null +++ b/f4se/f4se/InternalSerialization.h @@ -0,0 +1,4 @@ +#pragma once + +UInt32 ResolveModIndex(UInt32 modIndex); +void Init_CoreSerialization_Callbacks(); diff --git a/f4se/f4se/NiCloningProcess.h b/f4se/f4se/NiCloningProcess.h new file mode 100644 index 0000000..a9c084d --- /dev/null +++ b/f4se/f4se/NiCloningProcess.h @@ -0,0 +1,19 @@ +#pragma once + +#include "f4se/NiObjects.h" +#include "f4se/NiTypes.h" + +// 68 +class NiCloningProcess +{ +public: + tHashSet unk00; // 00 + void * unk30; // 30 + UInt64 unk38; // 38 + UInt64 unk40; // 40 + void * unk48; // 48 - DEADBEEF + void * unk50; // 50 - bucket + UInt64 unk58; // 58 + UInt32 unk60; // 60 - copytype? 0, 1, 2 + UInt32 unk64; // 64 +}; diff --git a/f4se/f4se/NiExtraData.cpp b/f4se/f4se/NiExtraData.cpp new file mode 100644 index 0000000..b1bae81 --- /dev/null +++ b/f4se/f4se/NiExtraData.cpp @@ -0,0 +1,74 @@ +#include "f4se/NiExtraData.h" +#include "f4se/BSGeometry.h" + +// ??_7NiStringExtraData@@6B@ +RelocAddr s_NiStringExtraDataVtbl(0x02E15AA8); +// ??_7BSFaceGenBaseMorphExtraData@@6B@ +RelocAddr s_BSFaceGenBaseMorphExtraDataVtbl(0x02CEB188); +// ??_7BSDynPosData@@6B@ +RelocAddr s_BSDynPosDataVtbl(0x02E16D08); +// ??_7NiBinaryExtraData@@6B@ +RelocAddr s_NiBinaryExtraDataVtbl(0x02E19808); + +NiStringExtraData * NiStringExtraData::Create(const BSFixedString & name, const BSFixedString & string) +{ + void* memory = Heap_Allocate(sizeof(NiStringExtraData)); + memset(memory, 0, sizeof(NiStringExtraData)); + ((UInt64*)memory)[0] = s_NiStringExtraDataVtbl.GetUIntPtr(); + NiStringExtraData * data = (NiStringExtraData*)memory; + data->m_name = name; + data->m_string = string; + return data; +} + +BSDynPosData* BSDynPosData::Create(const BSFixedString & name, BSTriShape * shape) +{ + void* memory = Heap_Allocate(sizeof(BSDynPosData)); + memset(memory, 0, sizeof(BSDynPosData)); + ((UInt64*)memory)[0] = s_BSDynPosDataVtbl.GetUIntPtr(); + BSDynPosData * data = (BSDynPosData*)memory; + data->m_name = name; + + UInt16 vertexSize = shape->GetVertexSize(); + UInt32 dynamicBlock = sizeof(UInt16) * 4; + + data->vertexData = (UInt8*)Heap_Allocate(shape->numVertices * dynamicBlock); + for(UInt32 i = 0; i < shape->numVertices; i++) + { + memcpy_s(&data->vertexData[i * dynamicBlock], dynamicBlock, &shape->geometryData->vertexData->vertexBlock[i * vertexSize], dynamicBlock); + } + return data; +} + +BSFaceGenBaseMorphExtraData * BSFaceGenBaseMorphExtraData::Create(BSTriShape * shape) +{ + BSFaceGenBaseMorphExtraData * data = (BSFaceGenBaseMorphExtraData*)Heap_Allocate(sizeof(BSFaceGenBaseMorphExtraData)); + CALL_MEMBER_FN(data, ctor)(shape); + return data; +} + +BSFaceGenBaseMorphExtraData* BSFaceGenBaseMorphExtraData::Create(const BSFixedString & name, UInt32 vertexCount) +{ + void* memory = Heap_Allocate(sizeof(BSFaceGenBaseMorphExtraData)); + memset(memory, 0, sizeof(BSFaceGenBaseMorphExtraData)); + ((UInt64*)memory)[0] = s_BSFaceGenBaseMorphExtraDataVtbl.GetUIntPtr(); + BSFaceGenBaseMorphExtraData * data = (BSFaceGenBaseMorphExtraData*)memory; + data->m_name = name; + data->modelVertexCount = vertexCount; + data->vertexCount = vertexCount; + data->vertexData = (NiPoint3*)Heap_Allocate(sizeof(NiPoint3) * vertexCount); + memset(data->vertexData, 0, sizeof(NiPoint3) * vertexCount); + return data; +} + +NiBinaryExtraData* NiBinaryExtraData::Create(const BSFixedString & name, UInt32 size) +{ + void* memory = Heap_Allocate(sizeof(NiBinaryExtraData)); + memset(memory, 0, sizeof(NiBinaryExtraData)); + ((UInt64*)memory)[0] = s_NiBinaryExtraDataVtbl.GetUIntPtr(); + NiBinaryExtraData * data = (NiBinaryExtraData*)memory; + data->m_name = name; + data->binaryData = (UInt8*)Heap_Allocate(size); + data->size = size; + return data; +} diff --git a/f4se/f4se/NiExtraData.h b/f4se/f4se/NiExtraData.h new file mode 100644 index 0000000..fccbca2 --- /dev/null +++ b/f4se/f4se/NiExtraData.h @@ -0,0 +1,141 @@ +#pragma once + +#include "f4se/NiRTTI.h" +#include "f4se/NiObjects.h" + +class BSGeometrySegmentData; + +// 18 +class NiExtraData : public NiObject +{ +public: + virtual ~NiExtraData() { }; + + virtual NiRTTI * GetRTTI(void) override { return NIRTTI_NiExtraData; }; + + virtual void LoadBinary(void * stream) override { CALL_MEMBER_FN(this, Internal_LoadBinary)(stream); }; + virtual void SaveBinary(void * stream) override { CALL_MEMBER_FN(this, Internal_SaveBinary)(stream); }; + + virtual bool IsEqual(NiObject * object) override + { + bool equal = __super::IsEqual(object); + if(equal) + return m_name == static_cast(object)->m_name; + } + + virtual bool Unk_28() { return false; }; + virtual bool Unk_29() { return false; }; + virtual bool Unk_2A() { return true; }; + virtual bool Unk_2B() { return true; }; + + BSFixedString m_name; // 10 + + MEMBER_FN_PREFIX(NiObject); + // 834C1A014705796BD4E62721AE5EFA5F72AE14DB+15 + DEFINE_MEMBER_FN(Internal_LoadBinary, void, 0x01B96400, void * stream); + // 99E296799A58456B4597D53A5954CD9AFFA441FB+1A + DEFINE_MEMBER_FN(Internal_SaveBinary, void, 0x01B964A0, void * stream); +}; + +// 20 +class NiStringExtraData : public NiExtraData +{ +public: + virtual ~NiStringExtraData(); + + BSFixedString m_string; // 18 + + static NiStringExtraData * Create(const BSFixedString & name, const BSFixedString & string); +}; +extern RelocAddr s_NiStringExtraDataVtbl; + +// 28 +class NiBinaryExtraData : public NiExtraData +{ +public: + UInt8 * binaryData; // 18 + UInt32 size; // 20 + + static NiBinaryExtraData* Create(const BSFixedString & name, UInt32 size); +}; + +// 20 (DynPosData) +class BSDynPosData : public NiExtraData +{ +public: + UInt8 * vertexData; // 18 + + static BSDynPosData* Create(const BSFixedString & name, BSTriShape * shape); +}; + +// 28 (FOD) +class BSFaceGenBaseMorphExtraData : public NiExtraData +{ +public: + NiPoint3 * vertexData; // 18 + UInt32 modelVertexCount; // 20 + UInt32 vertexCount; // 24 - 0 if owning type isnt BSDynamicTriShape + + static BSFaceGenBaseMorphExtraData* Create(BSTriShape * shape); + static BSFaceGenBaseMorphExtraData* Create(const BSFixedString & name, UInt32 vertexCount); + +protected: + // Fills the vertex data from the original object's vertex data in NiPoint3 layout + MEMBER_FN_PREFIX(BSFaceGenBaseMorphExtraData); + DEFINE_MEMBER_FN(ctor, BSFaceGenBaseMorphExtraData*, 0x00684440, BSTriShape * shape); +}; + +extern RelocAddr s_BSFaceGenBaseMorphExtraDataVtbl; + + +class BSConnectPoint +{ +public: + // 30 + class Parents : public NiExtraData + { + public: + struct ConnectPoint + { + UInt64 unk00; // 00 - refcount? + BSFixedString parent; // 08 + BSFixedString name; // 10 + NiQuaternion rot; // 18 + NiPoint3 pos; // 28 + float scale; // 34 + }; + tArray points; // 18 + }; +}; + +// 198 +class BSDismembermentExtraData : public NiExtraData +{ +public: + tArray segments[16]; // 18 +}; + +STATIC_ASSERT(sizeof(BSDismembermentExtraData) == 0x198); + +// 20 +class NiIntegerExtraData : public NiExtraData +{ +public: + UInt32 value; // 18 + UInt32 pad1C; // 1C +}; + +// 20 +class BSDismembermentLimbExtraData : public NiExtraData +{ +public: + UInt32 dismemberIndex; // 18 - Links to which index of the 0-15 array in BSDismemberExtraData this shape is on + UInt32 unk1C; // 1C +}; + +// 20 +class BSSegmentDataStorage : public NiExtraData +{ +public: + BSGeometrySegmentData * segmentData; // 18 +}; diff --git a/f4se/f4se/NiMaterials.cpp b/f4se/f4se/NiMaterials.cpp new file mode 100644 index 0000000..736ef74 --- /dev/null +++ b/f4se/f4se/NiMaterials.cpp @@ -0,0 +1,6 @@ +#include "f4se/NiMaterials.h" + +// 84348FDD81710405CBC1A9C84103811EF7BF8394+3E +RelocAddr <_CreateShaderMaterialByType> CreateShaderMaterialByType(0x02826CB0); + +RelocAddr <_LoadMaterialFile> LoadMaterialFile(0x01C9D170); diff --git a/f4se/f4se/NiMaterials.h b/f4se/f4se/NiMaterials.h new file mode 100644 index 0000000..eab46fb --- /dev/null +++ b/f4se/f4se/NiMaterials.h @@ -0,0 +1,525 @@ +#pragma once + +#include "f4se/GameTypes.h" +#include "f4se/NiTypes.h" +#include "f4se/NiTextures.h" +#include "f4se/NiProperties.h" + +class NiTexture; +class BSTextureSet; +class NiStream; +class BSShaderData; +class BSTriShape; + +class BSLightingShaderPropertyFloatController; +class BSLightingShaderPropertyUShortController; +class BSLightingShaderPropertyColorController; +class BSEffectShaderPropertyFloatController; +class BSEffectShaderPropertyColorController; +struct BSNiAlphaPropertyTestRefController; + +// 38 +class BSShaderMaterial +{ +public: + virtual ~BSShaderMaterial(); + + virtual BSShaderMaterial * Create(); + virtual void CopyMembers(const BSShaderMaterial * other); + virtual void ComputeCRC32(UInt32, bool); + virtual BSShaderMaterial * GetDefault(); + virtual UInt32 GetType(); + virtual UInt32 GetFeature(); // 2 for Lighting shaders, 1 for effect shaders + virtual void Compare(const BSShaderMaterial & other); + virtual void IsCopy(const BSShaderMaterial * other); + + volatile UInt32 m_refCount; // 08 + NiPoint2 textCoordOffset[2]; // 0C + NiPoint2 textCoordScale[2]; // 1C + SInt32 unk2C; // 2C + UInt32 uiHashKey; // 30 + UInt32 uiUniqueCode; // 34 + + void SetOffsetUV(float u, float v) + { + textCoordOffset[0].x = u; + textCoordOffset[1].x = u; + textCoordOffset[0].y = v; + textCoordOffset[1].y = v; + } + + void GetOffsetUV(float * u, float * v) + { + *u = textCoordOffset[0].x; + *v = textCoordOffset[0].y; + } + + void SetScaleUV(float u, float v) + { + textCoordScale[0].x = u; + textCoordScale[1].x = u; + textCoordScale[0].y = v; + textCoordScale[1].y = v; + } + + void GetScaleUV(float * u, float * v) + { + *u = textCoordScale[0].x; + *v = textCoordScale[0].y; + } +}; + +// B8 +class BSEffectShaderMaterial : public BSShaderMaterial +{ +public: + virtual ~BSEffectShaderMaterial(); + + float fFalloffStartAngle; // 38 + float fFalloffStopAngle; // 3C + float fFalloffStartOpacity; // 40 + float fFalloffStopOpacity; // 44 + NiColorA kBaseColor; // 48 - Alpha is Material Alpha + NiPointer spBaseTexture; // 58 + NiPointer spGrayscaleTexture; // 60 + NiPointer spEnvironmentMapTexture; // 68 + NiPointer spEnvironmentMapMaskTexture; // 70 + NiPointer spNormalTexture; // 78 + float fSoftDepth; // 80 + float fBaseColorScale; // 84 + BSFixedString strBaseTexture; // 88 + BSFixedString strGrayscaleTexture; // 90 + BSFixedString strEnvironmentMapTexture; // 98 + BSFixedString strEnvironmentMapMaskTexture; // A0 + BSFixedString strNormalTexture; // A8 + union + { + float fEnvironmentMaskScale; // B0 + float fRefractionPower; // B0 - Needs kShaderFlags_Refraction + }; + UInt8 uTextureClampMode; // B4 + UInt8 uLightingInfluence; // B5 - divided by 255 + UInt8 uEnvironmentMapMinLOD; // B6 + UInt8 unkB7; // B7 +}; +STATIC_ASSERT(sizeof(BSEffectShaderMaterial) == 0xB8); + +// 168 +class BSWaterShaderMaterial : public BSShaderMaterial +{ +public: + NiPointer spStaticReflectionMap; // 38 + NiPointer spNormalMap01; // 40 + NiPointer spNormalMap02; // 48 + NiPointer spNormalMap03; // 50 + NiColorA kShallowColor; // 58 + NiColorA kDeepColor; // 68 + NiColorA kReflectionColor; // 78 + NiColorA kUnderwaterFogColor; // 88 + NiColorA kLightSiltColor; // 98 + NiColorA kDarkSiltColor; // A8 + NiColorA kVarAmounts; // B8 + NiColorA kWaterParams; // C8 + NiColorA kWaterParams2; // D8 + NiColorA kWaterParams3; // E8 + NiColorA kWaterParams4; // F8 + NiColorA kNormalsScroll1; // 108 + NiColorA kNormalsScroll2; // 118 + NiColorA kNormalsScale; // 128 + NiColorA kNormalsAmplitude; // 138 + NiPlane kReflectPlane; // 148 + SInt32 uwTexOffsetX; // 158 + SInt32 uwTexOffsetY; // 15C + bool bUpdateConstants; // 160 + bool bUseDefaultReflectionTexture; // 161 + bool bUseSSR; // 162 +}; +STATIC_ASSERT(sizeof(BSWaterShaderMaterial) == 0x168); + +// C0 +class BSLightingShaderMaterialBase : public BSShaderMaterial +{ +public: + enum ShaderTypes + { + kType_Default = 0, + kType_Envmap, + kType_Glowmap, + kType_Parallax, + kType_Face, + kType_SkinTint, + kType_HairTint, + kType_ParallaxOcc, + kType_Landscape, + kType_LODLandscape, + kType_Snow, + kType_MultiLayerParallax, + kType_TreeAnim, + kType_LODObjects, + kType_MultiIndexSnow, + kType_LODObjectsHD, + kType_Eye, + kType_Cloud, + kType_LODLandscapeNoise, + kType_LODLandscapeBlend, + kType_Dismemberment + }; + + virtual bool IsLightingShaderMaterialEnvmap(); + virtual void ClearTextures(); + virtual void GetTextures(NiTexture ** textures); // Returns textures into this array + virtual void SaveBinary(NiStream * stream); + virtual void LoadBinary(NiStream * stream); + virtual void OnPrefetchTextures(); + virtual void OnLoadTextureSet1(); + virtual void OnLoadTextureSet(const BSTextureSet *); + virtual void DoReceiveValuesFromRootMaterial(const BSShaderData &); + + NiColor kSpecularColor; // 38 + NiPointer spDiffuseTexture; // 48 + NiPointer spNormalTexture; // 50 + NiPointer spRimSoftLightingTexture; // 58 + NiPointer spSmoothnessSpecMaskTexture; // 60 + NiPointer spLookupTexture; // 68 + UInt32 eTextureClampMode; // 70 + UInt32 unk74; // 74 + NiPointer spTextureSet; // 78 + float fMaterialAlpha; // 80 + float fRefractionPower; // 84 + float fSmoothness; // 88 + float fSpecularColorScale; // 8C + float fFresnelPower; // 90 + float fWetnessControl_SpecScale; // 94 + float fWetnessControl_SpecPowerScale; // 98 + float fWetnessControl_SpecMin; // 9C + float fWetnessControl_EnvMapScale; // A0 + float fWetnessControl_FresnelPower; // A4 + float fWetnessControl_Metalness; // A8 + float fSubsurfaceLightRolloff; // AC + float fRimLightPower; // B0 + float fBackLightPower; // B4 + float fLookupScale; // B8 + BSNonReentrantSpinLock LoadTextureSetLock; // BC + + MEMBER_FN_PREFIX(BSLightingShaderMaterialBase); + DEFINE_MEMBER_FN(Copy, void, 0x02825C70, const BSLightingShaderMaterialBase * src); +}; + +// D0 +class BSLightingShaderMaterial : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterial(); +}; + +// C8 +class BSLightingShaderMaterialDismemberment : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialDismemberment(); + + NiPointer spDisplacementTexture; // C0 +}; + +// D8 +class BSLightingShaderMaterialEnvmap : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialEnvmap(); + + NiPointer spEnvTexture; // C0 + NiPointer spEnvMaskTexture; // C8 + float fEnvmapScale; // D0 + bool bUseScreenSpaceReflections; // D4 + bool bWetnessControl_UseScreenSpaceReflections; // D5 +}; + +// 110 +class BSLightingShaderMaterialEye : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialEye(); + + NiPointer spEnvTexture; // E0 + NiPointer spEnvMaskTexture; // E8 + NiPoint3 kEyeCenter[2]; // F0 + float fEnvmapScale; // 108 +}; + +// C8 +class BSLightingShaderMaterialFace : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialFace(); + + NiPointer baseDiffuse; // C0 +}; + +// C8 +class BSLightingShaderMaterialGlowmap : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialGlowmap(); + + NiPointer spGlowMapTexture; // C0 +}; + +// D0 +class BSLightingShaderMaterialHairTint : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialHairTint(); + + NiColorA kTintColor; // C0 +}; + +// 110 +class BSLightingShaderMaterialLODLandscape : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialLODLandscape(); + + NiPointer spParentDiffuseTexture; // E0 + NiPointer spParentNormalTexture; // E8 + NiPointer spLandscapeNoiseTexture; // F0 + NiPointer spLandscapeDetailNormalTexture; // F8 + float fTerrainTexOffsetX; // 100 + float fTerrainTexOffsetY; // 104 + float fTerrainTexFade; // 108 +}; + +// 1B0 +class BSLightingShaderMaterialLandscape : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialLandscape(); + + bool bLandscape_WetnessControl_UseScreenSpaceReflections[3]; // E0 + float fLandscape_WetnessControl_SpecScale[3]; // E4 + float fLandscape_WetnessControl_SpecPowerScale[3]; // F0 + float fLandscape_WetnessControl_SpecMin[3]; // FC + float fLandscape_WetnessControl_EnvMapScale[3]; // 108 + float fLandscape_WetnessControl_FresnelPower[3]; // 114 + float fLandscape_WetnessControl_Metalness[3]; // 120 + UInt32 uiNumLandscapeTextures; // 12C + NiPointer spLandscapeDiffuseTexture[3]; // 130 + NiPointer spLandscapeNormalTexture[3]; // 148 + NiPointer spLandscapeSmoothSpecTexture[3]; // 160 + NiPointer spLandscapeLODBlendTexture; // 178 + NiPointer spLandscapeNoiseTexture; // 180 + NiPointer spLandscapeDetailNormalTexture; // 188 + NiColorA kLandBlendParams; // 190 + float fTerrainTexOffsetX; // 1A0 + float fTerrainTexOffsetY; // 1A4 + float fTerrainTexFade; // 1A8 +}; + +// F0 +class BSLightingShaderMaterialMultiLayerParallax : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialMultiLayerParallax(); + + NiPointer spLayerTexture; // C0 + NiPointer spEnvTexture; // C8 + NiPointer spEnvMaskTexture; // D0 + float fParallaxLayerThickness; // D8 + float fParallaxRefractionScale; // DC + float fParallaxInnerLayerUScale; // E0 + float fParallaxInnerLayerVScale; // E4 + float fEnvmapScale; // E8 +}; + +// C8 +class BSLightingShaderMaterialParallax : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialParallax(); + + NiPointer spHeightTexture; // C0 +}; + +// D0 +class BSLightingShaderMaterialParallaxOcc : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialParallaxOcc(); + + NiPointer spHeightTexture; // C0 + float fParallaxOccMaxPasses; // C8 + float fParallaxOccScale; // CC +}; + +// D0 +class BSLightingShaderMaterialSkinTint : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialSkinTint(); + + NiColorA kTintColor; // C0 +}; + +// D0 +class BSLightingShaderMaterialSnow : public BSLightingShaderMaterialBase +{ +public: + virtual ~BSLightingShaderMaterialSnow(); + + float unkC0; // C0 + float unkC4; // C4 + float unkC8; // C8 + float unkCC; // CC +}; + +typedef BSShaderMaterial * (* _CreateShaderMaterialByType)(UInt32 type); +extern RelocAddr <_CreateShaderMaterialByType> CreateShaderMaterialByType; + +// 1E0 +class BSShaderData : public BSIntrusiveRefCounted +{ +public: + BSShaderData() + { + CALL_MEMBER_FN(this, ctor)(); + } + ~BSShaderData() + { + CALL_MEMBER_FN(this, dtor)(); + } + + UInt64 unk08[(0x1E0 - 0x08) >> 3]; + + // Pulled from Export PDB + /*enum LightingShaderEnum + { + kStandard = 0, + kWater, + kEffect, + kSky, + kTallGrass, + kCount, + }; + + LightingShaderEnum eLightingShader; + bool bSpecularEnabled; + NiColor SpecularColor; + float fSpecularMult; + NiColor EmittanceColor; + float fEmittanceColorScale; + float fLightingInfluence; + char ucEnvmapMinLOD; + float fSmoothness; + float fFresnelPower_ShaderDataEntry; + float fWetnessControl_SpecScale_ShaderDataEntry; + float fWetnessControl_SpecPowerScale_ShaderDataEntry; + float fWetnessControl_SpecMin_ShaderDataEntry; + float fWetnessControl_EnvMapScale_ShaderDataEntry; + float fWetnessControl_FresnelPower_ShaderDataEntry; + float fWetnessControl_Metalness_ShaderDataEntry; + bool bWetnessControl_ScreenSpaceReflections; + BSFixedString rootMaterialPath; + float fAlpha; + float fEmittanceMult; + float fLookupScale; + bool bEmitEnabled; + BSFixedString materialPath; + bool bAlphaBlend; + NiAlphaProperty::AlphaFunction eAlphaSrcBlendMode; + NiAlphaProperty::AlphaFunction eAlphaDestBlendMode; + bool bAlphaTest; + NiAlphaProperty::TestFunction eAlphaTestFunction; + char ucAlphaTestRef; + bool bEnableEditorAlphaRef; + bool bVertexColors; + bool bVertexAlpha; + bool bModelSpaceNormals; + bool bDecal; + bool bExternalEmittance; + bool bHideSecret; + bool bNoFade; + bool bDismemberment; + bool bFacegen; + bool SkinTint; + bool bHair; + bool bTwoSided; + bool bTree; + bool bBloodEnabled; + bool bUniqueAlphaProperty; + bool bEffectLightingEnabled; + bool bCastShadows; + bool bReceiveShadows; + bool bDissolveFade; + bool bGlowmap; + bool bAssumeShadowmask; + bool bScreenSpaceReflections; + bool bNonOccluder; + bool bSkewSpecularAlpha; + bool Tessellate; + bool bBackLighting; + bool bSubSurfaceLighting; + bool bRimLighting; + float fSubSurfaceRolloff; + float fRimPower; + float fBackLightPower; + bool bAnisoLighting; + bool bZBufferWrite; + bool bZBufferTest; + bool bRefraction; + float fRefractionPower; + bool bRefractionFalloff; + tArray pLightingFloatControllerA; + tArray pLightingUShortControllerA; + tArray pLightingColorControllerA; + tArray pEffectFloatControllerA; + tArray pEffectColorControllerA; + BSNiAlphaPropertyTestRefController* pAlphaRefTestController; + bool bParallax; + bool bParallaxOcclusion; + bool bMultiLayerParallax; + float fParallaxOcclusionHeightScale; + float fParallaxOcclusionMaxPasses; + float fParallaxLayerThickness; + float fParallaxRefractionScale; + float fParallaxInnerLayerUScale; + float fParallaxInnerLayerVScale; + float fDisplacementTextureBias; + float fDisplacementTextureScale; + float fTessellationPNScale; + float fTessellationFadeDistance; + float fTessellationBaseFactor; + bool bTessellationNeedsDominantUVs; + bool bTessellationNeedsCrackFreeNormals; + bool bEnvironmentMapping; + bool bEnvironmentMappingEye; + bool bEnvironmentMappingWindow; + float fEnvironmentMappingMaskScale; + int iSkyType; + bool bReflectionsEnabled; + bool bSilhouetteReflectionsEnabled; + bool bRefractionsEnabled; + bool bDepthEnabled; + bool bVertexUVEnabled; + bool bVertexAlphaDepthEnabled; + bool bFoggingEnabled; + bool bFalloffEnabled; + bool bRGBFalloffEnabled; + NiColorA FalloffData; + bool bSoftEnabled; + float fSoftDepth; + NiPoint2 UVOffset; + NiPoint2 UVScale; + bool bGrayscaleToPaletteColor; + bool bGrayscaleToPaletteAlpha; + NiColor HairTintColor; + NiPointer spTextureSet; + UInt32 eClampMode;*/ + + MEMBER_FN_PREFIX(BSShaderData); + DEFINE_MEMBER_FN(ctor, void, 0x027BD630); + DEFINE_MEMBER_FN(dtor, void, 0x003901F0); + DEFINE_MEMBER_FN(ApplyMaterialData, void, 0x027C4550, BSGeometry * trishape, bool unk1); +}; +STATIC_ASSERT(sizeof(BSShaderData) == 0x1E0); + +typedef UInt32 (* _LoadMaterialFile)(const char * filePath, BSShaderData * shaderData, UInt32 unk1); +extern RelocAddr <_LoadMaterialFile> LoadMaterialFile; diff --git a/f4se/f4se/NiNodes.cpp b/f4se/f4se/NiNodes.cpp new file mode 100644 index 0000000..4394a2d --- /dev/null +++ b/f4se/f4se/NiNodes.cpp @@ -0,0 +1,8 @@ +#include "f4se/NiNodes.h" + +NiNode * NiNode::Create(UInt16 children) +{ + NiNode * node = (NiNode*)Heap_Allocate(sizeof(NiNode)); + CALL_MEMBER_FN(node, ctor)(children); + return node; +} diff --git a/f4se/f4se/NiNodes.h b/f4se/f4se/NiNodes.h new file mode 100644 index 0000000..760c9d3 --- /dev/null +++ b/f4se/f4se/NiNodes.h @@ -0,0 +1,78 @@ +#pragma once + +#include "f4se/NiObjects.h" +#include "f4se/BSLight.h" + +// 140 +class NiNode : public NiAVObject +{ +public: + virtual void Unk_39(void); + virtual void AttachChild(NiAVObject * obj, bool firstAvail); + virtual void InsertChildAt(UInt32 index, NiAVObject * obj); + virtual void DetachChild(NiAVObject * obj, NiPointer & out); + virtual void RemoveChild(NiAVObject * obj); + virtual void DetachChildAt(UInt32 i, NiPointer & out); + virtual void RemoveChildAt(UInt32 i); + virtual void SetAt(UInt32 index, NiAVObject * obj, NiPointer & replaced); + virtual void SetAt(UInt32 index, NiAVObject * obj); + virtual void Unk_42(void); + + NiTArray m_children; // 120 + float unk138; + float unk13C; + + static NiNode * Create(UInt16 children = 0); + + MEMBER_FN_PREFIX(NiNode); + DEFINE_MEMBER_FN(ctor, NiNode*, 0x01B98920, UInt16 children); +}; +STATIC_ASSERT(sizeof(NiNode) == 0x140); + +// 1C0 +class BSFadeNode : public NiNode +{ +public: + virtual ~BSFadeNode(); + + struct FlattenedGeometryData + { + NiBound kBound; // 00 + NiPointer spGeometry; // 10 + UInt32 uiFlags; // 18 + }; + + BSShaderPropertyLightData kLightData; // 140 + tArray kGeomArray; // 168 + tArray MergedGeomBoundsA; // 180 + float fNearDistSqr; // 198 + float fFarDistSqr; // 19C + float fCurrentFade; // 1A0 + float fCurrentDecalFade; // 1A4 + float fBoundRadius; // 1A8 + float fTimeSinceUpdate; // 1AC + SInt32 iFrameCounter; // 1B0 + float fPreviousMaxA; // 1B4 + float fCurrentShaderLODLevel; // 1B8 + float fPreviousShaderLODLevel; // 1BC +}; + +// 190 +class BSFaceGenNiNode : public NiNode +{ +public: + UInt64 unk140[(0x178 - 0x140) >> 3]; // 140 + UInt32 unk178; // 178 + + // 1FA2A8F9E63D0F771FC6A9BAB875E88A9215810B + enum + { + kFlag_UpdateModel = (1 << 0), + kFlag_Unk1 = (1 << 7), + kFlag_Unk2 = (1 << 8) + }; + + UInt32 flags; // 17C + UInt64 unk180; // 180 + UInt64 unk188; // 188 +}; diff --git a/f4se/f4se/NiObjects.cpp b/f4se/f4se/NiObjects.cpp new file mode 100644 index 0000000..d4eb61f --- /dev/null +++ b/f4se/f4se/NiObjects.cpp @@ -0,0 +1,41 @@ +#include "f4se/NiObjects.h" +#include "f4se/NiExtraData.h" + +RelocAddr <_WorldToScreen> WorldToScreen_Internal(0x00AE2840); + +void NiRefObject::IncRef(void) +{ + InterlockedIncrement(&m_uiRefCount); +} + +bool NiRefObject::Release(void) +{ + return InterlockedDecrement(&m_uiRefCount) == 0; +} + +void NiRefObject::DecRef(void) +{ + if(Release()) + DeleteThis(); +} + +NiExtraData * NiObjectNET::GetExtraData(const BSFixedString & name) +{ + if(!m_extraData) + return false; + + SimpleLocker locker(&m_extraData->lock); + for(UInt32 i = 0; i < m_extraData->count; i++) + { + NiPointer data = m_extraData->entries[i]; + if(data && data->m_name == name) + return data; + } + + return nullptr; +} + +bool NiObjectNET::AddExtraData(NiExtraData * extraData) +{ + return CALL_MEMBER_FN(this, Internal_AddExtraData)(extraData); +} diff --git a/f4se/f4se/NiObjects.h b/f4se/f4se/NiObjects.h new file mode 100644 index 0000000..d214bef --- /dev/null +++ b/f4se/f4se/NiObjects.h @@ -0,0 +1,224 @@ +#pragma once + +#include "f4se/GameTypes.h" +#include "f4se/NiTypes.h" + +class NiRTTI; +class NiExtraData; +class NiNode; +class NiSwitchNode; +class TESObjectREFR; +class NiGeometry; +class BSGeometry; +class BSTriShape; +class BSDynamicTriShape; +class BSSubIndexTriShape; +class NiCloningProcess; +class NiCollisionObject; +class bhkNiCollisionObject; +class bhkBlendCollisionObject; +class bhkNPCollisionObject; +class bhkRigidBody; +class bhkLimitedHingeConstraint; + +typedef void (* _WorldToScreen)(NiPoint3 * in, NiPoint3 * out); +extern RelocAddr <_WorldToScreen> WorldToScreen_Internal; + +// 10 +class NiRefObject +{ +public: + NiRefObject() : m_uiRefCount(0) { }; + virtual ~NiRefObject() { }; + + virtual void DeleteThis(void) { delete this; }; // calls virtual dtor + + void IncRef(void); + void DecRef(void); + bool Release(void); + +// void ** _vtbl; // 00 + volatile SInt32 m_uiRefCount; // 08 +}; + +// 10 +class NiObject : public NiRefObject +{ +public: + virtual NiRTTI * GetRTTI(void) { return nullptr; }; + virtual NiNode * GetAsNiNode(void) { return nullptr; }; + virtual NiSwitchNode * GetAsNiSwitchNode(void) { return nullptr; }; + virtual void * Unk_05() { return nullptr; }; + virtual void * Unk_06() { return nullptr; }; + virtual void * Unk_07() { return nullptr; }; + virtual BSGeometry * GetAsBSGeometry(void) { return nullptr; }; + virtual void * GetAsBStriStrips() { return nullptr; }; + virtual BSTriShape * GetAsBSTriShape(void) { return nullptr; }; + virtual BSDynamicTriShape * GetAsBSDynamicTriShape(void) { return nullptr; }; + virtual void * GetAsSegmentedTriShape() { return nullptr; }; + virtual BSSubIndexTriShape * GetAsBSSubIndexTriShape(void) { return nullptr; }; + virtual void * GetAsNiGeometry() { return nullptr; }; + virtual void * GetAsNiTriBasedGeom() { return nullptr; }; + virtual void * GetAsNiTriShape() { return nullptr; }; + virtual void * GetAsParticlesGeom() { return nullptr; }; + virtual void * GetAsParticleSystem() { return nullptr; }; + virtual void * GetAsLinesGeom() { return nullptr; }; + virtual void * GetAsLight() { return nullptr; }; + virtual bhkNiCollisionObject * GetAsBhkNiCollisionObject() { return nullptr; }; + virtual bhkBlendCollisionObject * GetAsBhkBlendCollisionObject() { return nullptr; }; + virtual bhkRigidBody * GetAsBhkRigidBody() { return nullptr; }; + virtual bhkLimitedHingeConstraint * GetAsBhkLimitedHingeConstraint() { return nullptr; }; + virtual bhkNPCollisionObject * GetAsbhkNPCollisionObject() { return nullptr; }; + virtual NiObject * CreateClone(NiCloningProcess * unk1) { return nullptr; }; + virtual void LoadBinary(void * stream) { }; // LoadBinary + virtual void Unk_1C() { }; + virtual bool Unk_1D() { return false; }; + virtual void SaveBinary(void * stream) { }; // SaveBinary + virtual bool IsEqual(NiObject * object) { return CALL_MEMBER_FN(this, Internal_IsEqual)(object); } // IsEqual + virtual bool Unk_20(void * unk1) { return false; }; + virtual void Unk_21() { }; + virtual bool Unk_22() { return false; }; + virtual NiRTTI * GetStreamableRTTI() { return GetRTTI(); }; + virtual bool Unk_24() { return false; }; + virtual bool Unk_25() { return false; }; + virtual void Unk_26() { }; + virtual bool Unk_27() { return false; }; + + MEMBER_FN_PREFIX(NiObject); + DEFINE_MEMBER_FN(Internal_IsEqual, bool, 0x01B94A90, NiObject * object); +}; + +// 28 +class NiObjectNET : public NiObject +{ +public: + enum CopyType + { + COPY_NONE, + COPY_EXACT, + COPY_UNIQUE + }; + + BSFixedString m_name; // 10 + void * unk10; // 18 - Controller? + tMutexArray * m_extraData; // 20 - must be locked/unlocked when altering/acquiring + + bool AddExtraData(NiExtraData * extraData); + NiExtraData * GetExtraData(const BSFixedString & name); + bool HasExtraData(const BSFixedString & name) { return GetExtraData(name) != nullptr; } + + MEMBER_FN_PREFIX(NiObjectNET); + DEFINE_MEMBER_FN(Internal_AddExtraData, bool, 0x01B978C0, NiExtraData * extraData); +}; + +// 120 +class NiAVObject : public NiObjectNET +{ +public: + + struct NiUpdateData + { + NiUpdateData() : unk00(nullptr), pCamera(nullptr), flags(0), unk14(0), unk18(0), unk20(0), unk28(0), unk30(0), unk38(0) { } + + void * unk00; // 00 + void * pCamera; // 08 + UInt32 flags; // 10 + UInt32 unk14; // 14 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + UInt64 unk28; // 28 + UInt64 unk30; // 30 + UInt64 unk38; // 38 + }; + + virtual void UpdateControllers(); + virtual void PerformOp(); + virtual void AttachProperty(); + virtual void SetMaterialNeedsUpdate(); // empty? + virtual void SetDefaultMaterialNeedsUpdateFlag(); // empty? + virtual void SetAppCulled(bool set); + virtual NiAVObject * GetObjectByName(const BSFixedString * nodeName); + virtual void SetSelectiveUpdateFlags(bool * unk1, bool unk2, bool * unk3); + virtual void UpdateDownwardPass(); + virtual void UpdateSelectedDownwardPass(); + virtual void UpdateRigidDownwardPass(); + virtual void UpdateWorldBound(); + virtual void UpdateWorldData(NiUpdateData * ctx); + virtual void UpdateTransformAndBounds(); + virtual void UpdateTransforms(); + virtual void Unk_37(); + virtual void Unk_38(); + + NiNode * m_parent; // 28 + NiTransform m_localTransform; // 30 + NiTransform m_worldTransform; // 70 + NiBound m_worldBound; // B0 + NiTransform m_previousWorld; // C0 + NiPointer m_spCollisionObject; // 100 + + enum : UInt64 + { + kFlagAlwaysDraw = (1 << 11), + kFlagIsMeshLOD = (1 << 12), + kFlagFixedBound = (1 << 13), + kFlagTopFadeNode = (1 << 14), + kFlagIgnoreFade = (1 << 15), + kFlagNoAnimSyncX = (1 << 16), + kFlagNoAnimSyncY = (1 << 17), + kFlagNoAnimSyncZ = (1 << 18), + kFlagNoAnimSyncS = (1 << 19), + kFlagNoDismember = (1 << 20), + kFlagNoDismemberValidity = (1 << 21), + kFlagRenderUse = (1 << 22), + kFlagMaterialsApplied = (1 << 23), + kFlagHighDetail = (1 << 24), + kFlagForceUpdate = (1 << 25), + kFlagPreProcessedNode = (1 << 26), + kFlagScenegraphChange = (1 << 29), + kFlagInInstanceGroup = (1LL << 35), + kFlagLODFadingOut = (1LL << 36), + kFlagFadedIn = (1LL << 37), + kFlagForcedFadeOut = (1LL << 38), + kFlagNotVisible = (1LL << 39), + kFlagShadowCaster = (1LL << 40), + kFlagNeedsRendererData = (1LL << 41), + kFlagAccumulated = (1LL << 42), + kFlagAlreadyTraversed = (1LL << 43), + kFlagPickOff = (1LL << 44), + kFlagUpdateWorldData = (1LL << 45), + kFlagHasPropController = (1LL << 46), + kFlagHasLockedChildAccess = (1LL << 47), + kFlagHasMovingSound = (1LL << 49), + }; + + UInt64 flags; // 108 + void * unk110; // 110 + float unk118; // 118 + UInt32 unk11C; // 11C + + MEMBER_FN_PREFIX(NiAVObject); + DEFINE_MEMBER_FN(GetAVObjectByName, NiAVObject*, 0x01C93980, BSFixedString * name, bool unk1, bool unk2); + DEFINE_MEMBER_FN(SetScenegraphChange, void, 0x01BA47C0); + + // Return true in the functor to halt traversal + template + bool Visit(T & functor) + { + if (functor(this)) + return true; + + NiPointer node(GetAsNiNode()); + if(node) { + for(UInt32 i = 0; i < node->m_children.m_emptyRunStart; i++) { + NiPointer object(node->m_children.m_data[i]); + if(object) { + if (object->Visit(functor)) + return true; + } + } + } + + return false; + } +}; +STATIC_ASSERT(sizeof(NiAVObject) == 0x120); diff --git a/f4se/f4se/NiProperties.cpp b/f4se/f4se/NiProperties.cpp new file mode 100644 index 0000000..6182416 --- /dev/null +++ b/f4se/f4se/NiProperties.cpp @@ -0,0 +1,15 @@ +#include "f4se/NiProperties.h" + +BSEffectShaderProperty * BSEffectShaderProperty::Create() +{ + BSEffectShaderProperty * prop = (BSEffectShaderProperty *)Heap_Allocate(sizeof(BSEffectShaderProperty)); + CALL_MEMBER_FN(prop, ctor)(); + return prop; +} + +BSLightingShaderProperty * BSLightingShaderProperty::Create() +{ + BSLightingShaderProperty * prop = (BSLightingShaderProperty *)Heap_Allocate(sizeof(BSLightingShaderProperty)); + CALL_MEMBER_FN(prop, ctor)(); + return prop; +} diff --git a/f4se/f4se/NiProperties.h b/f4se/f4se/NiProperties.h new file mode 100644 index 0000000..473c514 --- /dev/null +++ b/f4se/f4se/NiProperties.h @@ -0,0 +1,197 @@ +#pragma once + +#include "f4se/NiObjects.h" +#include "f4se/BSLight.h" + +class BSShaderMaterial; +class BSParticleShaderCubeEmitter; +class NiTexture; +class BSRenderPass; +class BSFadeNode; + +class NiProperty : public NiObjectNET +{ +public: + virtual ~NiProperty(); + + enum + { + kTypeAlpha = 0, + kTypeShade = 1 + }; + + virtual SInt32 GetType(void); +}; + +// 30 +class NiAlphaProperty : public NiProperty +{ +public: + virtual ~NiAlphaProperty(); + + enum AlphaFunction + { + kAlpha_One = 0, + kAlpha_Zero, + kAlpha_SrcColor, + kAlpha_InvSrcColor, + kAlpha_DestColor, + kAlpha_InvDestColor, + kAlpha_SrcAlpha, + kAlpha_InvSrcAlpha, + kAlpha_DestAlpha, + kAlpha_InvDestAlpha, + kAlpha_SrcAlphaSat, + kAlpha_Max_Modes, + }; + + enum TestFunction + { + kTest_Always = 0, + kTest_Less, + kTest_Equal, + kTest_LessEqual, + kTest_Greater, + kTest_NotEqual, + kTest_GreaterEqual, + kTest_Never, + kTest_Max_Modes, + }; + + UInt16 alphaFlags; // 28 + UInt8 alphaThreshold; // 2A +}; + +// 28 +class NiShadeProperty : public NiProperty +{ +public: + virtual ~NiShadeProperty(); + + virtual void Unk_23(void); +}; + +// 90 +class BSEffectShaderData : public BSIntrusiveRefCounted +{ +public: + bool (__cdecl *pNodeFilterFunction)(BSFixedString *); // 08 + NiPointer spBaseTexture; // 10 + NiPointer spPaletteTexture; // 18 + NiPointer spBlockOutTexture; // 20 + UInt32 eTextureClampMode; // 28 + NiColorA FillColor; // 2C + NiColorA RimColor; // 3C + float fBaseFillScale; // 4C + float fBaseFillAlpha; // 50 + float fBaseRimAlpha; // 54 + float fUOffset; // 58 + float fVOffset; // 5C + float fUScale; // 60 + float fVScale; // 64 + float fEdgeExponent; // 68 + float fBoundDiameter; // 6C + NiAlphaProperty::AlphaFunction eSrcBlend; // 70 + NiAlphaProperty::AlphaFunction eDestBlend; // 78 + UInt32 eZTestFunc; // 80 + char ucAlphaTestRef; // 84 + bool bGrayscaleToColor; // 85 + bool bGrayscaleToAlpha; // 86 + bool bIgnoreTextureAlpha; // 87 + bool bBaseTextureProjectedUVs; // 88 + bool bIgnoreBaseGeomTexAlpha; // 89 + bool bLighting; // 8A + bool bAlpha; // 8B +}; + +// 70 +class BSShaderProperty : public NiShadeProperty +{ +public: + float fAlpha; // 28 + SInt32 iLastRenderPassState; // 2C - Set to 0x7FFFFFFF somekind of lock? + UInt64 flags; // 30 + BSRenderPass * kRenderPassList; // 38 + BSRenderPass * kDebugRenderPassList; // 40 + BSFadeNode * pFadeNode; // 48 + BSEffectShaderData * pEffectData; // 50 + BSShaderMaterial * shaderMaterial; // 58 + UInt32 uiLastAccumTime; // 60 + float fLODFade; // 64 + BSNonReentrantSpinLock ClearRenderPassesLock; // 68 + + enum : UInt64 + { + kShaderFlags_GrayscaleToPalette = 0x0000000080000000ULL, + kShaderFlags_Hair = 0x0000400000000000ULL, + kShaderFlags_Refraction = 0x0000000000008000ULL, + kShaderFlags_EmitColor = 0x0000000000400000ULL, + kShaderFlags_NoExternalEmit = 0x4000000000000000ULL + }; + + MEMBER_FN_PREFIX(BSShaderProperty); + DEFINE_MEMBER_FN(SetMaterial, bool, 0x027BC560, BSShaderMaterial * material, bool unk1); + DEFINE_MEMBER_FN(SetFlag, void, 0x027BC3E0, UInt8 flags, bool enabled); // sets or unsets particular flags +}; +STATIC_ASSERT(sizeof(BSShaderProperty) == 0x70); + +// 88 +class BSEffectShaderProperty : public BSShaderProperty +{ +public: + BSParticleShaderCubeEmitter * pEnvCubeEmitter; // 70 + NiColor * pExternalEmitColor; // 78 + UInt32 uBaseTextureIndex; // 80 + float unk84; // 84 + + MEMBER_FN_PREFIX(BSEffectShaderProperty); + DEFINE_MEMBER_FN(ctor, void, 0x027CAAC0); + + static BSEffectShaderProperty * Create(); +}; +STATIC_ASSERT(sizeof(BSEffectShaderProperty) == 0x88); + +// E8 +class BSLightingShaderProperty : public BSShaderProperty +{ +public: + NiColorA kProjectedUVColor; // 70 + NiColorA kProjectedUVParams; // 80 + BSRenderPass * kDepthMapRenderPassListA[3]; // 90 + BSRenderPass * pDepthRenderPass; // A8 + BSRenderPass * kForwardPassList; // B0 + NiColor * pEmissiveColor; // B8 + BSFixedString rootName; // C0 + float fEmitColorScale; // C8 + float fForcedDarkness; // CC + float fSpecularLODFade; // D0 + float fEnvmapLODFade; // D4 + UInt32 uiBaseTechniqueID; // D8 + UInt32 ClearCommandBufferCount; // DC + UInt16 usDebugTintIndex; // E0 + UInt16 uiStencilRef; // E2 + UInt32 unkE4; // E4 + + MEMBER_FN_PREFIX(BSLightingShaderProperty); + DEFINE_MEMBER_FN(ctor, BSLightingShaderProperty *, 0x027CC880); + DEFINE_MEMBER_FN(MakeValidForRendering, void, 0x027CCB70, BSGeometry * geometry); // previously InvalidateShader + DEFINE_MEMBER_FN(ApplyMaterial, bool, 0x00054170); // Calls BSShaderProperty::SetMaterial + DEFINE_MEMBER_FN(LoadTextureSet, void, 0x027CD200, UInt32 unk1); // unk1 usually 0, called after material Releases textures (previously InvalidateTextures) + + static BSLightingShaderProperty * Create(); +}; +STATIC_ASSERT(sizeof(BSLightingShaderProperty) == 0xE8); + +// C0 +class BSWaterShaderProperty : public BSShaderProperty +{ +public: + UInt32 uiWaterFlags; // 70 + UInt32 iStencilMask; // 74 + NiPlane kWaterPlane; // 78 + BSRenderPass * kSimpleRenderPassArray; // 88 + BSRenderPass * kRainOcclusionMapRenderPassList; // 90 + BSShaderPropertyLightData kLightData; // 98 +}; + +STATIC_ASSERT(sizeof(BSWaterShaderProperty) == 0xC0); diff --git a/f4se/f4se/NiRTTI.cpp b/f4se/f4se/NiRTTI.cpp new file mode 100644 index 0000000..e59f04c --- /dev/null +++ b/f4se/f4se/NiRTTI.cpp @@ -0,0 +1,22 @@ +#include "f4se/NiRTTI.h" +#include "f4se/NiObjects.h" + +NiObject * DoNiRTTICast(NiObject * src, const NiRTTI * typeInfo) +{ + if(src) + for(NiRTTI * iter = src->GetRTTI(); iter; iter = iter->parent) + if(iter == typeInfo) + return src; + + return nullptr; +} + +// E8007B50AB42A5298C03123C69989D33E62E5595+3D +const RelocPtr NiRTTI_BSLightingShaderProperty(0x06721908); + +// E8007B50AB42A5298C03123C69989D33E62E5595+79 +const RelocPtr NiRTTI_BSEffectShaderProperty(0x067218F8); + +const RelocPtr NiRTTI_BSShaderProperty(0x06721890); // xref aBsshaderproper, loaded to ecx + +const RelocPtr NiRTTI_NiExtraData(0x05C08E90); // xref aNiextradata, loaded to ecx diff --git a/f4se/f4se/NiRTTI.h b/f4se/f4se/NiRTTI.h new file mode 100644 index 0000000..b2073d6 --- /dev/null +++ b/f4se/f4se/NiRTTI.h @@ -0,0 +1,22 @@ +#pragma once + +#include "f4se_common/Relocation.h" + +class NiObject; + +// 10 +class NiRTTI +{ +public: + const char * name; + NiRTTI * parent; +}; + +NiObject * DoNiRTTICast(NiObject * src, const NiRTTI * typeInfo); + +#define ni_cast(obj, type) (type *)DoNiRTTICast(obj, NiRTTI_##type) + +extern const RelocPtr NiRTTI_BSLightingShaderProperty; +extern const RelocPtr NiRTTI_BSEffectShaderProperty; +extern const RelocPtr NiRTTI_BSShaderProperty; +extern const RelocPtr NIRTTI_NiExtraData; diff --git a/f4se/f4se/NiSerialization.cpp b/f4se/f4se/NiSerialization.cpp new file mode 100644 index 0000000..29a075b --- /dev/null +++ b/f4se/f4se/NiSerialization.cpp @@ -0,0 +1 @@ +#include "f4se/NiSerialization.h" diff --git a/f4se/f4se/NiSerialization.h b/f4se/f4se/NiSerialization.h new file mode 100644 index 0000000..6c90b36 --- /dev/null +++ b/f4se/f4se/NiSerialization.h @@ -0,0 +1,44 @@ +#pragma once + +#include "f4se_common/Utilities.h" + +class NiBinaryStream; +class NiObject; + +// 708 +class NiStream +{ +public: + virtual ~NiStream(); + + virtual bool LoadStream(NiBinaryStream * stream); + virtual bool LoadBuf(char * buf, UInt32 len); + virtual bool LoadPath(const char * path); + virtual bool SaveStream(NiBinaryStream * stream); + virtual bool SaveBuf(char ** buf, UInt32 * len); + virtual bool SavePath(const char * path); + + virtual void Unk_07(void); + virtual void Unk_08(void); + virtual void Unk_09(void); + virtual void Unk_0A(void); + virtual void Unk_0B(void); + virtual void Unk_0C(void); + virtual void Unk_0D(void); + virtual void Unk_0E(void); + virtual void Unk_0F(void); + virtual void Unk_10(void); + virtual void Unk_11(void); + virtual void Unk_12(void); + virtual void Unk_13(void); + virtual void Unk_14(void); + virtual void Unk_15(void); + virtual void Unk_16(void); + + UInt64 unk08[(0x708 - 0x08) >> 3]; // 08 + + MEMBER_FN_PREFIX(NiStream); + DEFINE_MEMBER_FN(ctor, NiStream *, 0x01BB8F90); + DEFINE_MEMBER_FN(dtor, void, 0x01BB91C0); + DEFINE_MEMBER_FN(AddObject, void, 0x01BB92F0, NiObject * object); +}; diff --git a/f4se/f4se/NiTextures.cpp b/f4se/f4se/NiTextures.cpp new file mode 100644 index 0000000..a665fe5 --- /dev/null +++ b/f4se/f4se/NiTextures.cpp @@ -0,0 +1,7 @@ +#include "f4se/NiTextures.h" + +RelocAddr <_CreateTexture> CreateTexture(0x01BA5220); + +RelocAddr <_CreateBSShaderTextureSet> CreateBSShaderTextureSet(0x027BD1D0); + +RelocAddr <_LoadTextureByPath> LoadTextureByPath(0x027D61F0); \ No newline at end of file diff --git a/f4se/f4se/NiTextures.h b/f4se/f4se/NiTextures.h new file mode 100644 index 0000000..57c22ee --- /dev/null +++ b/f4se/f4se/NiTextures.h @@ -0,0 +1,94 @@ +#pragma once + +#include "f4se/NiObjects.h" +#include "f4se/GameTypes.h" + +struct ID3D11ShaderResourceView; +struct ID3D11Resource; + +// 40 +struct BSRenderData +{ + ID3D11ShaderResourceView * resourceView; // 00 + ID3D11Resource * resource; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 + UInt16 width; // 28 + UInt16 height; // 2A + UInt8 unk2B; // 2B + UInt8 unk2C; // 2C + UInt16 unk2D; // 2D + UInt64 unk30; // 30 + UInt64 unk38; // 38 +}; + +// 48 +class NiTexture : public NiObject +{ +public: + BSFixedString name; // 10 + UInt32 unk18; // 18 + UInt32 unk1C; // 1C + NiTexture * prevTexture; // 20 + NiTexture * nextTexture; // 28 + UInt64 unk30; // 30 + BSRenderData * rendererData; // 38 + UInt16 unk40; // 40 + UInt16 unk42; // 42 + UInt32 unk44; // 44 +}; + +class BSTextureArray +{ +public: + class StaticTexture : public NiTexture + { + public: + virtual ~StaticTexture(); + }; + + // 78 + class StaticTextureIndexed : public StaticTexture + { + public: + virtual ~StaticTextureIndexed(); + + MEMBER_FN_PREFIX(StaticTextureIndexed); + DEFINE_MEMBER_FN(ctor, StaticTextureIndexed*, 0x01CE68C0, UInt32 unk1, int unk2, bool unk3); + + UInt64 unk48[(0x78 - 0x48) >> 3]; + }; +}; + +// 10 +class BSTextureSet : public NiObject +{ +public: + virtual BSFixedString GetTextureFilenameFS(UInt32 typeEnum); + virtual const char * GetTextureFilename(UInt32 typeEnum); + virtual void Unk_2A(); + virtual void GetTexture(UInt32 typeEnum, NiPointer & texture, bool unk1); + virtual void SetTextureFilename(UInt32 typeEnum, const char * path); +}; + +// 60 +class BSShaderTextureSet : public BSTextureSet +{ +public: + BSFixedString textures[10]; // 10 + + MEMBER_FN_PREFIX(BSShaderTextureSet); + DEFINE_MEMBER_FN(Copy, BSShaderTextureSet*, 0x004C36B0); +}; +STATIC_ASSERT(sizeof(BSShaderTextureSet) == 0x60); + +typedef BSShaderTextureSet * (* _CreateBSShaderTextureSet)(); +extern RelocAddr <_CreateBSShaderTextureSet> CreateBSShaderTextureSet; + +typedef NiTexture * (* _CreateTexture)(const BSFixedString & name, UInt8 unk1); // unk1 is true on diffuses? +extern RelocAddr <_CreateTexture> CreateTexture; + +// unk1=1, unk2=0, unk3=0, unk4=0 +typedef void (* _LoadTextureByPath)(const char * filePath, bool unk1, NiTexture *& texture, SInt32 unk2, UInt64 unk3, UInt64 unk4); +extern RelocAddr <_LoadTextureByPath> LoadTextureByPath; \ No newline at end of file diff --git a/f4se/f4se/NiTypes.cpp b/f4se/f4se/NiTypes.cpp new file mode 100644 index 0000000..26ea192 --- /dev/null +++ b/f4se/f4se/NiTypes.cpp @@ -0,0 +1,192 @@ +#include "f4se/NiTypes.h" + +NiPoint3::NiPoint3() +{ + x = 0.0f; + y = 0.0f; + z = 0.0f; +} + +NiPoint3 NiPoint3::operator- () const +{ + return NiPoint3(-x, -y, -z); +} + +NiPoint3 NiPoint3::operator+ (const NiPoint3& pt) const +{ + return NiPoint3(x + pt.x, y + pt.y, z + pt.z); +} + +NiPoint3 NiPoint3::operator- (const NiPoint3& pt) const +{ + return NiPoint3(x - pt.x, y - pt.y, z - pt.z); +} + +NiPoint3& NiPoint3::operator+= (const NiPoint3& pt) +{ + x += pt.x; + y += pt.y; + z += pt.z; + return *this; +} +NiPoint3& NiPoint3::operator-= (const NiPoint3& pt) +{ + x -= pt.x; + y -= pt.y; + z -= pt.z; + return *this; +} + +// Scalar operations +NiPoint3 NiPoint3::operator* (float scalar) const +{ + return NiPoint3(scalar * x, scalar * y, scalar * z); +} +NiPoint3 NiPoint3::operator/ (float scalar) const +{ + float invScalar = 1.0f / scalar; + return NiPoint3(invScalar * x, invScalar * y, invScalar * z); +} + +NiPoint3& NiPoint3::operator*= (float scalar) +{ + x *= scalar; + y *= scalar; + z *= scalar; + return *this; +} +NiPoint3& NiPoint3::operator/= (float scalar) +{ + float invScalar = 1.0f / scalar; + x *= invScalar; + y *= invScalar; + z *= invScalar; + return *this; +} + +NiMatrix43 NiMatrix43::operator* (const NiMatrix43& rhs) const +{ + NiMatrix43 tmp; + tmp.data[0][0] = + data[0][0] * rhs.data[0][0] + + data[0][1] * rhs.data[1][0] + + data[0][2] * rhs.data[2][0]; + tmp.data[1][0] = + data[1][0] * rhs.data[0][0] + + data[1][1] * rhs.data[1][0] + + data[1][2] * rhs.data[2][0]; + tmp.data[2][0] = + data[2][0] * rhs.data[0][0] + + data[2][1] * rhs.data[1][0] + + data[2][2] * rhs.data[2][0]; + tmp.data[0][1] = + data[0][0] * rhs.data[0][1] + + data[0][1] * rhs.data[1][1] + + data[0][2] * rhs.data[2][1]; + tmp.data[1][1] = + data[1][0] * rhs.data[0][1] + + data[1][1] * rhs.data[1][1] + + data[1][2] * rhs.data[2][1]; + tmp.data[2][1] = + data[2][0] * rhs.data[0][1] + + data[2][1] * rhs.data[1][1] + + data[2][2] * rhs.data[2][1]; + tmp.data[0][2] = + data[0][0] * rhs.data[0][2] + + data[0][1] * rhs.data[1][2] + + data[0][2] * rhs.data[2][2]; + tmp.data[1][2] = + data[1][0] * rhs.data[0][2] + + data[1][1] * rhs.data[1][2] + + data[1][2] * rhs.data[2][2]; + tmp.data[2][2] = + data[2][0] * rhs.data[0][2] + + data[2][1] * rhs.data[1][2] + + data[2][2] * rhs.data[2][2]; + return tmp; +} + +NiPoint3 NiMatrix43::operator* (const NiPoint3& pt) const +{ + return NiPoint3 + ( + data[0][0]*pt.x+data[0][1]*pt.y+data[0][2]*pt.z, + data[1][0]*pt.x+data[1][1]*pt.y+data[1][2]*pt.z, + data[2][0]*pt.x+data[2][1]*pt.y+data[2][2]*pt.z + ); +} + +NiMatrix43 NiMatrix43::Transpose() const +{ + NiMatrix43 result; + result.data[0][0] = data[0][0]; + result.data[0][1] = data[1][0]; + result.data[0][2] = data[2][0]; + result.data[0][3] = data[0][3]; + result.data[1][0] = data[0][1]; + result.data[1][1] = data[1][1]; + result.data[1][2] = data[2][1]; + result.data[1][3] = data[1][3]; + result.data[2][0] = data[0][2]; + result.data[2][1] = data[1][2]; + result.data[2][2] = data[2][2]; + result.data[2][3] = data[2][3]; + return result; +} + +// Converted from Java to C +// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToEuler/index.htm +void NiMatrix43::GetEulerAngles(float * heading, float * attitude, float * bank) +{ + if (data[1][0] > 0.998) { // singularity at north pole + *heading = atan2(data[0][2], data[2][2]); + *attitude = MATH_PI / 2; + *bank = 0; + } + else if (data[1][0] < -0.998) { // singularity at south pole + *heading = atan2(data[0][2], data[2][2]); + *attitude = -MATH_PI / 2; + *bank = 0; + } + else { + *heading = atan2(-data[2][0], data[0][0]); + *bank = atan2(-data[1][2], data[1][1]); + *attitude = asin(data[1][0]); + } +} + +// Converted from Java to C +// http://www.euclideanspace.com/maths/geometry/rotations/conversions/eulerToMatrix/index.htm +void NiMatrix43::SetEulerAngles(float heading, float attitude, float bank) +{ + double ch = cos(heading); + double sh = sin(heading); + double ca = cos(attitude); + double sa = sin(attitude); + double cb = cos(bank); + double sb = sin(bank); + + data[0][0] = ch * ca; + data[0][1] = sh * sb - ch * sa * cb; + data[0][2] = ch * sa * sb + sh * cb; + data[1][0] = sa; + data[1][1] = ca * cb; + data[1][2] = -ca * sb; + data[2][0] = -sh * ca; + data[2][1] = sh * sa * cb + ch * sb; + data[2][2] = -sh * sa * sb + ch * cb; +} + +NiTransform NiTransform::operator*(const NiTransform &rhs) const +{ + NiTransform tmp; + tmp.scale = scale * rhs.scale; + tmp.rot = rot * rhs.rot; + tmp.pos = pos + (rot * rhs.pos) * scale; + return tmp; +} + +NiPoint3 NiTransform::operator*(const NiPoint3 & pt) const +{ + return (((rot * pt) * scale) + pos); +} diff --git a/f4se/f4se/NiTypes.h b/f4se/f4se/NiTypes.h new file mode 100644 index 0000000..2695117 --- /dev/null +++ b/f4se/f4se/NiTypes.h @@ -0,0 +1,413 @@ +#pragma once + +#include "f4se/GameAPI.h" + +// 8 +template +class NiPointer +{ +public: + T * m_pObject; // 00 + + inline NiPointer(T* pObject = (T*) 0) + { + m_pObject = pObject; + if(m_pObject) m_pObject->IncRef(); + } + + inline ~NiPointer() + { + if(m_pObject) m_pObject->DecRef(); + } + + inline operator bool() const + { + return m_pObject != nullptr; + } + + inline operator T *() const + { + return m_pObject; + } + + inline T & operator*() const + { + return *m_pObject; + } + + inline T * operator->() const + { + return m_pObject; + } + + inline NiPointer & operator=(const NiPointer & rhs) + { + if(m_pObject != rhs.m_pObject) + { + if(rhs) rhs.m_pObject->IncRef(); + if(m_pObject) m_pObject->DecRef(); + + m_pObject = rhs.m_pObject; + } + + return *this; + } + + inline NiPointer & operator=(T * rhs) + { + if(m_pObject != rhs) + { + if(rhs) rhs->IncRef(); + if(m_pObject) m_pObject->DecRef(); + + m_pObject = rhs; + } + + return *this; + } + + inline bool operator==(T * pObject) const + { + return m_pObject == pObject; + } + + inline bool operator!=(T * pObject) const + { + return m_pObject != pObject; + } + + inline bool operator==(const NiPointer & ptr) const + { + return m_pObject == ptr.m_pObject; + } + + inline bool operator!=(const NiPointer & ptr) const + { + return m_pObject != ptr.m_pObject; + } +}; + +#define MAKE_NI_POINTER(x) class x; typedef NiPointer x##Ptr + +template +T_to * niptr_cast(const T_from & src) +{ + return static_cast (src.m_pObject); +} + +// C +class NiPoint3 +{ +public: + float x; // 0 + float y; // 4 + float z; // 8 + + NiPoint3(); + NiPoint3(float X, float Y, float Z) : x(X), y(Y), z(Z) { }; + + // Negative + NiPoint3 operator- () const; + + // Basic operations + NiPoint3 operator+ (const NiPoint3& pt) const; + NiPoint3 operator- (const NiPoint3& pt) const; + + NiPoint3& operator+= (const NiPoint3& pt); + NiPoint3& operator-= (const NiPoint3& pt); + + // Scalar operations + NiPoint3 operator* (float fScalar) const; + NiPoint3 operator/ (float fScalar) const; + + NiPoint3& operator*= (float fScalar); + NiPoint3& operator/= (float fScalar); +}; + +class __declspec(align(8)) NiPoint3A : NiPoint3 +{ +public: + +}; + +// C +class NiColor +{ +public: + float r; // 00 + float g; // 04 + float b; // 08 +}; + +// 10 +class NiColorA +{ +public: + float r; // 00 + float g; // 04 + float b; // 08 + float a; // 0C +}; + +// 10 +template +class NiRect +{ +public: + T m_left; // 00 + T m_right; // 04 + T m_top; // 08 + T m_bottom; // 0C +}; + +// 1C +class NiFrustum +{ +public: + float m_fLeft; // 00 + float m_fRight; // 04 + float m_fTop; // 08 + float m_fBottom; // 0C + float m_fNear; // 10 + float m_fFar; // 14 + bool m_bOrtho; // 18 +}; + +// 10 +class NiQuaternion +{ +public: + // w is first + + float m_fW; // 0 + float m_fX; // 4 + float m_fY; // 8 + float m_fZ; // C + + NiQuaternion operator*(const NiQuaternion& q2) const + { + NiQuaternion tmp; + tmp.m_fX = m_fX * q2.m_fW + m_fY * q2.m_fZ - m_fZ * q2.m_fY + m_fW * q2.m_fX; + tmp.m_fY = -m_fX * q2.m_fZ + m_fY * q2.m_fW + m_fZ * q2.m_fX + m_fW * q2.m_fY; + tmp.m_fZ = m_fX * q2.m_fY - m_fY * q2.m_fX + m_fZ * q2.m_fW + m_fW * q2.m_fZ; + tmp.m_fW = -m_fX * q2.m_fX - m_fY * q2.m_fY - m_fZ * q2.m_fZ + m_fW * q2.m_fW; + return tmp; + } + + NiPoint3 operator* (const NiPoint3& pt) const + { + NiPoint3 tmp; + tmp.x = m_fW * m_fW*pt.x + 2 * m_fY*m_fW*pt.z - 2 * m_fZ*m_fW*pt.y + m_fX * m_fX*pt.x + 2 * m_fY*m_fX*pt.y + 2 * m_fZ*m_fX*pt.z - m_fZ * m_fZ*pt.x - m_fY * m_fY*pt.x; + tmp.y = 2 * m_fX*m_fY*pt.x + m_fY * m_fY*pt.y + 2 * m_fZ*m_fY*pt.z + 2 * m_fW*m_fZ*pt.x - m_fZ * m_fZ*pt.y + m_fW * m_fW*pt.y - 2 * m_fX*m_fW*pt.z - m_fX * m_fX*pt.y; + tmp.z = 2 * m_fX*m_fZ*pt.x + 2 * m_fY*m_fZ*pt.y + m_fZ * m_fZ*pt.z - 2 * m_fW*m_fY*pt.x - m_fY * m_fY*pt.z + 2 * m_fW*m_fX*pt.y - m_fX * m_fX*pt.z + m_fW * m_fW*pt.z; + return tmp; + } + + void SetEulerAngles(float pitch, float roll, float yaw) + { + float t0 = std::cos(yaw * 0.5); + float t1 = std::sin(yaw * 0.5); + float t2 = std::cos(roll * 0.5); + float t3 = std::sin(roll * 0.5); + float t4 = std::cos(pitch * 0.5); + float t5 = std::sin(pitch * 0.5); + + m_fW = t0 * t2 * t4 + t1 * t3 * t5; + m_fX = t0 * t3 * t4 - t1 * t2 * t5; + m_fY = t0 * t2 * t5 + t1 * t3 * t4; + m_fZ = t1 * t2 * t4 - t0 * t3 * t5; + } + + void GetEulerAngles(float& roll, float& pitch, float& yaw) + { + float ysqr = m_fY * m_fY; + + // roll (x-axis rotation) + float t0 = +2.0 * (m_fW * m_fX + m_fY * m_fZ); + float t1 = +1.0 - 2.0 * (m_fX * m_fX + ysqr); + roll = std::atan2(t0, t1); + + // pitch (y-axis rotation) + float t2 = +2.0 * (m_fW * m_fY - m_fZ * m_fX); + t2 = t2 > 1.0 ? 1.0 : t2; + t2 = t2 < -1.0 ? -1.0 : t2; + pitch = std::asin(t2); + + // yaw (z-axis rotation) + float t3 = +2.0 * (m_fW * m_fZ + m_fX * m_fY); + float t4 = +1.0 - 2.0 * (ysqr + m_fZ * m_fZ); + yaw = std::atan2(t3, t4); + } +}; + +// 8 +class NiPoint2 +{ +public: + float x; // 0 + float y; // 4 +}; + +// 10 +class NiBound +{ +public: + NiPoint3 m_kCenter; + union + { + float m_fRadius; + int m_iRadiusAsInt; + }; +}; + +class NiPoint4 +{ +public: + struct NiPoint4Struct + { + float x; + float y; + float z; + float w; + }; + union + { + NiPoint4Struct v; + float m_pt[4]; + }; +}; + + +class NiMatrix3 +{ +public: + NiPoint4 m_pEntry[3]; +}; + +// 30 +class NiMatrix43 +{ +public: + union + { + float data[3][4]; + float arr[12]; + }; + + NiMatrix43 operator* (const NiMatrix43& rhs) const; + NiMatrix43 Transpose() const; + NiPoint3 operator* (const NiPoint3& pt) const; + void GetEulerAngles(float * heading, float * attitude, float * bank); + void SetEulerAngles(float heading, float attitude, float bank); +}; + +// math.h +#define MATH_PI 3.14159265358979323846 + +// 40 +class NiTransform +{ +public: + NiMatrix43 rot; // 00 + NiPoint3 pos; // 30 + float scale; // 3C + + + NiTransform operator*(const NiTransform &rhs) const; + NiPoint3 operator*(const NiPoint3 & pt) const; +}; +STATIC_ASSERT(sizeof(NiTransform) == 0x40); + +// 10 +struct NiPlane +{ + NiPoint3 m_kNormal; + float m_fConstant; +}; + +// 18 +template +class NiTArray +{ +public: + NiTArray(); + virtual ~NiTArray(); + + // sparse array, can have NULL entries that should be skipped + // iterate from 0 to m_emptyRunStart - 1 + +// void ** _vtbl; // 00 + T * m_data; // 08 + UInt16 m_arrayBufLen; // 10 - max elements storable in m_data + UInt16 m_emptyRunStart; // 12 - index of beginning of empty slot run + UInt16 m_size; // 14 - number of filled slots + UInt16 m_growSize; // 16 - number of slots to grow m_data by +}; + +// 20 +// derives from NiTMapBase, we don't bother +template +class NiTMap +{ +public: + virtual ~NiTMap(); + + struct NiTMapItem + { + NiTMapItem * next; + T_key key; + T_data data; + }; + + T_data Get(T_key key) + { + UInt32 bucket = GetBucket(key); + + for(NiTMapItem * iter = buckets[bucket]; iter; iter = iter->next) + { + if(Compare(iter->key, key)) + { + return iter->data; + } + } + + return T_data(); + } + + virtual UInt32 GetBucket(T_key key); // return hash % numBuckets; + virtual bool Compare(T_key lhs, T_key rhs); // return lhs == rhs; + virtual void FillItem(NiTMapItem * item, T_key key, T_data data); + // item->key = key; item->data = data; + virtual void Fn_04(UInt32 arg); // nop + virtual NiTMapItem * AllocItem(void); // return new NiTMapItem; + virtual void FreeItem(NiTMapItem * item); // item->data = 0; delete item; + + void RemoveAll() + { + for (UInt32 ui = 0; ui < numBuckets; ui++) + { + while (buckets[ui]) + { + NiTMapItem * pkSave = buckets[ui]; + buckets[ui] = buckets[ui]->next; + DeleteItem(pkSave); + } + } + + numEntries = 0; + } + + // void ** _vtbl; // 00 + UInt32 numBuckets; // 08 + UInt32 unk0C; // 0C + NiTMapItem ** buckets; // 10 + UInt32 numEntries; // 18 +}; + +// 10 +template +class NiTPointerMap : public NiTMap +{ +public: + +}; diff --git a/f4se/f4se/ObScript.cpp b/f4se/f4se/ObScript.cpp new file mode 100644 index 0000000..bf9029d --- /dev/null +++ b/f4se/f4se/ObScript.cpp @@ -0,0 +1,6 @@ +#include "ObScript.h" + +// 61A506FAB79EC111F852CE23E1E7A87D3E195A1B+43 +RelocPtr g_firstObScriptCommand(0x036F6DD0); +// 61A506FAB79EC111F852CE23E1E7A87D3E195A1B+1A +RelocPtr g_firstConsoleCommand(0x03706DC0); diff --git a/f4se/f4se/ObScript.h b/f4se/f4se/ObScript.h new file mode 100644 index 0000000..38fe299 --- /dev/null +++ b/f4se/f4se/ObScript.h @@ -0,0 +1,50 @@ +#pragma once + +#include "f4se_common/Utilities.h" + +class TESObjectREFR; + +// 0C +struct ObScriptParam +{ + const char * typeStr; // 00 + UInt32 typeID; // 04 + UInt32 isOptional; // 08 +}; + +typedef bool (* ObScript_Execute)(void * paramInfo, void * scriptData, TESObjectREFR * thisObj, void * containingObj, void * scriptObj, void * locals, double * result, void * opcodeOffsetPtr); + +// 50 +struct ObScriptCommand +{ + const char * longName; // 00 + const char * shortName; // 08 + UInt32 opcode; // 10 + UInt32 pad14; // 14 + const char * helpText; // 18 + UInt8 needsParent; // 20 + UInt8 pad21; // 21 + UInt16 numParams; // 22 + UInt32 pad24; // 24 + ObScriptParam * params; // 28 + + // handlers + ObScript_Execute execute; // 30 + void * parse; // 38 + void * eval; // 40 + + UInt32 flags; // 48 + UInt32 pad4C; // 4C +}; + +enum +{ + kObScript_NumObScriptCommands = 0x0332, + kObScript_NumConsoleCommands = 0x0209, + + kObScript_ScriptOpBase = 0x1000, + kObScript_ConsoleOpBase = 0x0100, +}; + +extern RelocPtr g_firstObScriptCommand; +extern RelocPtr g_firstConsoleCommand; diff --git a/f4se/f4se/PapyrusActor.cpp b/f4se/f4se/PapyrusActor.cpp new file mode 100644 index 0000000..4dd7804 --- /dev/null +++ b/f4se/f4se/PapyrusActor.cpp @@ -0,0 +1,140 @@ +#include "f4se/PapyrusActor.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" +#include "f4se/PapyrusArgs.h" +#include "f4se/PapyrusStruct.h" + +#include "f4se/GameForms.h" +#include "f4se/GameReferences.h" +#include "f4se/GameRTTI.h" +#include "f4se/GameObjects.h" +#include "f4se/GameExtraData.h" + +#include "f4se/PapyrusDelayFunctors.h" + +#include "f4se/NiRTTI.h" +#include "f4se/NiNodes.h" +#include "f4se/BSGeometry.h" +#include "f4se/NiProperties.h" +#include "f4se/NiMaterials.h" + +namespace papyrusActor +{ + DECLARE_STRUCT(WornItem, "Actor") + + WornItem GetWornItem(Actor* actor, UInt32 slotIndex, bool bFirstPerson) + { + WornItem result; + result.Set("item", nullptr); + result.Set("model", nullptr); + result.Set("modelName", ""); + result.Set("materialSwap", nullptr); + result.Set("texture", nullptr); + + if(slotIndex >= ActorEquipData::kMaxSlots) { + result.SetNone(true); + return result; + } + + ActorEquipData * equipData = actor->equipData; + PlayerCharacter * pPC = DYNAMIC_CAST(actor, Actor, PlayerCharacter); + if(pPC && bFirstPerson) + equipData = pPC->playerEquipData; + + if(equipData) { + auto materialSwap = equipData->slots[slotIndex].modelMatSwap; + result.Set("item", equipData->slots[slotIndex].item); + result.Set("model", equipData->slots[slotIndex].model); + if(materialSwap) { + result.Set("modelName", materialSwap->GetModelName()); + result.Set("materialSwap", materialSwap->materialSwap); + } + result.Set("texture", equipData->slots[slotIndex].textureSet); + } + + return result; + } + + VMArray GetWornItemMods(Actor* actor, UInt32 slotIndex) + { + VMArray result; + result.SetNone(true); + + // Invalid slot id + if(slotIndex >= ActorEquipData::kMaxSlots) + return result; + + ExtraDataList * stackDataList = nullptr; + if (actor->GetEquippedExtraData(slotIndex, &stackDataList) && stackDataList) + { + result.SetNone(false); + + BSExtraData * extraData = stackDataList->GetByType(ExtraDataType::kExtraData_ObjectInstance); + if (extraData) + { + BGSObjectInstanceExtra * objectModData = DYNAMIC_CAST(extraData, BSExtraData, BGSObjectInstanceExtra); + if (objectModData) + { + auto data = objectModData->data; + if (!data || !data->forms) + return result; + + for (UInt32 i = 0; i < data->blockSize / sizeof(BGSObjectInstanceExtra::Data::Form); i++) + { + BGSMod::Attachment::Mod * objectMod = (BGSMod::Attachment::Mod *)Runtime_DynamicCast(LookupFormByID(data->forms[i].formId), RTTI_TESForm, RTTI_BGSMod__Attachment__Mod); + result.Push(&objectMod); + } + } + } + } + + return result; + } + + void QueueUpdate(Actor * actor, bool bDoEquipment, UInt32 flags) + { + if(actor) { + CALL_MEMBER_FN(actor, QueueUpdate)(bDoEquipment, 0, true, flags); + } + } + + TESObjectREFR * GetFurnitureReference(Actor * actor) + { + TESObjectREFR * refr = nullptr; + if(!actor) + return nullptr; + + auto middleProcess = actor->middleProcess; + if(!middleProcess) + return nullptr; + + UInt32 furnitureHandle = 0; + auto data08 = middleProcess->unk08; + if(!data08) + return nullptr; + + if(actor->actorState.flags & (ActorState::Flags::kUnk1 | ActorState::Flags::kUnk2)) + furnitureHandle = data08->furnitureHandle2; + else + furnitureHandle = data08->furnitureHandle1; + + LookupREFRByHandle(&furnitureHandle, &refr); + return refr; + } +} + +void papyrusActor::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction2("GetWornItem", "Actor", papyrusActor::GetWornItem, vm)); + + vm->RegisterFunction( + new NativeFunction1, UInt32>("GetWornItemMods", "Actor", papyrusActor::GetWornItemMods, vm)); + + vm->RegisterFunction( + new NativeFunction2("QueueUpdate", "Actor", papyrusActor::QueueUpdate, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetFurnitureReference", "Actor", papyrusActor::GetFurnitureReference, vm)); +} diff --git a/f4se/f4se/PapyrusActor.h b/f4se/f4se/PapyrusActor.h new file mode 100644 index 0000000..400f05f --- /dev/null +++ b/f4se/f4se/PapyrusActor.h @@ -0,0 +1,11 @@ +#pragma once + +class VirtualMachine; +struct StaticFunctionTag; + +#include "f4se/GameTypes.h" + +namespace papyrusActor +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusActorBase.cpp b/f4se/f4se/PapyrusActorBase.cpp new file mode 100644 index 0000000..a0970ba --- /dev/null +++ b/f4se/f4se/PapyrusActorBase.cpp @@ -0,0 +1,104 @@ +#include "f4se/PapyrusActorBase.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/GameObjects.h" +#include "f4se/GameData.h" + +DECLARE_STRUCT(BodyWeight, "ActorBase") + +namespace papyrusActorBase +{ + TESNPC* GetTemplate(TESNPC* thisNPC, bool topMost) + { + TESNPC * node = nullptr; + if(topMost) { + node = thisNPC->templateNPC; + if (node) { + while (node->templateNPC) + node = node->templateNPC; + } + } + else + node = thisNPC->templateNPC; + + return node; + } + + bool HasHeadPartOverlays(TESNPC* thisNPC) + { + return CALL_MEMBER_FN(thisNPC, HasOverlays)(); + } + + VMArray GetHeadParts(TESNPC* thisNPC, bool overlays) + { + VMArray result; + UInt32 numHeadParts = thisNPC->numHeadParts; + BGSHeadPart ** headParts = thisNPC->headParts; + if(CALL_MEMBER_FN(thisNPC, HasOverlays)() && overlays) + { + numHeadParts = CALL_MEMBER_FN(thisNPC, GetNumOverlayHeadParts)(); + headParts = CALL_MEMBER_FN(thisNPC, GetOverlayHeadParts)(); + } + + for(UInt32 i = 0; i < numHeadParts; i++) + { + result.Push(&headParts[i]); + } + return result; + } + + BGSOutfit* GetOutfit(TESNPC* thisNPC, bool bSleepOutfit) + { + if (!thisNPC) + return NULL; + return bSleepOutfit ? thisNPC->outfit[1] : thisNPC->outfit[0]; + } + + void SetBodyWeight(TESNPC* thisNPC, BodyWeight weight) + { + if(thisNPC) { + weight.Get("thin", &thisNPC->weightThin); + weight.Get("muscular", &thisNPC->weightMuscular); + weight.Get("large", &thisNPC->weightLarge); + + thisNPC->MarkChanged(0x4000); + } + } + + BodyWeight GetBodyWeight(TESNPC* thisNPC) + { + BodyWeight weight; + if(thisNPC) { + weight.Set("thin", thisNPC->weightThin); + weight.Set("muscular", thisNPC->weightMuscular); + weight.Set("large", thisNPC->weightLarge); + } + + return weight; + } +} + +void papyrusActorBase::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction1 ("GetTemplate", "ActorBase", papyrusActorBase::GetTemplate, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("HasHeadPartOverlays", "ActorBase", papyrusActorBase::HasHeadPartOverlays, vm)); + + vm->RegisterFunction( + new NativeFunction1 , bool>("GetHeadParts", "ActorBase", papyrusActorBase::GetHeadParts, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetOutfit", "ActorBase", papyrusActorBase::GetOutfit, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetBodyWeight", "ActorBase", papyrusActorBase::SetBodyWeight, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetBodyWeight", "ActorBase", papyrusActorBase::GetBodyWeight, vm)); + + vm->SetFunctionFlags("ActorBase", "GetTemplate", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusActorBase.h b/f4se/f4se/PapyrusActorBase.h new file mode 100644 index 0000000..9b43ecf --- /dev/null +++ b/f4se/f4se/PapyrusActorBase.h @@ -0,0 +1,10 @@ +#pragma once + +#include "f4se/GameTypes.h" + +class VirtualMachine; + +namespace papyrusActorBase +{ + void RegisterFuncs(VirtualMachine* vm); +}; diff --git a/f4se/f4se/PapyrusArgs.cpp b/f4se/f4se/PapyrusArgs.cpp new file mode 100644 index 0000000..fc5f881 --- /dev/null +++ b/f4se/f4se/PapyrusArgs.cpp @@ -0,0 +1,490 @@ +#include "PapyrusArgs.h" + +#include "f4se/GameReferences.h" +#include "f4se/GameExtraData.h" + +template <> void PackValue (VMValue * dst, void * src, VirtualMachine * vm) +{ + dst->SetNone(); +} + +template <> void PackValue (VMValue * dst, UInt32 * src, VirtualMachine * vm) +{ + dst->SetInt(*src); +} + +template <> void PackValue (VMValue * dst, SInt32 * src, VirtualMachine * vm) +{ + dst->SetInt(*src); +} + +template <> void PackValue (VMValue * dst, float * src, VirtualMachine * vm) +{ + dst->SetFloat(*src); +} + +template <> void PackValue (VMValue * dst, bool * src, VirtualMachine * vm) +{ + dst->SetBool(*src); +} + +template <> void PackValue (VMValue * dst, BSFixedString * src, VirtualMachine * vm) +{ + dst->SetString(src->c_str()); +} + +template <> void PackValue (VMValue * dst, VMVariable * src, VirtualMachine * vm) +{ + src->PackVariable(dst); +} + +template <> void PackValue (VMValue * dst, VMObject * src, VirtualMachine * vm) +{ + src->PackObject(dst); +} + +template <> void PackValue (VMValue * dst, VMRefOrInventoryObj * src, VirtualMachine * vm) +{ + src->PackObject(dst); +} +template <> void PackValue (VMValue * dst, VMRefOrInventoryObj ** src, VirtualMachine * vm) +{ + (*src)->PackObject(dst); +} + +void BindID(VMIdentifier ** identifier, void * srcData, VirtualMachine * vm, IObjectHandlePolicy * handlePolicy, UInt32 typeID) +{ + UInt32 unk = 0; + + VMObjectTypeInfo * typeInfo = (*identifier)->m_typeInfo; + if(typeInfo) + typeInfo->AddRef(); + + if(vm->Unk_0C(&typeInfo->m_typeName, &unk)) + { + UInt64 handle = handlePolicy->Create(typeID, srcData); + + if( handlePolicy->IsType(unk, handle) || + (handle == handlePolicy->GetInvalidHandle())) + { + CALL_MEMBER_FN(vm->GetObjectBindPolicy(), BindObject)(identifier, handle); + } + } + + if(typeInfo) + typeInfo->Release(); +} + +bool GetIdentifier(VMValue * dst, UInt64 handle, VMObjectTypeInfo * typeInfo, VirtualMachine * vm) +{ + dst->SetNone(); + + if(!typeInfo) return false; + + IObjectHandlePolicy * handlePolicy = vm->GetHandlePolicy(); + VMIdentifier * identifier = NULL; + + // find existing identifier + if(vm->GetObjectIdentifier(handle, typeInfo->m_typeName, 0, &identifier, 0)) + { + // copy the identifier out + if(identifier) + { + VMValue tempValue; + tempValue.SetComplexType(typeInfo); + CALL_MEMBER_FN(dst, Set)(&tempValue); + dst->SetIdentifier(&identifier); + } + + // release our reference + if(identifier) + { + if(!identifier->DecrementLock()) + { + identifier->Destroy(); + } + } + + return true; + } + + return false; +} + +bool GetIdentifier(VMValue * dst, UInt64 handle, const BSFixedString * typeName, VirtualMachine * vm) +{ + VMObjectTypeInfo * typeInfo = nullptr; + + // get class info + if(vm->GetObjectTypeInfoByName(typeName, &typeInfo)) + if(typeInfo) + typeInfo->Release(); + + return GetIdentifier(dst, handle, typeInfo, vm); +} + +void PackHandle(VMValue * dst, void * src, UInt32 typeID, VirtualMachine * vm) +{ + dst->SetNone(); + + if(!src) return; + + VMObjectTypeInfo * typeInfo = nullptr; + + // get class info + if(vm->GetObjectTypeInfo(typeID, &typeInfo)) + if(typeInfo) + typeInfo->Release(); + + if(!typeInfo) return; + + IObjectHandlePolicy * handlePolicy = vm->GetHandlePolicy(); + + UInt64 handle = handlePolicy->Create(typeID, src); + VMIdentifier * identifier = NULL; + + // find existing identifier + if(!vm->GetObjectIdentifier(handle, typeInfo->m_typeName, 0, &identifier, 0)) + { + if(vm->CreateObjectIdentifier(&typeInfo->m_typeName, &identifier)) + { + if(identifier) + { + BindID(&identifier, src, vm, handlePolicy, typeID); + } + } + } + + // copy the identifier out + if(identifier) + { + VMValue tempValue; + tempValue.SetComplexType(typeInfo); + CALL_MEMBER_FN(dst, Set)(&tempValue); + dst->SetIdentifier(&identifier); + } + + // release our reference + if(identifier) + { + if(!identifier->DecrementLock()) + { + identifier->Destroy(); + } + } +} + +//// VMValue -> type + +template <> void UnpackValue (float * dst, VMValue * src) +{ + switch(src->type.value) + { + case VMValue::kType_Int: + *dst = src->data.i; + break; + + case VMValue::kType_Float: + *dst = src->data.f; + break; + + case VMValue::kType_Bool: + *dst = src->data.b; + break; + + default: + *dst = 0; + break; + } +} + +template <> void UnpackValue (UInt32 * dst, VMValue * src) +{ + switch(src->type.value) + { + case VMValue::kType_Int: + *dst = src->data.u; + break; + + case VMValue::kType_Float: + *dst = src->data.f; + break; + + case VMValue::kType_Bool: + *dst = src->data.b; + break; + + default: + *dst = 0; + break; + } +} + +template <> void UnpackValue (SInt32 * dst, VMValue * src) +{ + switch(src->type.value) + { + case VMValue::kType_Int: + *dst = src->data.u; + break; + + case VMValue::kType_Float: + *dst = src->data.f; + break; + + case VMValue::kType_Bool: + *dst = src->data.b; + break; + + default: + *dst = 0; + break; + } +} + +template <> void UnpackValue (bool * dst, VMValue * src) +{ + switch(src->type.value) + { + case VMValue::kType_Int: + *dst = src->data.u != 0; + break; + + case VMValue::kType_Float: + *dst = src->data.f != 0; + break; + + case VMValue::kType_Bool: + *dst = src->data.b; + break; + + default: + *dst = 0; + break; + } +} + +template <> void UnpackValue (BSFixedString * dst, VMValue * src) +{ + StringCache::Entry * entry = NULL; + + if(src->type.value == VMValue::kType_String) + entry = src->data.str; + + CALL_MEMBER_FN(dst, Set)(entry ? entry->Get() : ""); +} + +template <> void UnpackValue (VMVariable * dst, VMValue * src) +{ + dst->UnpackVariable(src); +} + +template <> void UnpackValue (VMObject * dst, VMValue * src) +{ + dst->UnpackObject(src); +} + +template <> void UnpackValue (VMRefOrInventoryObj * dst, VMValue * src) +{ + dst->UnpackObject(src); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, VMValue::kType_IntArray); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, VMValue::kType_IntArray); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, VMValue::kType_FloatArray); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, VMValue::kType_BoolArray); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, VMValue::kType_StringArray); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, VMValue::kType_VariableArray); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, GetTypeID>((*g_gameVM)->m_virtualMachine)); +} + +template <> void UnpackValue >(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, GetTypeID>((*g_gameVM)->m_virtualMachine)); +} + +template <> void UnpackValue(VMObject ** dst, VMValue * src) +{ + if(src->IsIdentifier() && src->data.id) + { + *dst = new VMObject; + UnpackValue(*dst, src); + } +} + +template <> void DestroyValue(VMObject ** dst) +{ + if(*dst) + delete (*dst); +} + +template <> void UnpackValue(VMRefOrInventoryObj ** dst, VMValue * src) +{ + if(src->IsIdentifier() && src->data.id) + { + *dst = new VMRefOrInventoryObj; + UnpackValue(*dst, src); + } +} + +template <> void DestroyValue(VMRefOrInventoryObj ** dst) +{ + if(*dst) + delete (*dst); +} + +void * UnpackHandle(VMValue * src, UInt32 typeID) +{ + if(!src->IsIdentifier()) return NULL; + if(!src->data.id) return NULL; + + UInt64 handle = src->data.id->GetHandle(); + + if(!(*g_objectHandlePolicy)->IsType(typeID, handle)) return NULL; + if(!(*g_objectHandlePolicy)->Unk_03(handle)) return NULL; + + return (*g_objectHandlePolicy)->Resolve(typeID, handle); +} + +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_None; } +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_Int; } +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_Int; } +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_Int; } +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_Float; } +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_Bool; } +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_String; } +template <> UInt64 GetTypeID (VirtualMachine * vm) { return VMValue::kType_Variable; } + +template <> UInt64 GetTypeID >(VirtualMachine * vm) { return VMValue::kType_IntArray; } +template <> UInt64 GetTypeID >(VirtualMachine * vm) { return VMValue::kType_IntArray; } +template <> UInt64 GetTypeID >(VirtualMachine * vm) { return VMValue::kType_IntArray; } +template <> UInt64 GetTypeID >(VirtualMachine * vm) { return VMValue::kType_FloatArray; } +template <> UInt64 GetTypeID >(VirtualMachine * vm) { return VMValue::kType_BoolArray; } +template <> UInt64 GetTypeID >(VirtualMachine * vm) { return VMValue::kType_StringArray; } +template <> UInt64 GetTypeID >(VirtualMachine * vm) { return VMValue::kType_VariableArray; } + +template <> UInt64 GetTypeID (VirtualMachine * vm) +{ + return GetTypeIDFromFormTypeID(VMObject::kTypeID, vm); +} + +template <> UInt64 GetTypeID >(VirtualMachine * vm) +{ + return GetTypeIDFromFormTypeID(VMObject::kTypeID, vm) | VMValue::kType_Identifier; +} + +template <> UInt64 GetTypeID (VirtualMachine * vm) +{ + return GetTypeIDFromFormTypeID(VMRefOrInventoryObj::kTypeID, vm); +} + +template <> UInt64 GetTypeID >(VirtualMachine * vm) +{ + return GetTypeIDFromFormTypeID(VMRefOrInventoryObj::kTypeID, vm) | VMValue::kType_Identifier; +} + +UInt64 GetTypeIDFromFormTypeID(UInt32 formTypeID, VirtualMachine * vm) +{ + UInt64 result = 0; + VMObjectTypeInfo * typeInfo = nullptr; + if(vm->GetObjectTypeInfo(formTypeID, &typeInfo)) + { + result = (UInt64)typeInfo; + typeInfo->Release(); + } + + return result; +} + +UInt64 GetTypeIDFromStructName(const char * name, VirtualMachine * vm) +{ + UInt64 result = 0; + VMStructTypeInfo * typeInfo = nullptr; + BSFixedString structName(name); + if(vm->GetStructTypeInfo(&structName, &typeInfo)) + { + result = (UInt64)typeInfo; + typeInfo->Release(); + } + + return result; +} + +TESObjectREFR * VMRefOrInventoryObj::GetObjectReference() +{ + return m_refData.refr; +} + +TESObjectREFR * VMRefOrInventoryObj::GetOwner() +{ + return m_refData.owner; +} + +bool VMRefOrInventoryObj::GetExtraData(TESForm ** baseForm, ExtraDataList ** extraData) +{ + TESObjectREFR * refr = m_refData.refr; + if(refr) { + *baseForm = refr->baseForm; + *extraData = refr->extraDataList; + return true; + } + + TESObjectREFR * owner = m_refData.owner; + if(!owner) + return false; + + BGSInventoryList * inventoryList = owner->inventoryList; + if(!inventoryList) + return false; + + for(UInt32 i = 0; i < inventoryList->items.count; i++) + { + BGSInventoryItem item; + inventoryList->items.GetNthItem(i, item); + if(!item.stack) + continue; + + item.stack->Visit([&](BGSInventoryItem::Stack * stack) + { + ExtraDataList * stackDataList = stack->extraData; + if(stackDataList) { + ExtraUniqueID * extraUID = static_cast(stackDataList->GetByType(kExtraData_UniqueID)); + if(extraUID && extraUID->uniqueId == m_refData.uniqueId) { + *baseForm = item.form; + *extraData = stackDataList; + return false; + } + } + + return true; + }); + } + + return (*baseForm) != nullptr; +} diff --git a/f4se/f4se/PapyrusArgs.h b/f4se/f4se/PapyrusArgs.h new file mode 100644 index 0000000..8161acf --- /dev/null +++ b/f4se/f4se/PapyrusArgs.h @@ -0,0 +1,523 @@ +#pragma once + +#include "f4se/GameForms.h" +#include "f4se/GameTypes.h" +#include "f4se/PapyrusVM.h" +#include "f4se_common/Utilities.h" +#include "f4se/PapyrusStruct.h" + +#include + +class VMState; +class VMValue; +class VMArgList; + +struct StaticFunctionTag; + +class VMState +{ +public: + VMState(); + ~VMState(); + + VMArgList * argList; // 00 + UInt64 pad08[(0x38 - 0x08) >> 2]; // 08 + UInt32 numArgs; // 38 +}; + +class VMArgList +{ +public: + MEMBER_FN_PREFIX(VMArgList); + DEFINE_MEMBER_FN(GetOffset, UInt32, 0x02704000, VMState * state); + // 4C8A9FE3A95B9DED322C9C0A78312E29F0A2CC7A+5B + DEFINE_MEMBER_FN(Get, VMValue *, 0x02704060, VMState * state, UInt32 idx, UInt32 offset); +}; + +template +class VMArray +{ +public: + VMArray() : m_arr(nullptr), m_none(false) { } + ~VMArray() { } + + enum { kTypeID = 0 }; + + VMArray(std::vector & vec) : m_arr(nullptr), m_none(false) + { + for(auto & v : vec) + { + Push(&v); + } + } + + VMArray & operator=(std::vector & vec) + { + Clear(); + + for(auto & v : vec) + { + Push(&v); + } + return *this; + } + + UInt32 Length() const + { + return m_data.size(); + } + void Get(T * dst, const UInt32 idx) + { + UnpackValue(dst, &m_data[idx]); + } + void Set(T * src, const UInt32 idx, bool bReference = true) + { + PackValue(&m_data[idx], src, (*g_gameVM)->m_virtualMachine); + if(m_arr && bReference) + PackValue(&m_arr->arr.entries[idx], src, (*g_gameVM)->m_virtualMachine); + } + void Push(T * src, bool bReference = true) + { + VMValue tmp; + PackValue(&tmp, src, (*g_gameVM)->m_virtualMachine); + m_data.push_back(tmp); + if(m_arr && bReference) + { + m_arr->arr.Push(tmp); + } + } + void Remove(const UInt32 idx, bool bReference = true) + { + m_data.erase(m_data.begin() + idx); + if(m_arr && bReference) + { + m_arr->arr.Remove(idx); + } + } + + void Resize(const UInt32 size) + { + m_data.resize(size); + if(m_arr) + m_arr->arr.Resize(size); + } + + void Clear() + { + m_data.clear(); + if(m_arr) + m_arr->arr.Clear(); + } + + void PackArray(VMValue * dst, VirtualMachine * vm) + { + // Clear out old contents if any + dst->SetNone(); + dst->type.value = GetTypeID>(vm); // Always set the type + + if(m_data.size() > 0 && !m_none) + { + VMValue::ArrayData * data = nullptr; + // Request the VM allocate a new array + vm->CreateArray(dst, m_data.size(), &data); + if(data) { + // Set the appropriate TypeID and assign the new data array + dst->data.arr = data; + + // Copy from vector + for(int i = 0; i < data->arr.count; ++i) + { + data->arr.entries[i] = m_data[i]; + } + } + } + + // Clear the temp contents of the reference array + m_data.clear(); + } + + void UnpackArray(VMValue * src, const UInt64 type) + { + VMValue::ArrayData * arrData; + + if (src->type.value != type || (arrData = src->data.arr, !arrData)) + { + m_none = true; + m_arr = nullptr; + return; + } + + m_arr = arrData; + + m_data.resize(arrData->arr.count); + for(int i = 0; i < arrData->arr.count; ++i) + { + // Copy into vector + m_data[i] = arrData->arr.entries[i]; + } + } + + // Will make the VM return None instead of a zero sized array + void SetNone(bool bNone) { m_none = bNone; } + bool IsNone() const { return m_none; } + +protected: + VMValue::ArrayData * m_arr; // Original reference + std::vector m_data; // Temporary copies + bool m_none; +}; + +class VMVariable +{ +public: + VMVariable() : m_var(nullptr) { } + ~VMVariable() { } + + template + void Set(T * src, bool bReference = true) + { + PackValue(&m_value, src, (*g_gameVM)->m_virtualMachine); + if(m_var && bReference) + PackValue(m_var, src, (*g_gameVM)->m_virtualMachine); + } + + // Fails on invalid type unpack + template + bool Get(T * dst) + { + if(Is()) + { + UnpackValue(dst, &m_value); + return true; + } + + return false; + } + + // Skips type-check + template + T As() + { + T tmp; + UnpackValue(&tmp, &m_value); + return tmp; + } + + template + bool Is() + { + return m_value.type.value == GetTypeID((*g_gameVM)->m_virtualMachine); + } + + void PackVariable(VMValue * dst) + { + VMValue * newValue = new VMValue(m_value); + dst->SetVariable(newValue); + } + + void UnpackVariable(VMValue * value) + { + m_var = value->data.var; + m_value = *m_var; + } + + bool IsNone() + { + return m_value.GetTypeEnum() == 0; + } + + // Provides direct access to the VM data, advanced use only + VMValue & GetValue() + { + return m_value; + } + +protected: + VMValue * m_var; // Original reference + VMValue m_value; // Copied data +}; + +class VMObject +{ +public: + enum { kTypeID = -1 }; + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free); + + template + void Set(T * src) + { + PackValue(&m_value, src, (*g_gameVM)->m_virtualMachine); + } + + template + bool Get(T * dst) + { + if(Is()) + { + UnpackValue(dst, &m_value); + return true; + } + + return false; + } + + template + bool Is() + { + if(m_value.IsIdentifier() && m_value.data.id) { + UInt64 handle = m_value.data.id->GetHandle(); + + typedef std::remove_pointer ::type BaseType; + return (*g_objectHandlePolicy)->IsType(BaseType::kTypeID, handle); + } + + return false; + } + + UInt64 GetHandle() + { + if(m_value.IsIdentifier() && m_value.data.id) { + return m_value.data.id->GetHandle(); + } + + return (*g_objectHandlePolicy)->GetInvalidHandle(); + } + + BSFixedString GetObjectType() + { + if(m_value.IsIdentifier() && m_value.data.id && m_value.data.id->m_typeInfo) { + VMIdentifier * id = m_value.data.id; + if(id) { + IComplexType * type = m_value.data.id->m_typeInfo; + if(type) + return type->m_typeName; + } + } + + return BSFixedString("ScriptObject"); + } + + void PackObject(VMValue * dst) + { + *dst = m_value; + } + + void UnpackObject(VMValue * value) + { + m_value = *value; + } + +protected: + VMValue m_value; +}; + +class VMRefOrInventoryObj +{ +public: + enum { kTypeID = kFormType_REFR }; + + TESObjectREFR * GetObjectReference(); + TESObjectREFR * GetOwner(); + bool GetExtraData(TESForm ** baseForm, ExtraDataList ** extraData); + + void PackObject(VMValue * dst) + { + *dst = m_value; + } + + void UnpackObject(VMValue * value) + { + if(value->IsIdentifier() && value->data.id) { + UInt64 handle = value->data.id->GetHandle(); + GetRefFromHandle(&m_refData, handle); + } else { + m_refData.owner = nullptr; + m_refData.refr = nullptr; + m_refData.uniqueId = 0; + } + m_value = *value; + } + +protected: + VMValue m_value; + VMRefHandle m_refData; +}; + +template +void UnpackValue(VMArray * dst, VMValue * src) +{ + UnpackArray(dst, src, GetTypeID>((*g_gameVM)->m_virtualMachine)); +} + +template +void PackValue(VMValue * dst, T * src, VirtualMachine * vm); + +template +void PackValue(VMValue * dst, VMArray * src, VirtualMachine * vm) +{ + src->PackArray(dst, vm); +} + +template +void UnpackValue(T * dst, VMValue * src); + +template +UInt64 GetTypeID(VirtualMachine * vm); + +template <> void PackValue (VMValue * dst, void * src, VirtualMachine * registry); +template <> void PackValue (VMValue * dst, UInt32 * src, VirtualMachine * registry); +template <> void PackValue (VMValue * dst, SInt32 * src, VirtualMachine * registry); +template <> void PackValue (VMValue * dst, float * src, VirtualMachine * vm); +template <> void PackValue (VMValue * dst, bool * src, VirtualMachine * vm); +template <> void PackValue (VMValue * dst, BSFixedString * src, VirtualMachine * vm); +template <> void PackValue (VMValue * dst, VMVariable * src, VirtualMachine * vm); +template <> void PackValue (VMValue * dst, VMObject * src, VirtualMachine * vm); +template <> void PackValue (VMValue * dst, VMRefOrInventoryObj * src, VirtualMachine * vm); + +void PackHandle(VMValue * dst, void * src, UInt32 typeID, VirtualMachine * registry); + +bool GetIdentifier(VMValue * dst, UInt64 handle, VMObjectTypeInfo * typeInfo, VirtualMachine * vm); +bool GetIdentifier(VMValue * dst, UInt64 handle, const BSFixedString * typeName, VirtualMachine * vm); + +template +void PackValue(VMValue * dst, T ** src, VirtualMachine * vm) +{ + typedef std::remove_pointer ::type BaseType; + PackHandle(dst, *src, BaseType::kTypeID, vm); +} +template <> void PackValue (VMValue * dst, VMRefOrInventoryObj ** src, VirtualMachine * vm); + +template <> void UnpackValue (float * dst, VMValue * src); +template <> void UnpackValue (UInt32 * dst, VMValue * src); +template <> void UnpackValue (SInt32 * dst, VMValue * src); +template <> void UnpackValue (bool * dst, VMValue * src); +template <> void UnpackValue (BSFixedString * dst, VMValue * src); + +template <> void UnpackValue >(VMArray * dst, VMValue * src); +template <> void UnpackValue >(VMArray * dst, VMValue * src); +template <> void UnpackValue >(VMArray * dst, VMValue * src); +template <> void UnpackValue >(VMArray * dst, VMValue * src); +template <> void UnpackValue >(VMArray * dst, VMValue * src); +template <> void UnpackValue >(VMArray * dst, VMValue * src); +template <> void UnpackValue >(VMArray * dst, VMValue * src); + +void * UnpackHandle(VMValue * src, UInt32 typeID); + +template +void UnpackValue(T ** dst, VMValue * src) +{ + *dst = (T *)UnpackHandle(src, T::kTypeID); +} + +template +void DestroyValue(T ** dst) +{ + // Dummy implementation, used by VMObject to destroy the temp pointer +} + +template <> void UnpackValue(VMObject ** dst, VMValue * src); +template <> void DestroyValue(VMObject ** dst); + +template <> void UnpackValue(VMRefOrInventoryObj ** dst, VMValue * src); +template <> void DestroyValue(VMRefOrInventoryObj ** dst); + +template +void UnpackArray(VMArray * dst, VMValue * src, const UInt64 type) +{ + dst->UnpackArray(src, type); +} + +UInt64 GetTypeIDFromFormTypeID(UInt32 formTypeID, VirtualMachine * vm); +UInt64 GetTypeIDFromStructName(const char * name, VirtualMachine * vm); + +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); +template <> UInt64 GetTypeID (VirtualMachine * vm); + +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); +template <> UInt64 GetTypeID >(VirtualMachine * vm); + +template +struct IsObjectType +{ + static const bool value = false; +}; + +template<> +struct IsObjectType +{ + static const bool value = true; +}; + + +template +struct IsArrayType +{ + enum { value = 0 }; + typedef T TypedArg; +}; + +template +struct IsArrayType> +{ + enum { value = 1 }; + typedef T TypedArg; +}; + +template +UInt64 GetTypeID (VirtualMachine * vm) +{ + UInt64 result; + + if(IsArrayType::value) + { + typedef IsArrayType::TypedArg BaseType; + if(IsStructType::value) + { + result = GetTypeIDFromStructName(IsStructType::name(), vm) | VMValue::kType_Identifier; + } + else if(std::is_pointer::value) + { + typedef std::remove_pointer ::type ObjectType; + result = GetTypeIDFromFormTypeID(ObjectType::kTypeID, vm) | VMValue::kType_Identifier; + } + } + else if(IsStructType::value) + { + result = GetTypeIDFromStructName(IsStructType::name(), vm); + } + else if(std::is_pointer::value) + { + typedef std::remove_pointer ::type ObjectType; + result = GetTypeIDFromFormTypeID(ObjectType::kTypeID, vm); + } + + return result; +} + +template +struct IsStaticType +{ + enum { value = 0 }; +}; + +template <> +struct IsStaticType +{ + enum { value = 1 }; +}; diff --git a/f4se/f4se/PapyrusArmor.cpp b/f4se/f4se/PapyrusArmor.cpp new file mode 100644 index 0000000..7045768 --- /dev/null +++ b/f4se/f4se/PapyrusArmor.cpp @@ -0,0 +1,31 @@ +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/GameObjects.h" +#include "f4se/PapyrusArmor.h" + +namespace papyrusArmor +{ + VMArray GetArmorAddons(TESObjectARMO* thisArmor) + { + VMArray results; + if(!thisArmor) + return results; + + for(UInt32 i = 0; i < thisArmor->addons.count; ++i) + { + TESObjectARMO::ArmorAddons addon; + if(thisArmor->addons.GetNthItem(i, addon)) + results.Push(&addon.armorAddon); + } + + return results; + } +} + +void papyrusArmor::RegisterFuncs(VirtualMachine* vm) +{ + // Armor Addons + vm->RegisterFunction( + new NativeFunction0 >("GetArmorAddons", "Armor", papyrusArmor::GetArmorAddons, vm)); +} diff --git a/f4se/f4se/PapyrusArmor.h b/f4se/f4se/PapyrusArmor.h new file mode 100644 index 0000000..ace8f0a --- /dev/null +++ b/f4se/f4se/PapyrusArmor.h @@ -0,0 +1,8 @@ +#pragma once + +class VirtualMachine; + +namespace papyrusArmor +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusArmorAddon.cpp b/f4se/f4se/PapyrusArmorAddon.cpp new file mode 100644 index 0000000..25dd0de --- /dev/null +++ b/f4se/f4se/PapyrusArmorAddon.cpp @@ -0,0 +1,33 @@ +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" +#include "f4se/PapyrusArmorAddon.h" + +#include "f4se/GameObjects.h" + +namespace papyrusArmorAddon +{ + VMArray GetAdditionalRaces(TESObjectARMA* thisArmorAddon) + { + VMArray results; + if(!thisArmorAddon) + return results; + + for(UInt32 i = 0; i < thisArmorAddon->additionalRaces.count; ++i) + { + TESRace * race = nullptr; + if(thisArmorAddon->additionalRaces.GetNthItem(i, race)) + results.Push(&race); + } + + return results; + } +} + +void papyrusArmorAddon::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterForm(TESObjectARMA::kTypeID, "ArmorAddon"); + + // Races + vm->RegisterFunction( + new NativeFunction0 >("GetAdditionalRaces", "ArmorAddon", papyrusArmorAddon::GetAdditionalRaces, vm)); +} diff --git a/f4se/f4se/PapyrusArmorAddon.h b/f4se/f4se/PapyrusArmorAddon.h new file mode 100644 index 0000000..268164f --- /dev/null +++ b/f4se/f4se/PapyrusArmorAddon.h @@ -0,0 +1,8 @@ +#pragma once + +class VirtualMachine; + +namespace papyrusArmorAddon +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusCell.cpp b/f4se/f4se/PapyrusCell.cpp new file mode 100644 index 0000000..b56d929 --- /dev/null +++ b/f4se/f4se/PapyrusCell.cpp @@ -0,0 +1,39 @@ +#include "f4se/PapyrusCell.h" + +#include "f4se/GameExtraData.h" +#include "f4se/GameForms.h" +#include "f4se/GameRTTI.h" + +namespace papyrusCell +{ + TESWaterForm * GetWaterType(TESObjectCELL* thisCell) + { + if(!thisCell) + return nullptr; + + // If there is a water override there should be one in its ExtraData + if((thisCell->flags & TESObjectCELL::kFlag_HasWater) == TESObjectCELL::kFlag_HasWater) { + auto pExtraData = thisCell->extraDataList; + if(pExtraData) { + ExtraCellWaterType * extraWaterType = (ExtraCellWaterType *)pExtraData->GetByType(kExtraData_CellWaterType); + if(extraWaterType) + return extraWaterType->waterForm; + } + } + + // Exteriors have implied water, the default water if theres no override + if((thisCell->flags & TESObjectCELL::kFlag_IsInterior) != TESObjectCELL::kFlag_IsInterior) + return DYNAMIC_CAST(LookupFormByID(0x18), TESForm, TESWaterForm); + + return nullptr; + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusCell::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("GetWaterType", "Cell", papyrusCell::GetWaterType, vm)); +} diff --git a/f4se/f4se/PapyrusCell.h b/f4se/f4se/PapyrusCell.h new file mode 100644 index 0000000..9bfbf9c --- /dev/null +++ b/f4se/f4se/PapyrusCell.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusCell +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusComponent.cpp b/f4se/f4se/PapyrusComponent.cpp new file mode 100644 index 0000000..96be296 --- /dev/null +++ b/f4se/f4se/PapyrusComponent.cpp @@ -0,0 +1,51 @@ +#include "f4se/papyrusComponent.h" + +#include "f4se/GameExtraData.h" +#include "f4se/GameForms.h" +#include "f4se/GameObjects.h" +#include "f4se/GameRTTI.h" + +namespace papyrusComponent +{ + TESObjectMISC * GetScrapItem(BGSComponent * thisObject) + { + return thisObject ? thisObject->scrapItem : nullptr; + } + + void SetScrapItem(BGSComponent * thisObject, TESObjectMISC * scrapItem) + { + if(thisObject && scrapItem) { + thisObject->scrapItem = scrapItem; + } + } + + TESGlobal * GetScrapScalar(BGSComponent * thisObject) + { + return thisObject ? thisObject->scrapScalar : nullptr; + } + + void SetScrapScalar(BGSComponent * thisObject, TESGlobal * scrapScalar) + { + if(thisObject && scrapScalar) { + thisObject->scrapScalar = scrapScalar; + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusComponent::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("GetScrapItem", "Component", papyrusComponent::GetScrapItem, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetScrapItem", "Component", papyrusComponent::SetScrapItem, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetScrapScalar", "Component", papyrusComponent::GetScrapScalar, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetScrapScalar", "Component", papyrusComponent::SetScrapScalar, vm)); +} diff --git a/f4se/f4se/PapyrusComponent.h b/f4se/f4se/PapyrusComponent.h new file mode 100644 index 0000000..b03b9b4 --- /dev/null +++ b/f4se/f4se/PapyrusComponent.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusComponent +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusConstructibleObject.cpp b/f4se/f4se/PapyrusConstructibleObject.cpp new file mode 100644 index 0000000..0744de5 --- /dev/null +++ b/f4se/f4se/PapyrusConstructibleObject.cpp @@ -0,0 +1,142 @@ +#include "f4se/PapyrusConstructibleObject.h" + +#include "f4se/PapyrusArgs.h" +#include "f4se/PapyrusStruct.h" + +#include "f4se/GameExtraData.h" +#include "f4se/GameForms.h" +#include "f4se/GameObjects.h" +#include "f4se/GameRTTI.h" + +namespace papyrusConstructibleObject +{ + DECLARE_STRUCT(ConstructibleComponent, "ConstructibleObject") + + VMArray GetConstructibleComponents(BGSConstructibleObject * thisObject) + { + VMArray result; + if(!thisObject) + return result; + + if(!thisObject->components) + return result; + + for(UInt32 i = 0; i < thisObject->components->count; i++) + { + BGSConstructibleObject::Component cp; + thisObject->components->GetNthItem(i, cp); + + ConstructibleComponent comp; + comp.Set("object", cp.component); + comp.Set("count", cp.count); + result.Push(&comp); + } + + return result; + } + + void SetConstructibleComponents(BGSConstructibleObject * thisObject, VMArray components) + { + if(thisObject) { + if(!thisObject->components) + thisObject->components = new tArray(); + + thisObject->components->Clear(); + + for(UInt32 i = 0; i < components.Length(); i++) + { + ConstructibleComponent comp; + components.Get(&comp, i); + + BGSConstructibleObject::Component cp; + comp.Get("object", &cp.component); + comp.Get("count", &cp.count); + thisObject->components->Push(cp); + } + } + } + + TESForm * GetCreatedObject(BGSConstructibleObject * thisObject) + { + return thisObject ? thisObject->createdObject : nullptr; + } + + void SetCreatedObject(BGSConstructibleObject * thisObject, TESForm * form) + { + if(thisObject && form) { + thisObject->createdObject = form; + } + } + + UInt32 GetCreatedCount(BGSConstructibleObject * thisObject) + { + return thisObject ? thisObject->createdCount : 0; + } + + void SetCreatedCount(BGSConstructibleObject * thisObject, UInt32 count) + { + if(thisObject && count >= 0) { + thisObject->createdCount = count; + } + } + + UInt32 GetPriority(BGSConstructibleObject * thisObject) + { + return thisObject ? thisObject->priority : 0; + } + + void SetPriority(BGSConstructibleObject * thisObject, UInt32 priority) + { + if(thisObject) { + thisObject->priority = priority; + } + } + + BGSKeyword * GetWorkbenchKeyword(BGSConstructibleObject * thisObject) + { + return thisObject ? thisObject->workbenchKeyword : nullptr; + } + + void SetWorkbenchKeyword(BGSConstructibleObject * thisObject, BGSKeyword * keyword) + { + if(thisObject && keyword) { + thisObject->workbenchKeyword = keyword; + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusConstructibleObject::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 >("GetConstructibleComponents", "ConstructibleObject", papyrusConstructibleObject::GetConstructibleComponents, vm)); + + vm->RegisterFunction( + new NativeFunction1 >("SetConstructibleComponents", "ConstructibleObject", papyrusConstructibleObject::SetConstructibleComponents, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetCreatedObject", "ConstructibleObject", papyrusConstructibleObject::GetCreatedObject, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetCreatedObject", "ConstructibleObject", papyrusConstructibleObject::SetCreatedObject, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetCreatedCount", "ConstructibleObject", papyrusConstructibleObject::GetCreatedCount, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetCreatedCount", "ConstructibleObject", papyrusConstructibleObject::SetCreatedCount, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetPriority", "ConstructibleObject", papyrusConstructibleObject::GetPriority, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetPriority", "ConstructibleObject", papyrusConstructibleObject::SetPriority, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetWorkbenchKeyword", "ConstructibleObject", papyrusConstructibleObject::GetWorkbenchKeyword, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetWorkbenchKeyword", "ConstructibleObject", papyrusConstructibleObject::SetWorkbenchKeyword, vm)); +} diff --git a/f4se/f4se/PapyrusConstructibleObject.h b/f4se/f4se/PapyrusConstructibleObject.h new file mode 100644 index 0000000..d98a8e7 --- /dev/null +++ b/f4se/f4se/PapyrusConstructibleObject.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusConstructibleObject +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusDefaultObject.cpp b/f4se/f4se/PapyrusDefaultObject.cpp new file mode 100644 index 0000000..540261e --- /dev/null +++ b/f4se/f4se/PapyrusDefaultObject.cpp @@ -0,0 +1,40 @@ +#include "f4se/PapyrusDefaultObject.h" + +#include "f4se/GameData.h" +#include "f4se/GameForms.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +namespace papyrusDefaultObject +{ + TESForm * GetDefaultObject(StaticFunctionTag*, BSFixedString name) + { + return (*g_defaultObjectMap)->GetDefaultObject(name); + } + + TESForm * Get(BGSDefaultObject * defaultObject) + { + return defaultObject ? defaultObject->form : nullptr; + } + + void Set(BGSDefaultObject * defaultObject, TESForm * form) + { + if(defaultObject) + defaultObject->form = form; + } +} + +void papyrusDefaultObject::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterForm(BGSDefaultObject::kTypeID, "DefaultObject"); + + vm->RegisterFunction( + new NativeFunction1 ("GetDefaultObject", "DefaultObject", papyrusDefaultObject::GetDefaultObject, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("Get", "DefaultObject", papyrusDefaultObject::Get, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("Set", "DefaultObject", papyrusDefaultObject::Set, vm)); +} \ No newline at end of file diff --git a/f4se/f4se/PapyrusDefaultObject.h b/f4se/f4se/PapyrusDefaultObject.h new file mode 100644 index 0000000..9b256ab --- /dev/null +++ b/f4se/f4se/PapyrusDefaultObject.h @@ -0,0 +1,10 @@ +#pragma once + +#include "f4se/GameTypes.h" + +class VirtualMachine; + +namespace papyrusDefaultObject +{ + void RegisterFuncs(VirtualMachine* vm); +}; diff --git a/f4se/f4se/PapyrusDelayFunctors.cpp b/f4se/f4se/PapyrusDelayFunctors.cpp new file mode 100644 index 0000000..618ab52 --- /dev/null +++ b/f4se/f4se/PapyrusDelayFunctors.cpp @@ -0,0 +1,569 @@ +#include "f4se/PapyrusDelayFunctors.h" +#include "f4se/PapyrusObjects.h" + +#include + +#include "f4se/GameAPI.h" +#include "f4se/PapyrusVM.h" +#include "f4se/Serialization.h" + +/// +/// LatentF4SEDelayFunctor +/// + +LatentF4SEDelayFunctor::LatentF4SEDelayFunctor(UInt32 stackId) : + stackId_( stackId ) +{} + +LatentF4SEDelayFunctor::LatentF4SEDelayFunctor(SerializationTag) : + stackId_( 0 ) +{} + +bool LatentF4SEDelayFunctor::ShouldReschedule(SInt32& delayMSOut) +{ + return false; +} + +bool LatentF4SEDelayFunctor::ShouldResumeStack(UInt32& stackIdOut) +{ + stackIdOut = stackId_; + return true; +} + +bool LatentF4SEDelayFunctor::Save(const F4SESerializationInterface* intfc) +{ + using namespace Serialization; + + WriteData(intfc, &stackId_); + + //_MESSAGE("Serialized STACKID %d", stackId_); + + return true; +} + +bool LatentF4SEDelayFunctor::Load(const F4SESerializationInterface* intfc, UInt32 version) +{ + using namespace Serialization; + + if (! ReadData(intfc, &stackId_)) + return false; + + //_MESSAGE("De-serialized STACKID %d", stackId_); + + return true; +} +/// +/// F4SEDelayFunctorQueue +/// + +F4SEDelayFunctorQueue::~F4SEDelayFunctorQueue() +{ + ClearAndRelease(); +} + +void F4SEDelayFunctorQueue::Push(IF4SEDelayFunctor* func) +{// lock_ + IScopedCriticalSection scopedLock( &lock_ ); + + data_.push_back(func); +}// ~lock_ + +IF4SEDelayFunctor* F4SEDelayFunctorQueue::Pop() +{ + IF4SEDelayFunctor* result = NULL; + + {// lock_ + IScopedCriticalSection scopedLock( &lock_ ); + + if (! data_.empty()) + { + result = data_.front(); + data_.pop_front(); + } + }// ~lock_ + + return result; +} + +void F4SEDelayFunctorQueue::ClearAndRelease() +{ + for (DataT::iterator it = data_.begin(); it != data_.end(); ++it) + delete *it; + + data_.clear(); +} + +bool F4SEDelayFunctorQueue::Save(const F4SESerializationInterface* intfc) +{ + using namespace Serialization; + + // Save data + UInt32 dataSize = data_.size(); + if (! WriteData(intfc,&dataSize)) + return false; + + for (UInt32 i=0; iClassName()); + if (factory == NULL) + { + continue; + } + + // Wront type? Can't happen but who knows. + IF4SEDelayFunctor* functor = obj->GetAsDelayFunctor(); + if (functor == NULL) + { + // Throw the loaded object away. + factory->Free(obj); + continue; + } + + data_.push_back(functor); + } + + return true; +} + +/// +/// F4SEDelayFunctorWaitList +/// + +F4SEDelayFunctorWaitList::F4SEDelayFunctorWaitList() : + lastTickTime_( GetPerfCounter() ) +{ + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + freq.QuadPart /= 1000; + + msToCountMult_ = freq.QuadPart; +} + +F4SEDelayFunctorWaitList::~F4SEDelayFunctorWaitList() +{ + ClearAndRelease(); +} + +void F4SEDelayFunctorWaitList::Add(SInt32 delayMS, IF4SEDelayFunctor* func) +{// inLock_ + IScopedCriticalSection scopedLock( &inLock_ ); + + WaitEntryT t( msToCountMult_ * delayMS, func ); + inData_.push_back(t); +}// ~inLock_ + +void F4SEDelayFunctorWaitList::Update() +{ + // Move items from thread-safe input buffer to non-thread safe main queue + + {// inLock_ + IScopedCriticalSection scopedLock( &inLock_ ); + + waitData_.insert(waitData_.end(), inData_.begin(), inData_.end()); + inData_.clear(); + }// ~inLock + + SInt64 curTime = GetPerfCounter(); + SInt64 dt = curTime - lastTickTime_; + + lastTickTime_ = curTime; + + if (dt <= 0) + return; + + // Decrement wait times by time delta + for (WaitDataT::iterator it = waitData_.begin(); it != waitData_.end(); ++it) + if (it->first > 0) + it->first -= dt; + + struct IsStillWaiting_ + { + bool operator()(const WaitEntryT& e) { return e.first > 0; } + }; + IsStillWaiting_ pred; + + // Swap all ready entries to end of the vector#1, add them to vector#2, and truncate #1 + WaitDataT::iterator r = std::partition(waitData_.begin(), waitData_.end(), pred); + + for (WaitDataT::iterator it = r; it != waitData_.end(); ++it) + readyData_.push_back(it->second ); + + waitData_.resize(std::distance(waitData_.begin(), r)); +} + +IF4SEDelayFunctor* F4SEDelayFunctorWaitList::PopReady() +{ + IF4SEDelayFunctor* result = NULL; + + if (! readyData_.empty()) + { + result = readyData_.back(); + readyData_.pop_back(); + } + + return result; +} + +void F4SEDelayFunctorWaitList::ClearAndRelease() +{ + for (WaitDataT::iterator it = inData_.begin(); it != inData_.end(); ++it) + { + const IF4SEObjectFactory* factory = F4SEObjectRegistryInstance().GetFactoryByName(it->second->ClassName()); + if (factory == NULL) + { + continue; + } + + factory->Free(it->second); + } + + inData_.clear(); + + for (WaitDataT::iterator it = waitData_.begin(); it != waitData_.end(); ++it) + { + const IF4SEObjectFactory* factory = F4SEObjectRegistryInstance().GetFactoryByName(it->second->ClassName()); + if (factory == NULL) + { + continue; + } + + factory->Free(it->second); + } + + waitData_.clear(); + + for (ReadyDataT::iterator it = readyData_.begin(); it != readyData_.end(); ++it) + { + const IF4SEObjectFactory* factory = F4SEObjectRegistryInstance().GetFactoryByName((*it)->ClassName()); + if (factory == NULL) + { + continue; + } + factory->Free(*it); + } + + readyData_.clear(); + + // Avoid interval spanning two sessions + lastTickTime_= GetPerfCounter(); +} + +bool F4SEDelayFunctorWaitList::Save(const F4SESerializationInterface* intfc) +{ + using namespace Serialization; + + // inData_ + UInt32 inDataSize = inData_.size(); + if (! WriteData(intfc,&inDataSize)) + return false; + + for (UInt32 i=0; iClassName()); + if (factory == NULL) + { + continue; + } + + // Wront type? Can't happen but who knows. + IF4SEDelayFunctor* functor = obj->GetAsDelayFunctor(); + if (functor == NULL) + { + // Throw the loaded object away. + factory->Free(obj); + continue; + } + + SInt64 delay; + if (! ReadData(intfc, &delay)) + return false; + + WaitEntryT t( delay, functor ); + inData_.push_back(t); + } + + // waitData_ + UInt32 waitDataSize; + if (! ReadData(intfc,&waitDataSize)) + return false; + + waitData_.reserve(waitDataSize); + + for (UInt32 i=0; iClassName()); + if (factory == NULL) + { + continue; + } + + // Wront type? Can't happen but who knows. + IF4SEDelayFunctor* functor = obj->GetAsDelayFunctor(); + if (functor == NULL) + { + // Throw the loaded object away. + factory->Free(obj); + continue; + } + + SInt64 delay; + if (! ReadData(intfc, &delay)) + return false; + + WaitEntryT t( delay, functor ); + waitData_.push_back(t); + } + + // readyData_ + UInt32 readyDataSize; + if (! ReadData(intfc,&readyDataSize)) + return false; + + readyData_.reserve(readyDataSize); + + for (UInt32 i=0; iClassName()); + if (factory == NULL) + { + continue; + } + + // Wront type? Can't happen but who knows. + IF4SEDelayFunctor* functor = obj->GetAsDelayFunctor(); + if (functor == NULL) + { + // Throw the loaded object away. + factory->Free(obj); + continue; + } + + readyData_.push_back(functor); + } + + return true; +} + +/// +/// F4SEDelayFunctorManager +/// + +F4SEDelayFunctorManager::F4SEDelayFunctorManager() +{ + LARGE_INTEGER t; + QueryPerformanceFrequency(&t); + budgetFreqScale_ = double(t.QuadPart) * 0.001; +} + +void F4SEDelayFunctorManager::Enqueue(IF4SEDelayFunctor* func, SInt32 delayMS) +{ + if (delayMS < 0) + queue_.Push(func); + else + waitList_.Add(delayMS, func); +} + +void F4SEDelayFunctorManager::OnPreTick() +{ + waitList_.Update(); + + IF4SEDelayFunctor* func; + while ((func = waitList_.PopReady()) != NULL) + queue_.Push(func); +} + +void F4SEDelayFunctorManager::OnTick(SInt64 startTime, float budget) +{ + SInt64 budgetTime = budget * budgetFreqScale_; + + do + { + IF4SEDelayFunctor* functor = queue_.Pop(); + if (functor == NULL) + break; + + VMValue result; + functor->Run(result); + + SInt32 delayMS; + if (functor->ShouldReschedule(delayMS)) + { + if (delayMS > 0) + waitList_.Add(delayMS, functor); + else + queue_.Push(functor); + continue; + } + + UInt32 stackId; + if (functor->ShouldResumeStack(stackId)) + { + (*g_gameVM)->m_virtualMachine->ResumeStack(stackId, &result); + } + + const IF4SEObjectFactory* factory = F4SEObjectRegistryInstance().GetFactoryByName(functor->ClassName()); + if (factory == NULL) + { + continue; + } + + factory->Free(functor); + } + while (GetPerfCounter() - startTime <= budgetTime); +} + +void F4SEDelayFunctorManager::OnRevert() +{ + queue_.ClearAndRelease(); + waitList_.ClearAndRelease(); +} + +bool F4SEDelayFunctorManager::Save(const F4SESerializationInterface* intfc) +{ + using namespace Serialization; + + if (! SaveClassHelper(intfc, 'FUNQ', queue_)) + return false; + + if (! SaveClassHelper(intfc, 'WLST', waitList_)) + return false; + + intfc->OpenRecord('____', 1); + + return true; +} + +bool F4SEDelayFunctorManager::Load(const F4SESerializationInterface* intfc, UInt32 loadedVersion) +{ + using namespace Serialization; + + UInt32 type, length, version; + + while (intfc->GetNextRecordInfo(&type, &version, &length)) + { + switch (type) + { + case 'FUNQ': + if (! queue_.Load(intfc, version)) + return false; + break; + + case 'WLST': + if (! waitList_.Load(intfc, version)) + return false; + break; + + // Done + case '____': + return true; + + default: + _MESSAGE("Error loading unexpected chunk type %08X (%.4s)", type, &type); + break; + } + } + + _MESSAGE("Missing record data for F4SEDelayFunctorManager"); + return false; +} + +/// +/// Global instances +/// + +F4SEDelayFunctorManager& F4SEDelayFunctorManagerInstance() +{ + static F4SEDelayFunctorManager instance; + return instance; +} diff --git a/f4se/f4se/PapyrusDelayFunctors.h b/f4se/f4se/PapyrusDelayFunctors.h new file mode 100644 index 0000000..bc393ec --- /dev/null +++ b/f4se/f4se/PapyrusDelayFunctors.h @@ -0,0 +1,205 @@ +#pragma once + +#include "common/ICriticalSection.h" + +#include "f4se/GameTypes.h" +#include "f4se/PapyrusObjects.h" + +#include +#include +#include +#include + +class VMValue; +struct F4SESerializationInterface; + +/// +/// IF4SEDelayFunctor +/// +class IF4SEDelayFunctor : public IF4SEObject +{ +public: + virtual ~IF4SEDelayFunctor() {} + + virtual bool Run(VMValue& resultOut) = 0; + virtual IF4SEDelayFunctor * GetAsDelayFunctor() { return this; } + + virtual bool ShouldReschedule(SInt32& delayMSOut) = 0; + virtual bool ShouldResumeStack(UInt32& stackIdOut) = 0; +}; + +/// +/// LatentF4SEDelayFunctor +/// +class LatentF4SEDelayFunctor : public IF4SEDelayFunctor +{ +public: + explicit LatentF4SEDelayFunctor(UInt32 stackId); + + explicit LatentF4SEDelayFunctor(SerializationTag); + + virtual ~LatentF4SEDelayFunctor() {} + + virtual const char* ClassName() const = 0; + virtual UInt32 ClassVersion() const = 0; + + virtual bool Run(VMValue& resultOut) = 0; + + virtual bool ShouldReschedule(SInt32& delayMSOut) override; + virtual bool ShouldResumeStack(UInt32& stackIdOut) override; + + virtual bool Save(const F4SESerializationInterface* intfc) override; + virtual bool Load(const F4SESerializationInterface* intfc, UInt32 version) override; + + UInt32 StackId() const { return stackId_; } + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free); + +protected: + UInt32 stackId_; +}; + +/// +/// F4SEDelayFunctorQueue +/// + +class F4SEDelayFunctorQueue +{ + typedef std::deque DataT; + +public: + ~F4SEDelayFunctorQueue(); + + void Push(IF4SEDelayFunctor* func); + IF4SEDelayFunctor* Pop(); + + void ClearAndRelease(); + + enum { kSaveVersion = 1 }; + + bool Save(const F4SESerializationInterface* intfc); + bool Load(const F4SESerializationInterface* intfc, UInt32 version); + +private: + ICriticalSection lock_; + DataT data_; +}; + +/// +/// F4SEDelayFunctorWaitList +/// + +class F4SEDelayFunctorWaitList +{ +private: + typedef std::pair WaitEntryT; + typedef std::vector WaitDataT; + typedef std::vector ReadyDataT; + +public: + F4SEDelayFunctorWaitList(); + + ~F4SEDelayFunctorWaitList(); + + void Add(SInt32 delayMS, IF4SEDelayFunctor* func); + + // 1. Update + // 2. PopReady until NULL + + void Update(); + IF4SEDelayFunctor* PopReady(); + + void ClearAndRelease(); + + enum { kSaveVersion = 1 }; + + bool Save(const F4SESerializationInterface* intfc); + bool Load(const F4SESerializationInterface* intfc, UInt32 version); + +private: + SInt64 lastTickTime_; + SInt64 msToCountMult_; + + ICriticalSection inLock_; + WaitDataT inData_; + WaitDataT waitData_; + ReadyDataT readyData_; +}; + +/// +/// F4SEDelayFunctorManager +/// + +class F4SEDelayFunctorManager +{ +public: + F4SEDelayFunctorManager(); + + // Takes ownership of passed pointer. + virtual void Enqueue(IF4SEDelayFunctor* func, SInt32 delayMS = 0); + + void OnPreTick(); + void OnTick(SInt64 startTime, float budget); + void OnRevert(); + + enum { kSaveVersion = 1 }; + + bool Save(const F4SESerializationInterface* intfc); + bool Load(const F4SESerializationInterface* intfc, UInt32 version); + +private: + float budgetFreqScale_; + + F4SEDelayFunctorQueue queue_; + F4SEDelayFunctorWaitList waitList_; +}; + +/// +/// Global instances +/// + +F4SEDelayFunctorManager& F4SEDelayFunctorManagerInstance(); + +#include "f4se/PapyrusSerialization.h" +#include "f4se/PapyrusArgs.h" + +#define DECLARE_DELAY_FUNCTOR_TYPE(functorName, ...) ##functorName##; +#define DECLARE_DELAY_FUNCTOR_STRING(functorName) #functorName + +// FunctorName, Params, RunFunc, CallingClass, ReturnValue, Arguments... +#define DECLARE_DELAY_FUNCTOR(functorName, numParams, ...) \ + char FunctorName_##functorName##[] = "" DECLARE_STRUCT_STRING(structName); \ + typedef F4SEDelayFunctor##numParams## ##functorName##; + +#define NUM_PARAMS 0 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 1 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 2 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 3 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 4 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 5 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 6 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 7 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 8 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 9 +#include "PapyrusDelayFunctorsDef.inl" + +#define NUM_PARAMS 10 +#include "PapyrusDelayFunctorsDef.inl" \ No newline at end of file diff --git a/f4se/f4se/PapyrusDelayFunctorsDef.inl b/f4se/f4se/PapyrusDelayFunctorsDef.inl new file mode 100644 index 0000000..0b4735e --- /dev/null +++ b/f4se/f4se/PapyrusDelayFunctorsDef.inl @@ -0,0 +1,9 @@ + +#define CLASS_NAME __MACRO_JOIN__(F4SEDelayFunctor, NUM_PARAMS) +#define VOID_SPEC 0 +#include "PapyrusDelayFunctorsDef_Base.inl" + +#define VOID_SPEC 1 +#include "PapyrusDelayFunctorsDef_Base.inl" +#undef CLASS_NAME +#undef NUM_PARAMS \ No newline at end of file diff --git a/f4se/f4se/PapyrusDelayFunctorsDef_Base.inl b/f4se/f4se/PapyrusDelayFunctorsDef_Base.inl new file mode 100644 index 0000000..7a6ab5e --- /dev/null +++ b/f4se/f4se/PapyrusDelayFunctorsDef_Base.inl @@ -0,0 +1,444 @@ +#if NUM_PARAMS > 10 +#error PapyrusDelayFunctorsDef: too many params +#endif + +template = 1 + ,typename T_Arg0 +#endif +#if NUM_PARAMS >= 2 + ,typename T_Arg1 +#endif +#if NUM_PARAMS >= 3 + ,typename T_Arg2 +#endif +#if NUM_PARAMS >= 4 + ,typename T_Arg3 +#endif +#if NUM_PARAMS >= 5 + ,typename T_Arg4 +#endif +#if NUM_PARAMS >= 6 + ,typename T_Arg5 +#endif +#if NUM_PARAMS >= 7 + ,typename T_Arg6 +#endif +#if NUM_PARAMS >= 8 + ,typename T_Arg7 +#endif +#if NUM_PARAMS >= 9 + ,typename T_Arg8 +#endif +#if NUM_PARAMS >= 10 + ,typename T_Arg9 +#endif +> + +class CLASS_NAME +#if VOID_SPEC + = 1 + , T_Arg0 +#endif +#if NUM_PARAMS >= 2 + , T_Arg1 +#endif +#if NUM_PARAMS >= 3 + , T_Arg2 +#endif +#if NUM_PARAMS >= 4 + , T_Arg3 +#endif +#if NUM_PARAMS >= 5 + , T_Arg4 +#endif +#if NUM_PARAMS >= 6 + , T_Arg5 +#endif +#if NUM_PARAMS >= 7 + , T_Arg6 +#endif +#if NUM_PARAMS >= 8 + , T_Arg7 +#endif +#if NUM_PARAMS >= 9 + , T_Arg8 +#endif +#if NUM_PARAMS >= 10 + , T_Arg9 +#endif + > +#endif + : public LatentF4SEDelayFunctor +{ +public: + typedef +#if VOID_SPEC + void +#else + T_Result +#endif + (* CallbackType)(UInt32 stackId, + T_Base * base +#if NUM_PARAMS >= 1 + , T_Arg0 arg0 +#endif +#if NUM_PARAMS >= 2 + , T_Arg1 arg1 +#endif +#if NUM_PARAMS >= 3 + , T_Arg2 arg2 +#endif +#if NUM_PARAMS >= 4 + , T_Arg3 arg3 +#endif +#if NUM_PARAMS >= 5 + , T_Arg4 arg4 +#endif +#if NUM_PARAMS >= 6 + , T_Arg5 arg5 +#endif +#if NUM_PARAMS >= 7 + , T_Arg6 arg6 +#endif +#if NUM_PARAMS >= 8 + , T_Arg7 arg7 +#endif +#if NUM_PARAMS >= 9 + , T_Arg8 arg8 +#endif +#if NUM_PARAMS >= 10 + , T_Arg9 arg9 +#endif + ); + + // Function passed to Class isn't actually kept by the object, it's used to verify parameters author-end + explicit CLASS_NAME(CallbackType func, VirtualMachine * vm, UInt32 stackId, + T_Base * base +#if NUM_PARAMS >= 1 + , T_Arg0 arg0 +#endif +#if NUM_PARAMS >= 2 + , T_Arg1 arg1 +#endif +#if NUM_PARAMS >= 3 + , T_Arg2 arg2 +#endif +#if NUM_PARAMS >= 4 + , T_Arg3 arg3 +#endif +#if NUM_PARAMS >= 5 + , T_Arg4 arg4 +#endif +#if NUM_PARAMS >= 6 + , T_Arg5 arg5 +#endif +#if NUM_PARAMS >= 7 + , T_Arg6 arg6 +#endif +#if NUM_PARAMS >= 8 + , T_Arg7 arg7 +#endif +#if NUM_PARAMS >= 9 + , T_Arg8 arg8 +#endif +#if NUM_PARAMS >= 10 + , T_Arg9 arg9 +#endif + ) : LatentF4SEDelayFunctor( stackId ) + { + if (! IsStaticType ::value) + { + PackValue(&_base, &base, vm); + } + +#if NUM_PARAMS >= 1 + PackValue(&_arg0, &arg0, vm); +#endif +#if NUM_PARAMS >= 2 + PackValue(&_arg1, &arg1, vm); +#endif +#if NUM_PARAMS >= 3 + PackValue(&_arg2, &arg2, vm); +#endif +#if NUM_PARAMS >= 4 + PackValue(&_arg3, &arg3, vm); +#endif +#if NUM_PARAMS >= 5 + PackValue(&_arg4, &arg4, vm); +#endif +#if NUM_PARAMS >= 6 + PackValue(&_arg5, &arg5, vm); +#endif +#if NUM_PARAMS >= 7 + PackValue(&_arg6, &arg6, vm); +#endif +#if NUM_PARAMS >= 8 + PackValue(&_arg7, &arg7, vm); +#endif +#if NUM_PARAMS >= 9 + PackValue(&_arg8, &arg8, vm); +#endif +#if NUM_PARAMS >= 10 + PackValue(&_arg9, &arg9, vm); +#endif + } + explicit CLASS_NAME(SerializationTag tag) : LatentF4SEDelayFunctor( tag ) {} + + virtual const char* ClassName() const override { return T_functorName; } + virtual UInt32 ClassVersion() const override { return 1; } + + virtual bool Save(const F4SESerializationInterface* intfc) override + { + using namespace Serialization; + + if(!LatentF4SEDelayFunctor::Save(intfc)) + return false; + + if (! IsStaticType ::value) + { + if(!WriteVMData(intfc, &_base)) + return false; + } + +#if NUM_PARAMS >= 1 + if(!WriteVMData(intfc, &_arg0)) + return false; +#endif +#if NUM_PARAMS >= 2 + if(!WriteVMData(intfc, &_arg1)) + return false; +#endif +#if NUM_PARAMS >= 3 + if(!WriteVMData(intfc, &_arg2)) + return false; +#endif +#if NUM_PARAMS >= 4 + if(!WriteVMData(intfc, &_arg3)) + return false; +#endif +#if NUM_PARAMS >= 5 + if(!WriteVMData(intfc, &_arg4)) + return false; +#endif +#if NUM_PARAMS >= 6 + if(!WriteVMData(intfc, &_arg5)) + return false; +#endif +#if NUM_PARAMS >= 7 + if(!WriteVMData(intfc, &_arg6)) + return false; +#endif +#if NUM_PARAMS >= 8 + if(!WriteVMData(intfc, &_arg7)) + return false; +#endif +#if NUM_PARAMS >= 9 + if(!WriteVMData(intfc, &_arg8)) + return false; +#endif +#if NUM_PARAMS >= 10 + if(!WriteVMData(intfc, &_arg9)) + return false; +#endif + + return true; + } + + virtual bool Load(const F4SESerializationInterface* intfc, UInt32 version) override + { + using namespace Serialization; + + if(!LatentF4SEDelayFunctor::Load(intfc, version)) + return false; + + if (! IsStaticType ::value) + { + if(!ReadVMData(intfc, &_base)) + return false; + } + +#if NUM_PARAMS >= 1 + if(!ReadVMData(intfc, &_arg0)) + return false; +#endif +#if NUM_PARAMS >= 2 + if(!ReadVMData(intfc, &_arg1)) + return false; +#endif +#if NUM_PARAMS >= 3 + if(!ReadVMData(intfc, &_arg2)) + return false; +#endif +#if NUM_PARAMS >= 4 + if(!ReadVMData(intfc, &_arg3)) + return false; +#endif +#if NUM_PARAMS >= 5 + if(!ReadVMData(intfc, &_arg4)) + return false; +#endif +#if NUM_PARAMS >= 6 + if(!ReadVMData(intfc, &_arg5)) + return false; +#endif +#if NUM_PARAMS >= 7 + if(!ReadVMData(intfc, &_arg6)) + return false; +#endif +#if NUM_PARAMS >= 8 + if(!ReadVMData(intfc, &_arg7)) + return false; +#endif +#if NUM_PARAMS >= 9 + if(!ReadVMData(intfc, &_arg8)) + return false; +#endif +#if NUM_PARAMS >= 10 + if(!ReadVMData(intfc, &_arg9)) + return false; +#endif + + return true; + } + + virtual bool Run(VMValue& resultValue) override + { + T_Base * base = NULL; + + // extract base object pointer for non-static types + if (! IsStaticType ::value) + { + UnpackValue(&base, &_base); + if (!base) return false; + } + + // extract parameters +#if NUM_PARAMS >= 1 + T_Arg0 arg0; + UnpackValue(&arg0, &_arg0); +#endif +#if NUM_PARAMS >= 2 + T_Arg1 arg1; + UnpackValue(&arg1, &_arg1); +#endif +#if NUM_PARAMS >= 3 + T_Arg2 arg2; + UnpackValue(&arg2, &_arg2); +#endif +#if NUM_PARAMS >= 4 + T_Arg3 arg3; + UnpackValue(&arg3, &_arg3); +#endif +#if NUM_PARAMS >= 5 + T_Arg4 arg4; + UnpackValue(&arg4, &_arg4); +#endif +#if NUM_PARAMS >= 6 + T_Arg5 arg5; + UnpackValue(&arg5, &_arg5); +#endif +#if NUM_PARAMS >= 7 + T_Arg6 arg6; + UnpackValue(&arg6, &_arg6); +#endif +#if NUM_PARAMS >= 8 + T_Arg7 arg7; + UnpackValue(&arg7, &_arg7); +#endif +#if NUM_PARAMS >= 9 + T_Arg8 arg8; + UnpackValue(&arg8, &_arg8); +#endif +#if NUM_PARAMS >= 10 + T_Arg9 arg9; + UnpackValue(&arg9, &_arg9); +#endif + +#if !VOID_SPEC + T_Result result = +#endif + ((CallbackType)T_runFunc)(stackId_, base +#if NUM_PARAMS >= 1 + , arg0 +#endif +#if NUM_PARAMS >= 2 + , arg1 +#endif +#if NUM_PARAMS >= 3 + , arg2 +#endif +#if NUM_PARAMS >= 4 + , arg3 +#endif +#if NUM_PARAMS >= 5 + , arg4 +#endif +#if NUM_PARAMS >= 6 + , arg5 +#endif +#if NUM_PARAMS >= 7 + , arg6 +#endif +#if NUM_PARAMS >= 8 + , arg7 +#endif +#if NUM_PARAMS >= 9 + , arg8 +#endif +#if NUM_PARAMS >= 10 + , arg9 +#endif + ); + + // pack the result +#if VOID_SPEC + resultValue.SetNone(); +#else + PackValue(&resultValue, &result, (*g_gameVM)->m_virtualMachine); +#endif + if (! IsStaticType ::value) + { + DestroyValue(&base); + } + return true; + } + +private: + VMValue _base; +#if NUM_PARAMS >= 1 + VMValue _arg0; +#endif +#if NUM_PARAMS >= 2 + VMValue _arg1; +#endif +#if NUM_PARAMS >= 3 + VMValue _arg2; +#endif +#if NUM_PARAMS >= 4 + VMValue _arg3; +#endif +#if NUM_PARAMS >= 5 + VMValue _arg4; +#endif +#if NUM_PARAMS >= 6 + VMValue _arg5; +#endif +#if NUM_PARAMS >= 7 + VMValue _arg6; +#endif +#if NUM_PARAMS >= 8 + VMValue _arg7; +#endif +#if NUM_PARAMS >= 9 + VMValue _arg8; +#endif +#if NUM_PARAMS >= 10 + VMValue _arg9; +#endif +}; +#undef VOID_SPEC \ No newline at end of file diff --git a/f4se/f4se/PapyrusEncounterZone.cpp b/f4se/f4se/PapyrusEncounterZone.cpp new file mode 100644 index 0000000..ccdf842 --- /dev/null +++ b/f4se/f4se/PapyrusEncounterZone.cpp @@ -0,0 +1,127 @@ +#include "f4se/PapyrusEncounterZone.h" + +#include "f4se/GameForms.h" +#include "f4se/PapyrusArgs.h" + +namespace papyrusEncounterZone +{ + BGSLocation * GetLocation(BGSEncounterZone * thisEZ) + { + return thisEZ ? thisEZ->location : nullptr; + } + + void SetLocation(BGSEncounterZone * thisEZ, BGSLocation * newLoc) + { + if(thisEZ) { + thisEZ->location = newLoc; + } + } + + SInt32 GetRank(BGSEncounterZone * thisEZ) + { + return thisEZ ? thisEZ->rank : 0; + } + + void SetRank(BGSEncounterZone * thisEZ, SInt32 newRank) + { + if(thisEZ) { + thisEZ->rank = newRank; + } + } + + SInt32 GetMinLevel(BGSEncounterZone * thisEZ) + { + return thisEZ ? thisEZ->minLevel : 0; + } + + void SetMinLevel(BGSEncounterZone * thisEZ, SInt32 newLevel) + { + if(thisEZ) { + thisEZ->minLevel = newLevel; + } + } + + SInt32 GetMaxLevel(BGSEncounterZone * thisEZ) + { + return thisEZ ? thisEZ->maxLevel : 0; + } + + void SetMaxLevel(BGSEncounterZone * thisEZ, SInt32 newLevel) + { + if(thisEZ) { + thisEZ->maxLevel = newLevel; + } + } + + bool IsNeverResetable(BGSEncounterZone * thisEZ) + { + return thisEZ ? ((thisEZ->flags & BGSEncounterZone::kFlag_NeverResets) == BGSEncounterZone::kFlag_NeverResets) : false; + } + + void SetNeverResetable(BGSEncounterZone * thisEZ, bool neverResets) + { + if(thisEZ) { + if(neverResets) + thisEZ->flags |= BGSEncounterZone::kFlag_NeverResets; + else + thisEZ->flags &= ~BGSEncounterZone::kFlag_NeverResets; + } + } + + bool IsWorkshop(BGSEncounterZone * thisEZ) + { + return thisEZ ? ((thisEZ->flags & BGSEncounterZone::kFlag_Workshop) == BGSEncounterZone::kFlag_Workshop) : false; + } + + void SetWorkshop(BGSEncounterZone * thisEZ, bool workshop) + { + if(thisEZ) { + if(workshop) + thisEZ->flags |= BGSEncounterZone::kFlag_Workshop; + else + thisEZ->flags &= ~BGSEncounterZone::kFlag_Workshop; + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusEncounterZone::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("GetLocation", "EncounterZone", papyrusEncounterZone::GetLocation, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetLocation", "EncounterZone", papyrusEncounterZone::SetLocation, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetRank", "EncounterZone", papyrusEncounterZone::GetRank, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetRank", "EncounterZone", papyrusEncounterZone::SetRank, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetMinLevel", "EncounterZone", papyrusEncounterZone::GetMinLevel, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetMinLevel", "EncounterZone", papyrusEncounterZone::SetMinLevel, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetMaxLevel", "EncounterZone", papyrusEncounterZone::GetMaxLevel, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetMaxLevel", "EncounterZone", papyrusEncounterZone::SetMaxLevel, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("IsNeverResetable", "EncounterZone", papyrusEncounterZone::IsNeverResetable, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetNeverResetable", "EncounterZone", papyrusEncounterZone::SetNeverResetable, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("IsWorkshop", "EncounterZone", papyrusEncounterZone::IsWorkshop, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetWorkshop", "EncounterZone", papyrusEncounterZone::SetWorkshop, vm)); +} diff --git a/f4se/f4se/PapyrusEncounterZone.h b/f4se/f4se/PapyrusEncounterZone.h new file mode 100644 index 0000000..34c2248 --- /dev/null +++ b/f4se/f4se/PapyrusEncounterZone.h @@ -0,0 +1,8 @@ +#pragma once + +class VirtualMachine; + +namespace papyrusEncounterZone +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusEquipSlot.cpp b/f4se/f4se/PapyrusEquipSlot.cpp new file mode 100644 index 0000000..0a12d7f --- /dev/null +++ b/f4se/f4se/PapyrusEquipSlot.cpp @@ -0,0 +1,34 @@ +#include "f4se/PapyrusEquipSlot.h" + +#include "f4se/GameForms.h" +#include "f4se/PapyrusArgs.h" + +namespace papyrusEquipSlot +{ + VMArray GetParents(BGSEquipSlot* equipSlot) + { + VMArray results; + if(equipSlot) + { + for(UInt32 i = 0; i < equipSlot->parentSlots.count; i++) + { + BGSEquipSlot * slot = nullptr; + equipSlot->parentSlots.GetNthItem(i, slot); + results.Push(&slot); + } + } + + return results; + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusEquipSlot::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterForm(BGSEquipSlot::kTypeID, "EquipSlot"); + + vm->RegisterFunction( + new NativeFunction0 >("GetParents", "EquipSlot", papyrusEquipSlot::GetParents, vm)); +} diff --git a/f4se/f4se/PapyrusEquipSlot.h b/f4se/f4se/PapyrusEquipSlot.h new file mode 100644 index 0000000..c220ae7 --- /dev/null +++ b/f4se/f4se/PapyrusEquipSlot.h @@ -0,0 +1,8 @@ +#pragma once + +class VirtualMachine; + +namespace papyrusEquipSlot +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusEvents.cpp b/f4se/f4se/PapyrusEvents.cpp new file mode 100644 index 0000000..f8c9dc9 --- /dev/null +++ b/f4se/f4se/PapyrusEvents.cpp @@ -0,0 +1,37 @@ +#include "f4se/PapyrusEvents.h" +#include "f4se/PapyrusUtilities.h" + +#include "f4se/GameReferences.h" + +RelocAddr <_SendCustomEvent> SendCustomEvent_Internal(0x013D9460); +RelocAddr <_CallFunctionNoWait> CallFunctionNoWait_Internal(0x013D69D0); +RelocAddr <_CallGlobalFunctionNoWait> CallGlobalFunctionNoWait_Internal(0x01451A80); + +RegistrationMapHolder g_inputKeyEventRegs; +RegistrationMapHolder g_inputControlEventRegs; +RegistrationMapHolder g_externalEventRegs; +RegistrationSetHolder g_cameraEventRegs; +RegistrationSetHolder g_furnitureEventRegs; + +F4SEFurnitureEventSink g_furnitureEventSink; + +EventResult F4SEFurnitureEventSink::ReceiveEvent(TESFurnitureEvent * evn, void * dispatcher) +{ + g_furnitureEventRegs.ForEach( + [&evn](const EventRegistration & reg) + { + if(reg.params.NoFilter() || reg.params.HasFilter(evn->actor) || reg.params.HasFilter(evn->furniture)) + SendPapyrusEvent3(reg.handle, reg.scriptName, "OnFurnitureEvent", evn->actor, evn->furniture, evn->isGettingUp); + } + ); +#if 0 + UInt64 handle = PapyrusVM::GetHandleFromObject(evn->furniture, TESObjectREFR::kTypeID); + // Do not use, function signature is not correct + CALL_MEMBER_FN(*g_gameVM, SendPapyrusEvent)(handle, "OnFurnitureEvent", [handle](void * scriptVariable) + { + DumpClass(scriptVariable, 8); + return true; + }); +#endif + return kEvent_Continue; +} diff --git a/f4se/f4se/PapyrusEvents.h b/f4se/f4se/PapyrusEvents.h new file mode 100644 index 0000000..d77db12 --- /dev/null +++ b/f4se/f4se/PapyrusEvents.h @@ -0,0 +1,604 @@ +#pragma once + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusValue.h" +#include "f4se/PapyrusArgs.h" + +#include "f4se/PluginAPI.h" +#include "f4se/Serialization.h" + +#include +#include + +// This is the callback function to ScriptObject.SendCustomEvent, the high-level parameters were more convenient +// The only issue is you actually need a sending object and a CustomEvent on the sender's script, which can't be native +typedef void (* _SendCustomEvent)(VirtualMachine * vm, UInt64 unk1, VMIdentifier * sender, const BSFixedString * eventName, VMValue * args); +extern RelocAddr <_SendCustomEvent> SendCustomEvent_Internal; + +// Same callback as ScriptObject.CallFunctionNoWait +typedef void (* _CallFunctionNoWait)(VirtualMachine * vm, UInt64 unk1, VMIdentifier * vmIdentifier, const BSFixedString * eventName, VMValue * args); +extern RelocAddr <_CallFunctionNoWait> CallFunctionNoWait_Internal; + +// Same callback as Utility.CallGlobalFunctionNoWait +typedef void (* _CallGlobalFunctionNoWait)(VirtualMachine * vm, UInt64 unk1, UInt64 unk2, const BSFixedString * className, const BSFixedString * eventName, VMValue * args); +extern RelocAddr <_CallGlobalFunctionNoWait> CallGlobalFunctionNoWait_Internal; + +template +void SendCustomEvent(T * sender, const BSFixedString & eventName, VMArray & arguments) +{ + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + + VMValue senderValue; + PackValue(&senderValue, &sender, vm); + VMValue args; + PackValue(&args, &arguments, vm); + + if(senderValue.IsIdentifier()) { + VMIdentifier * identifier = senderValue.data.id; + if(identifier) { + SendCustomEvent_Internal(vm, 0, identifier, &eventName, &args); + } + } +} + +template +void CallFunctionNoWait(T * sender, const BSFixedString & eventName, VMArray & arguments) +{ + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + + VMValue senderValue; + PackValue(&senderValue, &sender, vm); + VMValue args; + PackValue(&args, &arguments, vm); + + if(senderValue.IsIdentifier()) { + VMIdentifier * identifier = senderValue.data.id; + if(identifier) { + CallFunctionNoWait_Internal(vm, 0, identifier, &eventName, &args); + } + } +} + +enum InternalEventVersion +{ + kVersion1, + kVersion2, + kCurrentVersion = kVersion2 +}; + +template +class EventRegistration +{ +public: + UInt64 handle; + BSFixedString scriptName; + D params; + + bool operator<(const EventRegistration & rhs) const + { + if (handle != rhs.handle) + { + return handle < rhs.handle; + } + else + { + return scriptName < rhs.scriptName; + } + } + + bool Save(const F4SESerializationInterface * intfc, UInt32 version) const + { + if (! intfc->WriteRecordData(&handle, sizeof(handle))) + return false; + if (!Serialization::WriteData(intfc, &scriptName)) + return false; + if (! params.Save(intfc, version)) + return false; + return true; + } + + bool Load(const F4SESerializationInterface * intfc, UInt32 version) + { + if (! intfc->ReadRecordData(&handle, sizeof(handle))) + return false; + if(version >= InternalEventVersion::kVersion2) { + if (!Serialization::ReadData(intfc, &scriptName)) + return false; + } else { + scriptName = "ScriptObject"; // Older versions will use ScriptObject by default + } + if (! params.Load(intfc, version)) + return false; + return true; + } + + void Dump(void) + { + _MESSAGE("> handle:\t%016I64x", handle); + params.Dump(); + } +}; + +class NullParameters +{ +public: + bool Save(const F4SESerializationInterface * intfc, UInt32 version) const { return true; } + bool Load(const F4SESerializationInterface * intfc, UInt32 version) { return true; } + void Dump(void) {} +}; + +class FormParameters +{ +public: + std::set forms; + + bool Save(const F4SESerializationInterface * intfc, UInt32 version) const + { + UInt32 size = forms.size(); + if (! intfc->WriteRecordData(&size, sizeof(size))) + return false; + + for(auto & form : forms) + { + UInt32 formId = form; + if (! intfc->WriteRecordData(&formId, sizeof(formId))) + return false; + } + + return true; + } + + bool Load(const F4SESerializationInterface * intfc, UInt32 version) + { + UInt32 size = 0; + if (!Serialization::ReadData(intfc, &size)) + { + _MESSAGE("Error loading formId size parameter"); + return false; + } + + for(UInt32 i = 0; i < size; i++) + { + UInt32 oldformId = 0; + UInt32 newformId = 0; + + if (!Serialization::ReadData(intfc, &oldformId)) + { + _MESSAGE("Error loading formId parameter"); + return false; + } + + // Skip if handle is no longer valid. + if (!intfc->ResolveFormId(oldformId, &newformId)) + continue; + + forms.insert(newformId); + } + + return true; + } + + void AddFilter(TESForm * form) + { + forms.insert(form ? form->formID : 0); + } + + void RemoveFilter(TESForm * form) + { + forms.erase(form ? form->formID : 0); + } + + bool HasFilter(TESForm * form) const + { + return forms.find(form ? form->formID : 0) != forms.end(); + } + + bool NoFilter() const + { + return forms.size() == 0; + } + + void Dump(void) + { + _MESSAGE("> formId:\t%08X", forms.size()); + } +}; + +class ExternalEventParameters +{ +public: + BSFixedString callbackName; + + bool Save(const F4SESerializationInterface * intfc, UInt32 version) const + { + return Serialization::WriteData(intfc, &callbackName); + } + + bool Load(const F4SESerializationInterface * intfc, UInt32 version) + { + return Serialization::ReadData(intfc, &callbackName); + } + + void Dump(void) + { + _MESSAGE("> callbackName:\t%s", callbackName); + } +}; + +template +class RegistrationMapHolder : public SafeDataHolder>>> +{ + typedef std::set> RegSet; + typedef std::map RegMap; + +public: + + void Register(K & key, UInt64 handle, BSFixedString scriptName, D * params = NULL) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = vm->GetHandlePolicy(); + + EventRegistration reg; + reg.handle = handle; + reg.scriptName = scriptName; + if (params) + reg.params = *params; + + Lock(); + + if (m_data[key].insert(reg).second) + policy->AddRef(handle); + + Release(); + } + + void Unregister(K & key, UInt64 handle, BSFixedString scriptName) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = vm->GetHandlePolicy(); + + EventRegistration reg; + reg.handle = handle; + reg.scriptName = scriptName; + + Lock(); + + if (m_data[key].erase(reg)) + policy->Release(handle); + + Release(); + } + + void UnregisterAll(UInt64 handle, BSFixedString scriptName) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = vm->GetHandlePolicy(); + + EventRegistration reg; + reg.handle = handle; + reg.scriptName = scriptName; + + Lock(); + + for (RegMap::iterator iter = m_data.begin(); iter != m_data.end(); ++iter) + if (iter->second.erase(reg)) + policy->Release(handle); + + Release(); + } + + template + void ForEach(K & key, F & functor) + { + Lock(); + + RegMap::iterator handles = m_data.find(key); + + if (handles != m_data.end()) + for (RegSet::iterator iter = handles->second.begin(); iter != handles->second.end(); ++iter) + functor(*iter); + + Release(); + } + + void Clear(void) + { + Lock(); + m_data.clear(); + Release(); + } + + bool Save(const F4SESerializationInterface * intfc, UInt32 type, UInt32 version) + { + intfc->OpenRecord(type, version); + + Lock(); + + for (RegMap::iterator iter = m_data.begin(); iter != m_data.end(); ++iter) + { + UInt32 numRegs = iter->second.size(); + + if (!numRegs) + continue; + + intfc->OpenRecord('REGS', version); + + // Key + Serialization::WriteData(intfc, &iter->first); + // Reg count + intfc->WriteRecordData(&numRegs, sizeof(numRegs)); + // Regs + for (RegSet::iterator elems = iter->second.begin(); elems != iter->second.end(); ++elems) + elems->Save(intfc, version); + } + + intfc->OpenRecord('REGE', version); + + Release(); + + return true; + } + + bool Load(const F4SESerializationInterface* intfc, UInt32 version) + { + UInt32 type, length, curVersion; + + while (intfc->GetNextRecordInfo(&type, &curVersion, &length)) + { + switch (type) + { + case 'REGS': + { + K curKey; + // Key + if (! Serialization::ReadData(intfc, &curKey)) + { + _MESSAGE("Error loading reg key"); + return false; + } + + // Reg count + UInt32 numRegs = 0; + if (! intfc->ReadRecordData(&numRegs, sizeof(numRegs))) + { + _MESSAGE("Error loading reg count"); + return false; + } + + for (UInt32 i = 0; i< numRegs; i++) + { + EventRegistration reg; + if (reg.Load(intfc, curVersion)) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = vm->GetHandlePolicy(); + + UInt64 newHandle = 0; + + // Skip if handle is no longer valid. + if (! intfc->ResolveHandle(reg.handle, &newHandle)) + continue; + + VMObjectTypeInfo * typeInfo = nullptr; + if(vm->GetObjectTypeInfoByName(®.scriptName, &typeInfo)) + if(typeInfo) + typeInfo->Release(); + + // No valid type for script, skip + if(!typeInfo) + continue; + + reg.handle = newHandle; + + Lock(); + + if (m_data[curKey].insert(reg).second) + policy->AddRef(reg.handle); + + Release(); + + } + else + { + _MESSAGE("Error loading regs"); + return false; + } + } + + break; + } + case 'REGE': + { + return true; + } + default: + { + _MESSAGE("Error loading unexpected chunk type %08X (%.4s)", type, &type); + return false; + } + } + } + + _MESSAGE("Missing record data for type %08X (%.4s)", type, &type); + return false; + } +}; + +template +class RegistrationSetHolder : public SafeDataHolder>> +{ + typedef std::set> RegSet; + +public: + + void Register(UInt64 handle, BSFixedString scriptName, D * params = NULL) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = vm->GetHandlePolicy(); + + EventRegistration reg; + reg.handle = handle; + reg.scriptName = scriptName; + if (params) + reg.params = *params; + + Lock(); + + if (m_data.insert(reg).second) + policy->AddRef(handle); + + Release(); + } + + void Unregister(UInt64 handle, BSFixedString scriptName) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = vm->GetHandlePolicy(); + + EventRegistration reg; + reg.handle = handle; + reg.scriptName = scriptName; + + Lock(); + + if (m_data.erase(reg)) + policy->Release(handle); + + Release(); + } + + template + void ForEach(F & functor) + { + Lock(); + + for (RegSet::iterator iter = m_data.begin(); iter != m_data.end(); ++iter) + functor(*iter); + + Release(); + } + + void Clear(void) + { + Lock(); + m_data.clear(); + Release(); + } + + bool Save(const F4SESerializationInterface * intfc, UInt32 type, UInt32 version) + { + intfc->OpenRecord(type, version); + + Lock(); + + UInt32 numRegs = m_data.size(); + + // Reg count + intfc->WriteRecordData(&numRegs, sizeof(numRegs)); + + // Regs + for (RegSet::iterator iter = m_data.begin(); iter != m_data.end(); ++iter) + iter->Save(intfc, version); + + Release(); + + return true; + } + + bool Load(const F4SESerializationInterface* intfc, UInt32 version) + { + // Reg count + UInt32 numRegs = 0; + if (! intfc->ReadRecordData(&numRegs, sizeof(numRegs))) + { + _MESSAGE("Error loading reg count"); + return false; + } + + for (UInt32 i=0; i reg; + if (reg.Load(intfc, version)) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = vm->GetHandlePolicy(); + + UInt64 newHandle = 0; + + // Skip if handle is no longer valid. + if (! intfc->ResolveHandle(reg.handle, &newHandle)) + continue; + + VMObjectTypeInfo * typeInfo = nullptr; + if(vm->GetObjectTypeInfoByName(®.scriptName, &typeInfo)) + if(typeInfo) + typeInfo->Release(); + + // No valid type for script, skip + if(!typeInfo) + continue; + + reg.handle = newHandle; + + Lock(); + + if (m_data.insert(reg).second) + policy->AddRef(reg.handle); + + Release(); + + } + else + { + _MESSAGE("Error loading regs"); + return false; + } + } + + return true; + } +}; + +extern RegistrationMapHolder g_inputKeyEventRegs; +extern RegistrationMapHolder g_inputControlEventRegs; +extern RegistrationMapHolder g_externalEventRegs; +extern RegistrationSetHolder g_cameraEventRegs; +extern RegistrationSetHolder g_furnitureEventRegs; + +class F4SEFurnitureEventSink : public BSTEventSink +{ +public: + virtual EventResult ReceiveEvent(TESFurnitureEvent * evn, void * dispatcher) override; +}; + +extern F4SEFurnitureEventSink g_furnitureEventSink; + +#define NUM_PARAMS 1 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 2 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 3 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 4 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 5 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 6 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 7 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 8 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 9 +#include "PapyrusEventsDef.inl" + +#define NUM_PARAMS 10 +#include "PapyrusEventsDef.inl" diff --git a/f4se/f4se/PapyrusEventsDef.inl b/f4se/f4se/PapyrusEventsDef.inl new file mode 100644 index 0000000..beb9b35 --- /dev/null +++ b/f4se/f4se/PapyrusEventsDef.inl @@ -0,0 +1,13 @@ + +#define EVENT_OBJECT 1 +#define EVENT_NAME __MACRO_JOIN__(SendPapyrusEvent, NUM_PARAMS) +#include "PapyrusEventsDef_Base.inl" +#undef EVENT_NAME +#undef EVENT_OBJECT + +#define EVENT_OBJECT 0 +#define EVENT_NAME __MACRO_JOIN__(CallGlobalFunctionNoWait, NUM_PARAMS) +#include "PapyrusEventsDef_Base.inl" +#undef EVENT_NAME +#undef EVENT_OBJECT +#undef NUM_PARAMS diff --git a/f4se/f4se/PapyrusEventsDef_Base.inl b/f4se/f4se/PapyrusEventsDef_Base.inl new file mode 100644 index 0000000..7d5c0cc --- /dev/null +++ b/f4se/f4se/PapyrusEventsDef_Base.inl @@ -0,0 +1,150 @@ +#if NUM_PARAMS > 10 +#error PapyrusEventsDef: too many params +#endif + +// Avoid passing VMArrays as arguments, behavior may be undefined as Papyrus does not normally support multi-dimensional arrays +template< +#if NUM_PARAMS >= 1 + typename T_arg1 +#endif +#if NUM_PARAMS >= 2 + , typename T_arg2 +#endif +#if NUM_PARAMS >= 3 + , typename T_arg3 +#endif +#if NUM_PARAMS >= 4 + , typename T_arg4 +#endif +#if NUM_PARAMS >= 5 + , typename T_arg5 +#endif +#if NUM_PARAMS >= 6 + , typename T_arg6 +#endif +#if NUM_PARAMS >= 7 + , typename T_arg7 +#endif +#if NUM_PARAMS >= 8 + , typename T_arg8 +#endif +#if NUM_PARAMS >= 9 + , typename T_arg9 +#endif +#if NUM_PARAMS >= 10 + , typename T_arg10 +#endif +> +void EVENT_NAME( +#if EVENT_OBJECT + UInt64 handle, +#endif + const BSFixedString & className, const BSFixedString & eventName +#if NUM_PARAMS >= 1 + , T_arg1 & t1 +#endif +#if NUM_PARAMS >= 2 + , T_arg2 & t2 +#endif +#if NUM_PARAMS >= 3 + , T_arg3 & t3 +#endif +#if NUM_PARAMS >= 4 + , T_arg4 & t4 +#endif +#if NUM_PARAMS >= 5 + , T_arg5 & t5 +#endif +#if NUM_PARAMS >= 6 + , T_arg6 & t6 +#endif +#if NUM_PARAMS >= 7 + , T_arg7 & t7 +#endif +#if NUM_PARAMS >= 8 + , T_arg8 & t8 +#endif +#if NUM_PARAMS >= 9 + , T_arg9 & t9 +#endif +#if NUM_PARAMS >= 10 + , T_arg10 & t10 +#endif +) +{ + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; +#if EVENT_OBJECT + VMValue receiver; + if(GetIdentifier(&receiver, handle, &className, vm)) { +#endif + // Build the VM arguments for the CallFunctionNoWait + VMArray arguments; +#if NUM_PARAMS >= 1 + VMVariable var1; + var1.Set(&t1); + arguments.Push(&var1); +#endif +#if NUM_PARAMS >= 2 + VMVariable var2; + var2.Set(&t2); + arguments.Push(&var2); +#endif +#if NUM_PARAMS >= 3 + VMVariable var3; + var3.Set(&t3); + arguments.Push(&var3); +#endif +#if NUM_PARAMS >= 4 + VMVariable var4; + var4.Set(&t4); + arguments.Push(&var4); +#endif +#if NUM_PARAMS >= 5 + VMVariable var5; + var5.Set(&t5); + arguments.Push(&var5); +#endif +#if NUM_PARAMS >= 6 + VMVariable var6; + var6.Set(&t6); + arguments.Push(&var6); +#endif +#if NUM_PARAMS >= 7 + VMVariable var7; + var7.Set(&t7); + arguments.Push(&var7); +#endif +#if NUM_PARAMS >= 8 + VMVariable var8; + var8.Set(&t8); + arguments.Push(&var8); +#endif +#if NUM_PARAMS >= 9 + VMVariable var9; + var9.Set(&t9); + arguments.Push(&var9); +#endif +#if NUM_PARAMS >= 10 + VMVariable var10; + var10.Set(&t10); + arguments.Push(&var10); +#endif + // Pack the VMArray + VMValue args; + PackValue(&args, &arguments, vm); + + // This should eventually replaced by an actual call to the Event Queue (VM+0x158), this is a workaround for now +#if EVENT_OBJECT + if(receiver.IsIdentifier()) { + VMIdentifier * identifier = receiver.data.id; + if(identifier) { + CallFunctionNoWait_Internal(vm, 0, identifier, &eventName, &args); + } + } +#else + CallGlobalFunctionNoWait_Internal(vm, 0, 0, &className, &eventName, &args); +#endif +#if EVENT_OBJECT + } +#endif +} diff --git a/f4se/f4se/PapyrusF4SE.cpp b/f4se/f4se/PapyrusF4SE.cpp new file mode 100644 index 0000000..e9b47a7 --- /dev/null +++ b/f4se/f4se/PapyrusF4SE.cpp @@ -0,0 +1,79 @@ +#include "f4se/PapyrusF4SE.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se_common/f4se_version.h" +#include "f4se/PluginManager.h" + +extern PluginManager g_pluginManager; + +namespace papyrusF4SE { + + UInt32 GetVersion(StaticFunctionTag* base) + { + return F4SE_VERSION_INTEGER; + } + UInt32 GetVersionMinor(StaticFunctionTag* base) + { + return F4SE_VERSION_INTEGER_MINOR; + } + + UInt32 GetVersionBeta(StaticFunctionTag* base) + { + return F4SE_VERSION_INTEGER_BETA; + } + + UInt32 GetVersionRelease(StaticFunctionTag* base) + { + return F4SE_VERSION_RELEASEIDX; + } + + UInt32 GetPluginVersion(StaticFunctionTag* base, BSFixedString name) + { + PluginInfo * info = g_pluginManager.GetInfoByName(name); + if(info) { + return info->version; + } + + return -1; + } + +#ifdef _DEBUG + void TestInventoryFunc(StaticFunctionTag* base, VMRefOrInventoryObj ref) + { + TESForm * baseForm = nullptr; + ExtraDataList * extraData = nullptr; + ref.GetExtraData(&baseForm, &extraData); + } +#endif +} + +void papyrusF4SE::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0("GetVersion", "F4SE", papyrusF4SE::GetVersion, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetVersionMinor", "F4SE", papyrusF4SE::GetVersionMinor, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetVersionBeta", "F4SE", papyrusF4SE::GetVersionBeta, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetVersionRelease", "F4SE", papyrusF4SE::GetVersionRelease, vm)); + + vm->RegisterFunction( + new NativeFunction1("GetPluginVersion", "F4SE", papyrusF4SE::GetPluginVersion, vm)); + +#ifdef _DEBUG + vm->RegisterFunction( + new NativeFunction1("TestInventoryFunc", "F4SE", papyrusF4SE::TestInventoryFunc, vm)); +#endif + + vm->SetFunctionFlags("F4SE", "GetVersion", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("F4SE", "GetVersionMinor", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("F4SE", "GetVersionBeta", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("F4SE", "GetVersionRelease", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("F4SE", "GetPluginVersion", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusF4SE.h b/f4se/f4se/PapyrusF4SE.h new file mode 100644 index 0000000..877ba02 --- /dev/null +++ b/f4se/f4se/PapyrusF4SE.h @@ -0,0 +1,17 @@ +#pragma once + +class VirtualMachine; +struct StaticFunctionTag; + +#include "f4se/GameTypes.h" + +namespace papyrusF4SE +{ + void RegisterFuncs(VirtualMachine* vm); + + UInt32 GetVersion(StaticFunctionTag* base); + UInt32 GetVersionMinor(StaticFunctionTag* base); + UInt32 GetVersionBeta(StaticFunctionTag* base); + UInt32 GetVersionRelease(StaticFunctionTag* base); + UInt32 GetPluginVersion(StaticFunctionTag* base, BSFixedString name); +} diff --git a/f4se/f4se/PapyrusFavoritesManager.cpp b/f4se/f4se/PapyrusFavoritesManager.cpp new file mode 100644 index 0000000..a7534b7 --- /dev/null +++ b/f4se/f4se/PapyrusFavoritesManager.cpp @@ -0,0 +1,172 @@ +#include "f4se/PapyrusFavoritesManager.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" +#include "f4se/PapyrusDelayFunctors.h" + +#include "f4se/GameData.h" + +namespace papyrusFavoritesManager { + + VMArray GetTaggedFormsLatent(UInt32 stackId, StaticFunctionTag *) + { + VMArray result; + (*g_favoritesManager)->taggedForms.ForEach([&](FavoritesManager::TaggedEntry * entry) + { + if(entry->form) + { + result.Push(&entry->form); + } + + return true; + }); + return result; + } + + DECLARE_DELAY_FUNCTOR(F4SEGetTaggedFormsFunctor, 0, GetTaggedFormsLatent, StaticFunctionTag, VMArray); + + bool GetTaggedForms(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag* s) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEGetTaggedFormsFunctor(GetTaggedFormsLatent, vm, stackId, s)); + return true; + } + + void AddTaggedFormsLatent(UInt32 stackId, StaticFunctionTag *, VMArray forms) + { + for(int i = 0; i < forms.Length(); ++i) + { + TESForm * form = nullptr; + forms.Get(&form, i); + + if(form) { + FavoritesManager::TaggedEntry entry; + entry.form = form; + entry.unk08 = 1; + (*g_favoritesManager)->taggedForms.Add(&entry); + } + } + } + + DECLARE_DELAY_FUNCTOR(F4SEAddTaggedFormsFunctor, 1, AddTaggedFormsLatent, StaticFunctionTag, void, VMArray); + + bool AddTaggedForms(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag* s, VMArray forms) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEAddTaggedFormsFunctor(AddTaggedFormsLatent, vm, stackId, s, forms)); + return true; + } + + void RemoveTaggedFormsLatent(UInt32 stackId, StaticFunctionTag *, VMArray forms) + { + for(int i = 0; i < forms.Length(); ++i) + { + TESForm * form = nullptr; + forms.Get(&form, i); + + if(form) { + (*g_favoritesManager)->taggedForms.Remove(&form); + } + } + } + + DECLARE_DELAY_FUNCTOR(F4SERemoveTaggedFormsFunctor, 1, RemoveTaggedFormsLatent, StaticFunctionTag, void, VMArray); + + bool RemoveTaggedForms(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag* s, VMArray forms) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SERemoveTaggedFormsFunctor(RemoveTaggedFormsLatent, vm, stackId, s, forms)); + return true; + } + + bool IsTaggedFormLatent(UInt32 stackId, StaticFunctionTag *, TESForm* form) + { + if(form) { + return (*g_favoritesManager)->taggedForms.Find(&form) != nullptr; + } + + return false; + } + + DECLARE_DELAY_FUNCTOR(F4SEIsTaggedFormFunctor, 1, IsTaggedFormLatent, StaticFunctionTag, bool, TESForm*); + + bool IsTaggedForm(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag* s, TESForm* form) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEIsTaggedFormFunctor(IsTaggedFormLatent, vm, stackId, s, form)); + return true; + } + + VMArray GetFavoritesLatent(UInt32 stackId, StaticFunctionTag *) + { + VMArray result; + for(int i = 0; i < FavoritesManager::kNumFavorites; ++i) + { + TESForm * form = (*g_favoritesManager)->favorites[i]; + result.Push(&form); + } + + return result; + } + + DECLARE_DELAY_FUNCTOR(F4SEGetFavoritesFunctor, 0, GetFavoritesLatent, StaticFunctionTag, VMArray); + + bool GetFavorites(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag* s) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEGetFavoritesFunctor(GetFavoritesLatent, vm, stackId, s)); + return true; + } + + void SetFavoritesLatent(UInt32 stackId, StaticFunctionTag *, VMArray forms) + { + for(int i = 0; i < FavoritesManager::kNumFavorites; ++i) + { + TESForm * form = nullptr; + if(i < forms.Length()) { + forms.Get(&form, i); + } + (*g_favoritesManager)->favorites[i] = form; + } + } + + DECLARE_DELAY_FUNCTOR(F4SESetFavoritesFunctor, 1, SetFavoritesLatent, StaticFunctionTag, void, VMArray); + + bool SetFavorites(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag* s, VMArray forms) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SESetFavoritesFunctor(SetFavoritesLatent, vm, stackId, s, forms)); + return true; + } +} + +void papyrusFavoritesManager::RegisterFuncs(VirtualMachine* vm) +{ + F4SEObjectRegistry& f4seObjRegistry = F4SEObjectRegistryInstance(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + + vm->RegisterFunction( + new LatentNativeFunction0>("GetTaggedForms", "FavoritesManager", papyrusFavoritesManager::GetTaggedForms, vm)); + + vm->RegisterFunction( + new LatentNativeFunction1>("AddTaggedForms", "FavoritesManager", papyrusFavoritesManager::AddTaggedForms, vm)); + + vm->RegisterFunction( + new LatentNativeFunction1>("RemoveTaggedForms", "FavoritesManager", papyrusFavoritesManager::RemoveTaggedForms, vm)); + + vm->RegisterFunction( + new LatentNativeFunction1("IsTaggedForm", "FavoritesManager", papyrusFavoritesManager::IsTaggedForm, vm)); + + vm->RegisterFunction( + new LatentNativeFunction0>("GetFavorites", "FavoritesManager", papyrusFavoritesManager::GetFavorites, vm)); + + vm->RegisterFunction( + new LatentNativeFunction1>("SetFavorites", "FavoritesManager", papyrusFavoritesManager::SetFavorites, vm)); + + vm->SetFunctionFlags("FavoritesManager", "GetTaggedForms", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("FavoritesManager", "AddTaggedForms", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("FavoritesManager", "RemoveTaggedForms", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("FavoritesManager", "IsTaggedForm", IFunction::kFunctionFlag_NoWait); + + vm->SetFunctionFlags("FavoritesManager", "GetFavorites", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("FavoritesManager", "SetFavorites", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusFavoritesManager.h b/f4se/f4se/PapyrusFavoritesManager.h new file mode 100644 index 0000000..88509cc --- /dev/null +++ b/f4se/f4se/PapyrusFavoritesManager.h @@ -0,0 +1,11 @@ +#pragma once + +class VirtualMachine; +struct StaticFunctionTag; + +#include "f4se/GameTypes.h" + +namespace papyrusFavoritesManager +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusForm.cpp b/f4se/f4se/PapyrusForm.cpp new file mode 100644 index 0000000..73ae75e --- /dev/null +++ b/f4se/f4se/PapyrusForm.cpp @@ -0,0 +1,363 @@ +#include "f4se/PapyrusForm.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/GameObjects.h" +#include "f4se/GameRTTI.h" + +namespace papyrusForm +{ + BSFixedString GetName(TESForm* thisForm) + { + if (!thisForm) + return BSFixedString(); + + TESFullName* pFullName = DYNAMIC_CAST(thisForm, TESForm, TESFullName); + if (pFullName) + return pFullName->name; + + return BSFixedString(); + } + + void SetName(TESForm* thisForm, BSFixedString nuName) + { + TESFullName* pFullName = DYNAMIC_CAST(thisForm, TESForm, TESFullName); + if (pFullName) { + pFullName->name = nuName; + } + } + + float GetWeight(TESForm* thisForm) + { + if (!thisForm) + return 0.0; + + TESWeightForm* pWeight = DYNAMIC_CAST(thisForm, TESForm, TESWeightForm); + if (pWeight) + return pWeight->weight; + return 0.0; + } + + void SetWeight(TESForm* thisForm, float nuWeight) + { + if (!thisForm) + return; + + TESWeightForm* pWeight = DYNAMIC_CAST(thisForm, TESForm, TESWeightForm); + if (pWeight) + pWeight->weight = nuWeight; + } + + void SetGoldValue(TESForm* thisForm, UInt32 value) + { + if (!thisForm) + return; + TESValueForm* pValue = DYNAMIC_CAST(thisForm, TESForm, TESValueForm); + if (pValue) + pValue->value = value; + } + + VMArray GetKeywords(TESForm* thisForm) + { + VMArray result; + if (!thisForm) + return result; + + BGSKeywordForm* pKeywords = DYNAMIC_CAST(thisForm, TESForm, BGSKeywordForm); + if (pKeywords) { + for(UInt32 i = 0; i < pKeywords->numKeywords; i++) + { + if(pKeywords->keywords[i]) + result.Push(&pKeywords->keywords[i]); + } + } + return result; + } + + bool HasWorldModel(TESForm * thisForm) + { + if(thisForm) { + TESModel* pWorldModel = DYNAMIC_CAST(thisForm, TESForm, TESModel); + if (pWorldModel) + return true; + } + + return false; + } + + BSFixedString GetWorldModelPath(TESForm * thisForm) + { + if(!thisForm) + return BSFixedString(); + + TESModel* pWorldModel = DYNAMIC_CAST(thisForm, TESForm, TESModel); + if (!pWorldModel) + return BSFixedString(); + + return pWorldModel->GetModelName(); + } + + void SetWorldModelPath(TESForm * thisForm, BSFixedString nuPath) + { + if(thisForm) { + TESModel* pWorldModel = DYNAMIC_CAST(thisForm, TESForm, TESModel); + if(pWorldModel) + pWorldModel->SetModelName(nuPath.c_str()); + } + } + + + BSFixedString GetIconPath(TESForm * thisForm) + { + if(!thisForm) + return BSFixedString(); + + TESIcon* pIcon = DYNAMIC_CAST(thisForm, TESForm, TESIcon); + return (pIcon) ? pIcon->str : BSFixedString(); + } + + void SetIconPath(TESForm * thisForm, BSFixedString nuPath) + { + if(thisForm) { + TESIcon* pIcon = DYNAMIC_CAST(thisForm, TESForm, TESIcon); + if(pIcon) + pIcon->str = nuPath; + } + } + + BSFixedString GetMessageIconPath(TESForm * thisForm) + { + if(!thisForm) + return BSFixedString(); + + BGSMessageIcon* pMessageIcon = DYNAMIC_CAST(thisForm, TESForm, BGSMessageIcon); + return (pMessageIcon) ? pMessageIcon->unk08.str : BSFixedString(); + } + + void SetMessageIconPath(TESForm * thisForm, BSFixedString nuPath) + { + if(thisForm) { + BGSMessageIcon* pMessageIcon = DYNAMIC_CAST(thisForm, TESForm, BGSMessageIcon); + if(pMessageIcon) + pMessageIcon->unk08.str = nuPath; + } + } + + EnchantmentItem* GetEnchantment(TESForm * thisForm) + { + if(!thisForm) + return nullptr; + + TESEnchantableForm* pEnchantment = DYNAMIC_CAST(thisForm, TESForm, TESEnchantableForm); + return (pEnchantment) ? pEnchantment->enchantment : nullptr; + } + + void SetEnchantment(TESForm * thisForm, EnchantmentItem* enchantment) + { + if (thisForm) { + TESEnchantableForm* pEnchantment = DYNAMIC_CAST(thisForm, TESForm, TESEnchantableForm); + if(pEnchantment) + pEnchantment->enchantment = enchantment; + } + } + + UInt32 GetEnchantmentValue(TESForm * thisForm) + { + if(!thisForm) + return 0; + + TESEnchantableForm* pEnchantment = DYNAMIC_CAST(thisForm, TESForm, TESEnchantableForm); + return (pEnchantment && pEnchantment->enchantment) ? pEnchantment->maxCharge : 0; + } + + void SetEnchantmentValue(TESForm * thisForm, UInt32 value) + { + if (thisForm) { + TESEnchantableForm* pEnchantment = DYNAMIC_CAST(thisForm, TESForm, TESEnchantableForm); + if(pEnchantment->enchantment) + pEnchantment->maxCharge = value; + } + } + + BGSEquipSlot * GetEquipType(TESForm * thisForm) + { + if(!thisForm) + return nullptr; + + BGSEquipType* pEquipType = DYNAMIC_CAST(thisForm, TESForm, BGSEquipType); + if (pEquipType) { + return pEquipType->GetEquipSlot(); + } + + // Invalid EquipSlot + return nullptr; + } + + void SetEquipType(TESForm * thisForm, BGSEquipSlot * slot) + { + if (thisForm && slot) { + BGSEquipType* pEquipType = DYNAMIC_CAST(thisForm, TESForm, BGSEquipType); + if(pEquipType) + pEquipType->SetEquipSlot(slot); + } + } + + BSFixedString GetDescription(TESForm * thisForm) + { + if(!thisForm) + return BSFixedString(); + + TESDescription * pDescription = DYNAMIC_CAST(thisForm, TESForm, TESDescription); + if(pDescription) { + BSString str; + CALL_MEMBER_FN(pDescription, Get)(&str, nullptr); + return str.Get(); + } + + return BSFixedString(); + } + + TESRace * GetRaceForm(TESForm * thisForm) + { + if(!thisForm) + return nullptr; + + TESRaceForm* pRaceForm = DYNAMIC_CAST(thisForm, TESForm, TESRaceForm); + if (pRaceForm) { + return pRaceForm->race; + } + + return nullptr; + } + + void SetRaceForm(TESForm * thisForm, TESRace * race) + { + if (thisForm && race) { + TESRaceForm* pRaceForm = DYNAMIC_CAST(thisForm, TESForm, TESRaceForm); + if(pRaceForm) + pRaceForm->race = race; + } + } + + UInt32 GetSlotMask(TESForm* thisForm) + { + BGSBipedObjectForm* pBipedObject = DYNAMIC_CAST(thisForm, TESForm, BGSBipedObjectForm); + return (pBipedObject) ? pBipedObject->GetSlotMask() : 0; + } + + void SetSlotMask(TESForm* thisForm, UInt32 slotMask) + { + if (thisForm) { + BGSBipedObjectForm* pBipedObject = DYNAMIC_CAST(thisForm, TESForm, BGSBipedObjectForm); + if(pBipedObject) + pBipedObject->SetSlotMask(slotMask); + } + } + + UInt32 AddSlotToMask(TESForm* thisForm, UInt32 slot) + { + BGSBipedObjectForm* pBipedObject = DYNAMIC_CAST(thisForm, TESForm, BGSBipedObjectForm); + return (pBipedObject) ? pBipedObject->AddSlotToMask(slot) : 0; + + } + + UInt32 RemoveSlotFromMask(TESForm* thisForm, UInt32 slot) + { + BGSBipedObjectForm* pBipedObject = DYNAMIC_CAST(thisForm, TESForm, BGSBipedObjectForm); + return (pBipedObject) ? pBipedObject->RemoveSlotFromMask(slot) : 0; + } + + UInt32 GetMaskForSlot(StaticFunctionTag*, UInt32 slot) + { + if (slot < 29 || slot > 61) + return 0; + + return (1 << (slot - 30)); + } +} + +void papyrusForm::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("GetName", "Form", papyrusForm::GetName, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetName", "Form", papyrusForm::SetName, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetDescription", "Form", papyrusForm::GetDescription, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetWeight", "Form", papyrusForm::GetWeight, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetWeight", "Form", papyrusForm::SetWeight, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetGoldValue", "Form", papyrusForm::SetGoldValue, vm)); + + vm->RegisterFunction( + new NativeFunction0 > ("GetKeywords", "Form", papyrusForm::GetKeywords, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("HasWorldModel", "Form", papyrusForm::HasWorldModel, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetWorldModelPath", "Form", papyrusForm::GetWorldModelPath, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetWorldModelPath", "Form", papyrusForm::SetWorldModelPath, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetIconPath", "Form", papyrusForm::GetIconPath, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetIconPath", "Form", papyrusForm::SetIconPath, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetMessageIconPath", "Form", papyrusForm::GetMessageIconPath, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetMessageIconPath", "Form", papyrusForm::SetMessageIconPath, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetEnchantment", "Form", papyrusForm::GetEnchantment, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetEnchantment", "Form", papyrusForm::SetEnchantment, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetEnchantmentValue", "Form", papyrusForm::GetEnchantmentValue, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetEnchantmentValue", "Form", papyrusForm::SetEnchantmentValue, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetEquipType", "Form", papyrusForm::GetEquipType, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetEquipType", "Form", papyrusForm::SetEquipType, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetRaceForm", "Form", papyrusForm::GetRaceForm, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetRaceForm", "Form", papyrusForm::SetRaceForm, vm)); + + // Slot Mask + vm->RegisterFunction( + new NativeFunction0 ("GetSlotMask", "Form", papyrusForm::GetSlotMask, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetSlotMask", "Form", papyrusForm::SetSlotMask, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("AddSlotToMask", "Form", papyrusForm::AddSlotToMask, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("RemoveSlotFromMask", "Form", papyrusForm::RemoveSlotFromMask, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetMaskForSlot", "Form", papyrusForm::GetMaskForSlot, vm)); +} diff --git a/f4se/f4se/PapyrusForm.h b/f4se/f4se/PapyrusForm.h new file mode 100644 index 0000000..f729166 --- /dev/null +++ b/f4se/f4se/PapyrusForm.h @@ -0,0 +1,11 @@ +#pragma once + +class VirtualMachine; +struct StaticFunctionTag; + +#include "f4se/GameTypes.h" + +namespace papyrusForm +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusGame.cpp b/f4se/f4se/PapyrusGame.cpp new file mode 100644 index 0000000..530f1ee --- /dev/null +++ b/f4se/f4se/PapyrusGame.cpp @@ -0,0 +1,221 @@ +#include "f4se/PapyrusGame.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/GameAPI.h" +#include "f4se/GameReferences.h" +#include "f4se/GameData.h" +#include "f4se/GameSettings.h" +#include "f4se/GameCamera.h" +#include "f4se/GameRTTI.h" + + +namespace papyrusGame +{ + DECLARE_STRUCT(PluginInfo, "Game") + + TESObjectREFR * GetCurrentConsoleRef(StaticFunctionTag * base) + { + UInt32 handle = (*g_consoleHandle); + TESObjectREFR * refr = NULL; + if(handle != 0 && handle != (*g_invalidRefHandle)) { + LookupREFRByHandle(&handle, &refr); + return refr; + } + + return nullptr; + } + + VMArray GetInstalledPlugins(StaticFunctionTag * base) + { + VMArray result; + UInt8 modCount = (*g_dataHandler)->modList.loadedMods.count; + for (UInt32 i = 0; i < modCount; i++) + { + ModInfo * modInfo = (*g_dataHandler)->modList.loadedMods[i]; + + PluginInfo info; + info.Set("index", modInfo->modIndex); + info.Set("name", modInfo->name); + info.Set("author", modInfo->author.Get()); + info.Set("description", modInfo->description.Get()); + result.Push(&info); + } + + return result; + } + + VMArray GetInstalledLightPlugins(StaticFunctionTag * base) + { + VMArray result; + UInt8 modCount = (*g_dataHandler)->modList.lightMods.count; + for (UInt32 i = 0; i < modCount; i++) + { + ModInfo * modInfo = (*g_dataHandler)->modList.lightMods[i]; + + PluginInfo info; + info.Set("index", modInfo->modIndex); + info.Set("name", modInfo->name); + info.Set("author", modInfo->author.Get()); + info.Set("description", modInfo->description.Get()); + result.Push(&info); + } + + return result; + } + + VMArray GetPluginDependencies(StaticFunctionTag * base, BSFixedString plugin) + { + VMArray result; + const ModInfo* pluginInfo = (*g_dataHandler)->LookupLoadedModByName(plugin); + if(!pluginInfo) + pluginInfo = (*g_dataHandler)->LookupLoadedLightModByName(plugin); + + if(pluginInfo) + { + for(UInt32 i = 0; i < pluginInfo->numRefMods; i++) + { + ModInfo * modInfo = pluginInfo->refModInfo[i]; + if(modInfo) { + BSFixedString modName(modInfo->name); + result.Push(&modName); + } + } + } + + return result; + } + + void SetGameSettingFloat(StaticFunctionTag * base, BSFixedString name, float value) + { + Setting * setting = GetGameSetting(name.c_str()); + if(setting) + { + if(!setting->SetDouble(value)) + { + _WARNING("SetGameSettingFloat: %s is not a float", name.data); + } + } + else + { + _WARNING("SetGameSettingFloat: %s not found", name.data); + } + } + + void SetGameSettingInt(StaticFunctionTag * base, BSFixedString name, UInt32 value) + { + Setting * setting = GetGameSetting(name.c_str()); + if(setting) + { + if(setting->GetType() == Setting::kType_Integer) + { + setting->data.u32 = value; + } + else + { + _WARNING("SetGameSettingInt: %s is not an int", name.data); + } + } + else + { + _WARNING("SetGameSettingInt: %s not found", name.data); + } + } + + void SetGameSettingBool(StaticFunctionTag * base, BSFixedString name, bool value) + { + Setting * setting = GetGameSetting(name.c_str()); + if(setting) + { + if(setting->GetType() == Setting::kType_Bool) + { + setting->data.u8 = value; + } + else + { + _WARNING("SetGameSettingBool: %s is not a bool", name.data); + } + } + else + { + _WARNING("SetGameSettingBool: %s not found", name.data); + } + } + + void SetGameSettingString(StaticFunctionTag * base, BSFixedString name, BSFixedString value) + { + Setting * setting = GetGameSetting(name.c_str()); + if(setting) + { + if(!setting->SetString(value.c_str())) + { + _WARNING("SetGameSettingString: %s is not a string", name.data); + } + } + else + { + _WARNING("SetGameSettingString: %s not found", name.data); + } + } + + void UpdateThirdPerson(StaticFunctionTag * base) + { + PlayerCharacter * player = *g_player; + PlayerCamera * playerCamera = *g_playerCamera; + if(playerCamera && player) + { + ThirdPersonState * thirdPersonCamera = DYNAMIC_CAST( + playerCamera->cameraStates[PlayerCamera::kCameraState_ThirdPerson2], + TESCameraState, ThirdPersonState); + if(thirdPersonCamera) + { + thirdPersonCamera->UpdateMode(player->actorState.IsWeaponDrawn()); + } + } + } + + SInt32 GetCameraState(StaticFunctionTag * base) + { + PlayerCamera * playerCamera = *g_playerCamera; + if(playerCamera) + return playerCamera->GetCameraStateId(playerCamera->cameraState); + + return -1; + } +} + +void papyrusGame::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("GetCurrentConsoleRef", "Game", papyrusGame::GetCurrentConsoleRef, vm)); + + vm->RegisterFunction( + new NativeFunction0 >("GetInstalledPlugins", "Game", papyrusGame::GetInstalledPlugins, vm)); + + vm->RegisterFunction( + new NativeFunction0 >("GetInstalledLightPlugins", "Game", papyrusGame::GetInstalledLightPlugins, vm)); + + vm->RegisterFunction( + new NativeFunction1 , BSFixedString>("GetPluginDependencies", "Game", papyrusGame::GetPluginDependencies, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetGameSettingFloat", "Game", papyrusGame::SetGameSettingFloat, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetGameSettingInt", "Game", papyrusGame::SetGameSettingInt, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetGameSettingBool", "Game", papyrusGame::SetGameSettingBool, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetGameSettingString", "Game", papyrusGame::SetGameSettingString, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("UpdateThirdPerson", "Game", papyrusGame::UpdateThirdPerson, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetCameraState", "Game", papyrusGame::GetCameraState, vm)); + + vm->SetFunctionFlags("Game", "GetCameraState", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusGame.h b/f4se/f4se/PapyrusGame.h new file mode 100644 index 0000000..ba03cc4 --- /dev/null +++ b/f4se/f4se/PapyrusGame.h @@ -0,0 +1,11 @@ +#pragma once + +class VirtualMachine; +struct StaticFunctionTag; + +#include "f4se/GameTypes.h" + +namespace papyrusGame +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusHeadPart.cpp b/f4se/f4se/PapyrusHeadPart.cpp new file mode 100644 index 0000000..46860e9 --- /dev/null +++ b/f4se/f4se/PapyrusHeadPart.cpp @@ -0,0 +1,76 @@ +#include "f4se/PapyrusHeadPart.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/GameObjects.h" +#include "f4se/GameData.h" + +namespace papyrusHeadPart +{ + UInt32 GetType(BGSHeadPart* thisPart) + { + if(!thisPart) + return 0; + return thisPart->type; + } + + VMArray GetExtraParts(BGSHeadPart* thisPart) + { + VMArray result; + if(!thisPart) + return result; + + BGSHeadPart* headPart; + for(UInt32 i = 0; i < thisPart->extraParts.count; i++) + { + thisPart->extraParts.GetNthItem(i, headPart); + result.Push(&headPart); + } + + return result; + } + + bool IsExtraPart(BGSHeadPart* thisPart) + { + return (thisPart && thisPart->IsExtraPart()) ? true : false; + } + + bool HasExtraPart(BGSHeadPart* thisPart, BGSHeadPart* extraPart) + { + return (thisPart && thisPart->extraParts.GetItemIndex(extraPart) != -1) ? true : false; + } + + BGSListForm* GetValidRaces(BGSHeadPart* thisPart) + { + return (thisPart) ? thisPart->validRaces : NULL; + } + + void SetValidRaces(BGSHeadPart* thisPart, BGSListForm* raceList) + { + if(thisPart && raceList) { + thisPart->validRaces = raceList; + } + } +} + +void papyrusHeadPart::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0("GetType", "HeadPart", papyrusHeadPart::GetType, vm)); + + vm->RegisterFunction( + new NativeFunction0>("GetExtraParts", "HeadPart", papyrusHeadPart::GetExtraParts, vm)); + + vm->RegisterFunction( + new NativeFunction1("HasExtraPart", "HeadPart", papyrusHeadPart::HasExtraPart, vm)); + + vm->RegisterFunction( + new NativeFunction0("IsExtraPart", "HeadPart", papyrusHeadPart::IsExtraPart, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetValidRaces", "HeadPart", papyrusHeadPart::GetValidRaces, vm)); + + vm->RegisterFunction( + new NativeFunction1("SetValidRaces", "HeadPart", papyrusHeadPart::SetValidRaces, vm)); +} diff --git a/f4se/f4se/PapyrusHeadPart.h b/f4se/f4se/PapyrusHeadPart.h new file mode 100644 index 0000000..29e6ffc --- /dev/null +++ b/f4se/f4se/PapyrusHeadPart.h @@ -0,0 +1,13 @@ +#pragma once + +#include "f4se/GameTypes.h" + +class BGSHeadPart; +class BGSListForm; +class VirtualMachine; +struct StaticFunctionTag; + +namespace papyrusHeadPart +{ + void RegisterFuncs(VirtualMachine* vm); +}; diff --git a/f4se/f4se/PapyrusInput.cpp b/f4se/f4se/PapyrusInput.cpp new file mode 100644 index 0000000..16e0894 --- /dev/null +++ b/f4se/f4se/PapyrusInput.cpp @@ -0,0 +1,107 @@ +#include "f4se/PapyrusInput.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/GameInput.h" +#include "f4se/InputMap.h" + +namespace papyrusInput +{ + SInt32 GetMappedKey(StaticFunctionTag* thisInput, BSFixedString name, UInt32 deviceType) + { + InputManager * inputManager = (*g_inputMgr); + if (!inputManager) + return -1; + + UInt32 key = 0xFF; + + // Manual device selection + if (deviceType != 0xFF) + { + key = inputManager->GetMappedKey(name, deviceType, InputManager::kContext_Gameplay); + } + // Auto-selected device + else + { + // Gamepad + if ((*g_inputDeviceMgr)->IsGamepadEnabled()) + { + deviceType = InputEvent::kDeviceType_Gamepad; + key = inputManager->GetMappedKey(name, InputEvent::kDeviceType_Gamepad, InputManager::kContext_Gameplay); + } + // Mouse + Keyboard + else + { + deviceType = InputEvent::kDeviceType_Keyboard; + key = inputManager->GetMappedKey(name, deviceType, InputManager::kContext_Gameplay); + if (key == 0xFF) + { + deviceType = InputEvent::kDeviceType_Mouse; + key = inputManager->GetMappedKey(name, deviceType, InputManager::kContext_Gameplay); + } + } + } + + if (key == 0xFF) + return -1; + + // Map to common value space + if (deviceType == InputEvent::kDeviceType_Mouse) + { + return key + InputMap::kMacro_MouseButtonOffset; + } + else if (deviceType == InputEvent::kDeviceType_Gamepad) + { + UInt32 mapped = InputMap::GamepadMaskToKeycode(key); + return (mapped != InputMap::kMaxMacros ? mapped : -1); + } + else + { + return key; + } + } + + BSFixedString GetMappedControl(StaticFunctionTag* thisInput, SInt32 keyCode) + { + if (keyCode < 0 || keyCode >= InputMap::kMaxMacros) + return BSFixedString(); + + InputManager * inputManager = (*g_inputMgr); + if (!inputManager) + return BSFixedString(); + + UInt32 buttonID; + UInt32 deviceType; + + if (keyCode >= InputMap::kMacro_GamepadOffset) + { + buttonID = InputMap::GamepadKeycodeToMask(keyCode); + deviceType = InputEvent::kDeviceType_Gamepad; + } + else if (keyCode >= InputMap::kMacro_MouseButtonOffset) + { + buttonID = keyCode - InputMap::kMacro_MouseButtonOffset; + deviceType = InputEvent::kDeviceType_Mouse; + } + else + { + buttonID = keyCode; + deviceType = InputEvent::kDeviceType_Keyboard; + } + + return inputManager->GetMappedControl(buttonID, deviceType, InputManager::kContext_Gameplay); + } +} + +void papyrusInput::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction2 ("GetMappedKey", "Input", papyrusInput::GetMappedKey, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetMappedControl", "Input", papyrusInput::GetMappedControl, vm)); + + vm->SetFunctionFlags("Input", "GetMappedKey", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Input", "GetMappedControl", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusInput.h b/f4se/f4se/PapyrusInput.h new file mode 100644 index 0000000..90d72e5 --- /dev/null +++ b/f4se/f4se/PapyrusInput.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusInput +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusInstanceData.cpp b/f4se/f4se/PapyrusInstanceData.cpp new file mode 100644 index 0000000..6b3b4d0 --- /dev/null +++ b/f4se/f4se/PapyrusInstanceData.cpp @@ -0,0 +1,844 @@ +#include "f4se/PapyrusInstanceData.h" + +#include "f4se/GameObjects.h" +#include "f4se/GameReferences.h" +#include "f4se/GameRTTI.h" +#include "f4se/GameExtraData.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +namespace papyrusInstanceData +{ + DECLARE_STRUCT(DamageTypeInfo, "InstanceData") + DECLARE_STRUCT(Owner, "InstanceData") + + TBO_InstanceData * GetInstanceData(Owner* thisInstance) + { + if(!thisInstance || thisInstance->IsNone()) + return nullptr; + + TESForm * form = nullptr; + ExtraDataList * extraDataList = nullptr; + + if(!thisInstance->Get("owner", &form)) + return nullptr; + + // Must be an inventory object, or doesn't exist + if(!form) { + VMRefOrInventoryObj ref; + if(!thisInstance->Get("owner", &ref)) + return nullptr; + + // Try to get ExtraDataList instead + ref.GetExtraData(&form, &extraDataList); + } + + // Passed a reference directly, just get the instance data directly + TESObjectREFR * refr = DYNAMIC_CAST(form, TESForm, TESObjectREFR); + if(refr) + extraDataList = refr->extraDataList; + + if(extraDataList) { + BSExtraData * extraData = extraDataList->GetByType(ExtraDataType::kExtraData_InstanceData); + if(extraData) { + ExtraInstanceData * objectModData = DYNAMIC_CAST(extraData, BSExtraData, ExtraInstanceData); + if(objectModData) + return objectModData->instanceData; + } + else { + TESBoundObject * boundObject = DYNAMIC_CAST(form, TESForm, TESBoundObject); + TBO_InstanceData * instanceData = nullptr; + if(boundObject) { + instanceData = boundObject->CloneInstanceData(nullptr); + if(instanceData) { + ExtraInstanceData * objectModData = ExtraInstanceData::Create(form, instanceData); + if(objectModData) { + extraDataList->Add(ExtraDataType::kExtraData_InstanceData, objectModData); + return instanceData; + } + } + } + } + } + + TESObjectWEAP * weapon = DYNAMIC_CAST(form, TESForm, TESObjectWEAP); + if(weapon) { + return &weapon->weapData; + } + + TESObjectARMO * armor = DYNAMIC_CAST(form, TESForm, TESObjectARMO); + if(armor) { + return &armor->instanceData; + } + + Actor * actor = DYNAMIC_CAST(form, TESForm, Actor); + if(actor) { + UInt32 iSlotIndex = 0; + if(!thisInstance->Get("slotIndex", &iSlotIndex)) + return nullptr; + + // Invalid slot id + if(iSlotIndex >= ActorEquipData::kMaxSlots) + return nullptr; + + ActorEquipData * equipData = actor->equipData; + if(!equipData) + return nullptr; + + // Make sure there is an item in this slot + auto item = equipData->slots[iSlotIndex].item; + if(!item) + return nullptr; + + return equipData->slots[iSlotIndex].instanceData; + } + + return nullptr; + } + + TESObjectWEAP::InstanceData * GetWeaponInstanceData(Owner* thisInstance) + { + TBO_InstanceData * instanceData = GetInstanceData(thisInstance); + if(!instanceData) + return nullptr; + + return (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + } + + TESObjectARMO::InstanceData * GetArmorInstanceData(Owner* thisInstance) + { + TBO_InstanceData * instanceData = GetInstanceData(thisInstance); + if(!instanceData) + return nullptr; + + return (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + } + + + UInt32 GetAttackDamage(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->baseDamage : 0; + } + + void SetAttackDamage(StaticFunctionTag*, Owner thisInstance, UInt32 nuDamage) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->baseDamage = max(0, min(nuDamage, 0xFFFF)); + } + } + + TESLevItem* GetAddAmmoList(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->addAmmoList : nullptr; + } + + void SetAddAmmoList(StaticFunctionTag*, Owner thisInstance, TESLevItem* item) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->addAmmoList = item; + } + } + + UInt32 GetAmmoCapacity(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->ammoCapacity : 0; + } + + void SetAmmoCapacity(StaticFunctionTag*, Owner thisInstance, UInt32 nuDamage) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->ammoCapacity = max(0, min(nuDamage, 0xFFFF)); + } + } + + TESAmmo * GetAmmo(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->ammo : nullptr; + } + + void SetAmmo(StaticFunctionTag*, Owner thisInstance, TESAmmo * ammo) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->ammo = ammo; + } + } + + VMArray GetDamageTypes(StaticFunctionTag*, Owner thisInstance) + { + VMArray result; + tArray * damageTypes = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(!instanceData) + return result; + + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + damageTypes = weaponInstance->damageTypes; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + damageTypes = armorInstance->damageTypes; + } + + if(!damageTypes) + return result; + + for(UInt32 i = 0; i < damageTypes->count; i++) + { + TESObjectWEAP::InstanceData::DamageTypes dt; + damageTypes->GetNthItem(i, dt); + + DamageTypeInfo dts; + dts.Set("type", dt.damageType); + dts.Set("damage", dt.value); + result.Push(&dts); + } + + return result; + } + + void SetDamageTypes(StaticFunctionTag*, Owner thisInstance, VMArray dts) + { + tArray ** damageTypes = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + damageTypes = &weaponInstance->damageTypes; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + damageTypes = &armorInstance->damageTypes; + } + + if(damageTypes) + { + if(!(*damageTypes)) + (*damageTypes) = new tArray(); + + (*damageTypes)->Clear(); + + for(UInt32 i = 0; i < dts.Length(); i++) + { + DamageTypeInfo dti; + dts.Get(&dti, i); + + UInt32 damage = 0; + TESForm * damageType = nullptr; + dti.Get("type", &damageType); + dti.Get("damage", &damage); + + if(damageType && damageType->formType == BGSDamageType::kTypeID) + { + TESObjectWEAP::InstanceData::DamageTypes dt; + dt.damageType = (BGSDamageType*)damageType; + dt.value = damage; + (*damageTypes)->Push(dt); + } + } + } + } + + UInt32 GetAccuracyBonus(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->accuracyBonus : 0; + } + + void SetAccuracyBonus(StaticFunctionTag*, Owner thisInstance, UInt32 nuDamage) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->accuracyBonus = max(0, min(nuDamage, 0xFF)); + } + } + + float GetCritMultiplier(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->critDamageMult : 0.0f; + } + + void SetCritMultiplier(StaticFunctionTag*, Owner thisInstance, float critDamage) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->critDamageMult = critDamage; + } + } + + float GetCritChargeBonus(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->critChargeBonus : 0.0f; + } + + void SetCritChargeBonus(StaticFunctionTag*, Owner thisInstance, float critDamage) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->critChargeBonus = critDamage; + } + } + + float GetAttackDelay(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->attackDelay : 0.0f; + } + + void SetAttackDelay(StaticFunctionTag*, Owner thisInstance, float attackDelay) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->attackDelay = attackDelay; + } + } + + float GetOutOfRangeMultiplier(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->outOfRangeMultiplier : 0.0f; + } + + void SetOutOfRangeMultiplier(StaticFunctionTag*, Owner thisInstance, float multiplier) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->outOfRangeMultiplier = multiplier; + } + } + + float GetActionPointCost(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->actionCost : 0.0f; + } + + void SetActionPointCost(StaticFunctionTag*, Owner thisInstance, float cost) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->actionCost = cost; + } + } + + float GetReloadSpeed(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->reloadSpeed : 0.0f; + } + + void SetReloadSpeed(StaticFunctionTag*, Owner thisInstance, float reloadSpeed) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->reloadSpeed = reloadSpeed; + } + } + + float GetReach(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->reach : 0; + } + + void SetReach(StaticFunctionTag*, Owner thisInstance, float nuReach) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if(instanceData) { + instanceData->reach = nuReach; + } + } + + float GetSpeed(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->speed : 0.0; + } + + void SetSpeed(StaticFunctionTag*, Owner thisInstance, float speed) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->speed = speed; + } + } + + UInt32 GetStagger(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->stagger : 0.0; + } + + void SetStagger(StaticFunctionTag*, Owner thisInstance, UInt32 stagger) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->stagger = max(0, min(stagger, 4)); + } + } + + float GetMinRange(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->minRange : 0.0; + } + + void SetMinRange(StaticFunctionTag*, Owner thisInstance, float minRange) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->minRange = minRange; + } + } + + float GetMaxRange(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? instanceData->maxRange : 0.0; + } + + void SetMaxRange(StaticFunctionTag*, Owner thisInstance, float maxRange) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + instanceData->maxRange = maxRange; + } + } + + ActorValueInfo * GetSkill(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return instanceData ? instanceData->skill : nullptr; + } + + void SetSkill(StaticFunctionTag*, Owner thisInstance, ActorValueInfo * skill) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if(instanceData) { + instanceData->skill = skill; + } + } + + ActorValueInfo * GetResist(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return instanceData ? instanceData->damageResist : nullptr; + } + + void SetResist(StaticFunctionTag*, Owner thisInstance, ActorValueInfo * resist) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if(instanceData) { + instanceData->damageResist = resist; + } + } + + BGSProjectile * GetProjectileOverride(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return instanceData && instanceData->firingData ? instanceData->firingData->projectileOverride : nullptr; + } + + void SetProjectileOverride(StaticFunctionTag*, Owner thisInstance, BGSProjectile * proj) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if(instanceData && instanceData->firingData) { + instanceData->firingData->projectileOverride = proj; + } + } + + UInt32 GetNumProjectiles(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return instanceData && instanceData->firingData ? instanceData->firingData->numProjectiles : 0; + } + + void SetNumProjectiles(StaticFunctionTag*, Owner thisInstance, UInt32 nuDamage) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if(instanceData && instanceData->firingData) { + instanceData->firingData->numProjectiles = max(0, min(nuDamage, 0xFF)); + } + } + + float GetSightedTransition(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return instanceData && instanceData->firingData ? instanceData->firingData->sightedTransition : 0; + } + + void SetSightedTransition(StaticFunctionTag*, Owner thisInstance, float nuDamage) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if(instanceData && instanceData->firingData) { + instanceData->firingData->sightedTransition = nuDamage; + } + } + + bool GetFlag(StaticFunctionTag*, Owner thisInstance, UInt32 flagNumber) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + return (instanceData) ? ((instanceData->flags & flagNumber) == flagNumber) : false; + } + + void SetFlag(StaticFunctionTag*, Owner thisInstance, UInt32 flagNumber, bool set) + { + auto instanceData = GetWeaponInstanceData(&thisInstance); + if (instanceData) { + if(set) { + instanceData->flags |= flagNumber; + } else { + instanceData->flags &= ~flagNumber; + } + } + } + + + UInt32 GetArmorHealth(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetArmorInstanceData(&thisInstance); + return instanceData ? instanceData->health : 0; + } + + void SetArmorHealth(StaticFunctionTag*, Owner thisInstance, UInt32 health) + { + auto instanceData = GetArmorInstanceData(&thisInstance); + if(instanceData) { + instanceData->health = health; + } + } + + UInt32 GetArmorRating(StaticFunctionTag*, Owner thisInstance) + { + auto instanceData = GetArmorInstanceData(&thisInstance); + return instanceData ? instanceData->armorRating : 0; + } + + void SetArmorRating(StaticFunctionTag*, Owner thisInstance, UInt32 health) + { + auto instanceData = GetArmorInstanceData(&thisInstance); + if(instanceData) { + instanceData->armorRating = health; + } + } + + float GetWeight(StaticFunctionTag*, Owner thisInstance) + { + float * weight = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + weight = &weaponInstance->weight; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + weight = &armorInstance->weight; + } + + return weight ? *weight : 0.0f; + } + + void SetWeight(StaticFunctionTag*, Owner thisInstance, float newWeight) + { + float * weight = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + weight = &weaponInstance->weight; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + weight = &armorInstance->weight; + } + + if(weight) + *weight = newWeight; + } + + UInt32 GetGoldValue(StaticFunctionTag*, Owner thisInstance) + { + UInt32 * value = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + value = &weaponInstance->value; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + value = &armorInstance->value; + } + + return value ? *value : 0; + } + + void SetGoldValue(StaticFunctionTag*, Owner thisInstance, UInt32 newValue) + { + UInt32 * value = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + value = &weaponInstance->value; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + value = &armorInstance->value; + } + + if(value) + *value = newValue; + } + + + VMArray GetKeywords(StaticFunctionTag*, Owner thisInstance) + { + VMArray result; + BGSKeywordForm * keywordForm = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(!instanceData) + return result; + + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + keywordForm = weaponInstance->keywords; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + keywordForm = armorInstance->keywords; + } + + if(!keywordForm) + return result; + + for(UInt32 i = 0; i < keywordForm->numKeywords; i++) + { + result.Push(&keywordForm->keywords[i]); + } + + return result; + } + + void SetKeywords(StaticFunctionTag*, Owner thisInstance, VMArray kwds) + { + BGSKeywordForm * keywordForm = nullptr; + TBO_InstanceData * instanceData = GetInstanceData(&thisInstance); + if(instanceData) { + auto weaponInstance = (TESObjectWEAP::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectWEAP__InstanceData); + if(weaponInstance) + keywordForm = weaponInstance->keywords; + + auto armorInstance = (TESObjectARMO::InstanceData*)Runtime_DynamicCast(instanceData, RTTI_TBO_InstanceData, RTTI_TESObjectARMO__InstanceData); + if(armorInstance) + keywordForm = armorInstance->keywords; + } + + if(keywordForm) + { + if(keywordForm->keywords) { + Heap_Free(keywordForm->keywords); + keywordForm->keywords = nullptr; + keywordForm->numKeywords = 0; + } + if(kwds.Length() > 0) + { + keywordForm->keywords = (BGSKeyword**)Heap_Allocate(sizeof(BGSKeyword*) * kwds.Length()); + keywordForm->numKeywords = kwds.Length(); + + for(UInt32 i = 0; i < kwds.Length(); i++) + { + BGSKeyword * kwd = nullptr; + kwds.Get(&kwd, i); + keywordForm->keywords[i] = kwd; + } + } + } + } +} + +void papyrusInstanceData::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction1 ("GetAttackDamage", "InstanceData", papyrusInstanceData::GetAttackDamage, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetAttackDamage", "InstanceData", papyrusInstanceData::SetAttackDamage, vm)); + + vm->RegisterFunction( + new NativeFunction1 , Owner>("GetDamageTypes", "InstanceData", papyrusInstanceData::GetDamageTypes, vm)); + + vm->RegisterFunction( + new NativeFunction2 >("SetDamageTypes", "InstanceData", papyrusInstanceData::SetDamageTypes, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetAmmoCapacity", "InstanceData", papyrusInstanceData::GetAmmoCapacity, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetAmmoCapacity", "InstanceData", papyrusInstanceData::SetAmmoCapacity, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetAmmo", "InstanceData", papyrusInstanceData::GetAmmo, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetAmmo", "InstanceData", papyrusInstanceData::SetAmmo, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetAddAmmoList", "InstanceData", papyrusInstanceData::GetAddAmmoList, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetAddAmmoList", "InstanceData", papyrusInstanceData::SetAddAmmoList, vm)); + + + vm->RegisterFunction( + new NativeFunction1 ("GetAccuracyBonus", "InstanceData", papyrusInstanceData::GetAccuracyBonus, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetAccuracyBonus", "InstanceData", papyrusInstanceData::SetAccuracyBonus, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetActionPointCost", "InstanceData", papyrusInstanceData::GetActionPointCost, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetActionPointCost", "InstanceData", papyrusInstanceData::SetActionPointCost, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetAttackDelay", "InstanceData", papyrusInstanceData::GetAttackDelay, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetAttackDelay", "InstanceData", papyrusInstanceData::SetAttackDelay, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetOutOfRangeMultiplier", "InstanceData", papyrusInstanceData::GetOutOfRangeMultiplier, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetOutOfRangeMultiplier", "InstanceData", papyrusInstanceData::SetOutOfRangeMultiplier, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetReloadSpeed", "InstanceData", papyrusInstanceData::GetReloadSpeed, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetReloadSpeed", "InstanceData", papyrusInstanceData::SetReloadSpeed, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetReach", "InstanceData", papyrusInstanceData::GetReach, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetReach", "InstanceData", papyrusInstanceData::SetReach, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetMinRange", "InstanceData", papyrusInstanceData::GetMinRange, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetMinRange", "InstanceData", papyrusInstanceData::SetMinRange, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetMaxRange", "InstanceData", papyrusInstanceData::GetMaxRange, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetMaxRange", "InstanceData", papyrusInstanceData::SetMaxRange, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetSpeed", "InstanceData", papyrusInstanceData::GetSpeed, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetSpeed", "InstanceData", papyrusInstanceData::SetSpeed, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetStagger", "InstanceData", papyrusInstanceData::GetStagger, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetStagger", "InstanceData", papyrusInstanceData::SetStagger, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetSkill", "InstanceData", papyrusInstanceData::GetSkill, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetSkill", "InstanceData", papyrusInstanceData::SetSkill, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetResist", "InstanceData", papyrusInstanceData::GetResist, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetResist", "InstanceData", papyrusInstanceData::SetResist, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetCritMultiplier", "InstanceData", papyrusInstanceData::GetCritMultiplier, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetCritMultiplier", "InstanceData", papyrusInstanceData::SetCritMultiplier, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetCritChargeBonus", "InstanceData", papyrusInstanceData::GetCritChargeBonus, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetCritChargeBonus", "InstanceData", papyrusInstanceData::SetCritChargeBonus, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetProjectileOverride", "InstanceData", papyrusInstanceData::GetProjectileOverride, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetProjectileOverride", "InstanceData", papyrusInstanceData::SetProjectileOverride, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetNumProjectiles", "InstanceData", papyrusInstanceData::GetNumProjectiles, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetNumProjectiles", "InstanceData", papyrusInstanceData::SetNumProjectiles, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetSightedTransition", "InstanceData", papyrusInstanceData::GetSightedTransition, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetSightedTransition", "InstanceData", papyrusInstanceData::SetSightedTransition, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("GetFlag", "InstanceData", papyrusInstanceData::GetFlag, vm)); + + vm->RegisterFunction( + new NativeFunction3 ("SetFlag", "InstanceData", papyrusInstanceData::SetFlag, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetArmorHealth", "InstanceData", papyrusInstanceData::GetArmorHealth, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetArmorHealth", "InstanceData", papyrusInstanceData::SetArmorHealth, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetArmorRating", "InstanceData", papyrusInstanceData::GetArmorRating, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetArmorRating", "InstanceData", papyrusInstanceData::SetArmorRating, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetWeight", "InstanceData", papyrusInstanceData::GetWeight, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetWeight", "InstanceData", papyrusInstanceData::SetWeight, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetGoldValue", "InstanceData", papyrusInstanceData::GetGoldValue, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("SetGoldValue", "InstanceData", papyrusInstanceData::SetGoldValue, vm)); + + vm->RegisterFunction( + new NativeFunction1 , Owner>("GetKeywords", "InstanceData", papyrusInstanceData::GetKeywords, vm)); + + vm->RegisterFunction( + new NativeFunction2 >("SetKeywords", "InstanceData", papyrusInstanceData::SetKeywords, vm)); +} diff --git a/f4se/f4se/PapyrusInstanceData.h b/f4se/f4se/PapyrusInstanceData.h new file mode 100644 index 0000000..f9054b7 --- /dev/null +++ b/f4se/f4se/PapyrusInstanceData.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusInstanceData +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusInterfaces.cpp b/f4se/f4se/PapyrusInterfaces.cpp new file mode 100644 index 0000000..4a1ff7c --- /dev/null +++ b/f4se/f4se/PapyrusInterfaces.cpp @@ -0,0 +1,21 @@ +#include "f4se/PapyrusInterfaces.h" + +// 84C7A0FD630AF430626C929A1B9A7FC62FDC358A+1B1 +RelocPtr g_objectHandlePolicy(0x0671E578); + +RelocAddr <_GetRefFromHandle> GetRefFromHandle(0x012C6590); + +RelocAddr <_GetVMPropertyInfo> GetVMPropertyInfo(0x026F3970); + +void IComplexType::AddRef(void) +{ + InterlockedIncrement(&m_refCount); +} + +void IComplexType::Release(void) +{ + if(!InterlockedDecrement(&m_refCount)) + { + delete this; + } +} diff --git a/f4se/f4se/PapyrusInterfaces.h b/f4se/f4se/PapyrusInterfaces.h new file mode 100644 index 0000000..ca46970 --- /dev/null +++ b/f4se/f4se/PapyrusInterfaces.h @@ -0,0 +1,230 @@ +#pragma once + +#include "f4se_common/Relocation.h" +#include "f4se_common/Utilities.h" + +#include "f4se/GameTypes.h" + +class VMIdentifier; + +// 08 +class IClientVM +{ +public: + virtual void Unk_01(void) = 0; + virtual void Unk_02(void) = 0; +}; + +// 08 +class IStackCallbackSaveInterface +{ +public: + virtual ~IStackCallbackSaveInterface(); + + virtual void Unk_01(void); + virtual void Unk_02(void); +}; + +// ?? +class IVMSaveLoadInterface +{ +public: + virtual ~IVMSaveLoadInterface(); + + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); + virtual bool Unk_06(); + virtual void Unk_07(); + virtual void Unk_08(); + virtual void Unk_09(); + virtual void Unk_0A(); + virtual void Unk_0B(); + virtual void Unk_0C(); + virtual void Unk_0D(); + virtual void Unk_0E(); + virtual void Unk_0F(); + virtual void Unk_10(); + virtual void Unk_11(); + virtual void Unk_12(); + virtual void Unk_13(); + virtual void Unk_14(); + virtual void Unk_15(); + virtual void Unk_16(); + virtual void Unk_17(); + virtual void Unk_18(); +}; + +// ?? +class IVMDebugInterface +{ +public: + virtual ~IVMDebugInterface(); + + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); +}; + +// 08 +class IMemoryPagePolicy +{ +public: + virtual ~IMemoryPagePolicy(); + + virtual void Unk_01() = 0; + virtual void Unk_02() = 0; + virtual void Unk_03() = 0; + virtual void Unk_04() = 0; + virtual void Unk_05() = 0; +}; + +// 28 +class SimpleAllocMemoryPagePolicy : public IMemoryPagePolicy +{ +public: + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); + + UInt64 unk08; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 + UInt64 unk20; // 20 +}; + +// ?? +class Logger +{ +public: + virtual void Unk_01(); + virtual void Unk_02(); +}; + +// 20 +class ILoader +{ +public: + virtual void Unk_01(); + virtual void Unk_02(); + + UInt64 unk08; // 08 + UInt64 unk10; // 10 + UInt64 unk18; // 18 +}; + +// 38 +class CompiledScriptLoader : public ILoader +{ +public: + UInt64 unk20; // 20 + UInt64 unk28; // 28 + UInt64 unk30; // 30 +}; + +// 08 +class IComplexType +{ +public: + virtual ~IComplexType(); + + virtual UInt32 GetType() = 0; + + SInt32 m_refCount; // 08 + UInt32 unk0C; // 0C + BSFixedString m_typeName; // 10 + IComplexType * m_parent; // 18 + + void AddRef(void); + void Release(void); +}; + +class IObjectHandlePolicy +{ +public: + IObjectHandlePolicy(); + virtual ~IObjectHandlePolicy(); + + virtual bool IsType(UInt32 typeID, UInt64 handle); + virtual bool Unk_02(UInt64 unk1, UInt32 * formType); + virtual bool Unk_03(UInt64 handle); + virtual bool HasBoundGameObject(UInt64 handle); + virtual bool Unk_05(UInt64 unk1); + virtual UInt64 GetInvalidHandle(void); + virtual UInt64 Create(UInt32 typeID, void * srcData); + virtual bool IsREFR(UInt64 handle); // return IsType(TESObjectREFR::kTypeID, handle); + virtual UInt64 Unk_09(UInt64 unk1); + virtual UInt32 Unk_0A(UInt64 unk1); + virtual UInt32 Unk_0B(UInt64 unk1); + virtual void * Resolve(UInt32 typeID, UInt64 handle); + virtual void AddRef(UInt64 handle); + virtual void Release(UInt64 handle); + virtual void GetName(UInt64 handle, void * outStr); +}; + +extern RelocPtr g_objectHandlePolicy; + +class IObjectBindPolicy +{ +public: + IObjectBindPolicy(); + virtual ~IObjectBindPolicy(); + + virtual void Unk_01(UInt64 unk); + virtual void Unk_02(UInt64 unk); + virtual void Unk_03(UInt64 unk); + virtual void Unk_04(UInt64 unk); + virtual SInt32 Unk_05(void); + virtual UInt32 Unk_06(UInt64 unk); + virtual UInt32 Unk_07(void); + virtual void Unk_08(void); + virtual void Unk_09(UInt64 unk0, UInt16 unk1, UInt64 unk2, UInt64 unk3); + virtual void * Unk_0A(void); + virtual void Unk_0B(UInt64 unk0, UInt64 unk1, UInt64 unk2, UInt8 unk3); + virtual void Unk_0C(UInt64 unk0, UInt64 unk1, UInt64 unk2, UInt8 unk3); + virtual UInt64 Unk_0D(UInt64 unk0, UInt64 unk1, UInt8 unk2, UInt64 unk3, UInt64 unk4); + virtual void Unk_0E(UInt64 unk0, UInt64 unk1, UInt8 unk2, UInt64 unk3, UInt64 unk4); + + MEMBER_FN_PREFIX(IObjectBindPolicy); + DEFINE_MEMBER_FN(BindObject, void, 0x026E3BC0, VMIdentifier ** identifier, UInt64 handle); +}; + +class TESObjectREFR; + +// 18 +struct VMRefHandle +{ + TESObjectREFR * refr; // 00 - May be null + TESObjectREFR * owner; // 08 + UInt16 uniqueId; // 10 +}; + +typedef VMRefHandle * (* _GetRefFromHandle)(VMRefHandle * ref, UInt64 handle); +extern RelocAddr <_GetRefFromHandle> GetRefFromHandle; + +class VMObjectTypeInfo; + +// 40 +struct VMPropertyInfo +{ + BSFixedString scriptName; // 00 + BSFixedString propertyName; // 08 + UInt64 unk10; // 10 + void* unk18; // 18 + void* unk20; // 20 + void* unk28; // 28 + SInt32 index; // 30 -1 if not found + UInt32 unk34; // 34 + BSFixedString unk38; // 38 +}; +STATIC_ASSERT(offsetof(VMPropertyInfo, index) == 0x30); +STATIC_ASSERT(sizeof(VMPropertyInfo) == 0x40); + +typedef VMPropertyInfo * (*_GetVMPropertyInfo)(VMObjectTypeInfo* objectTypeInfo, VMPropertyInfo * outInfo, BSFixedString* propertyName, bool unk4); // unk4 = 1 +extern RelocAddr <_GetVMPropertyInfo> GetVMPropertyInfo; \ No newline at end of file diff --git a/f4se/f4se/PapyrusLocation.cpp b/f4se/f4se/PapyrusLocation.cpp new file mode 100644 index 0000000..2268748 --- /dev/null +++ b/f4se/f4se/PapyrusLocation.cpp @@ -0,0 +1,57 @@ +#include "f4se/PapyrusLocation.h" + +#include "f4se/GameForms.h" +#include "f4se/PapyrusArgs.h" + +namespace papyrusLocation +{ + BGSLocation * GetParent(BGSLocation * thisLocation) + { + return thisLocation ? thisLocation->parent : nullptr; + } + + void SetParent(BGSLocation * thisLocation, BGSLocation * newLoc) + { + if(thisLocation) { + thisLocation->parent = newLoc; + } + } + + BGSEncounterZone * GetEncounterZone(BGSLocation * thisLocation, bool recursive) + { + BGSEncounterZone * result = nullptr; + while(!result && thisLocation) + { + result = thisLocation->encounterZone; + if(!recursive) + break; + thisLocation = thisLocation->parent; + } + return result; + } + + void SetEncounterZone(BGSLocation * thisLocation, BGSEncounterZone * encounterZone) + { + if(thisLocation) { + thisLocation->encounterZone = encounterZone; + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusLocation::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("GetParent", "Location", papyrusLocation::GetParent, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetParent", "Location", papyrusLocation::SetParent, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("GetEncounterZone", "Location", papyrusLocation::GetEncounterZone, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetEncounterZone", "Location", papyrusLocation::SetEncounterZone, vm)); +} diff --git a/f4se/f4se/PapyrusLocation.h b/f4se/f4se/PapyrusLocation.h new file mode 100644 index 0000000..8abab79 --- /dev/null +++ b/f4se/f4se/PapyrusLocation.h @@ -0,0 +1,8 @@ +#pragma once + +class VirtualMachine; + +namespace papyrusLocation +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusMaterialSwap.cpp b/f4se/f4se/PapyrusMaterialSwap.cpp new file mode 100644 index 0000000..80e6918 --- /dev/null +++ b/f4se/f4se/PapyrusMaterialSwap.cpp @@ -0,0 +1,64 @@ +#include "f4se/PapyrusMaterialSwap.h" + +#include "f4se/GameObjects.h" +#include "f4se/GameData.h" + +#include "f4se/PapyrusStruct.h" + +DECLARE_STRUCT(RemapData, "MatSwap"); + +namespace papyrusMaterialSwap +{ + VMArray GetRemapData(BGSMaterialSwap * materialSwap) + { + VMArray result; + if(!materialSwap) + return result; + + materialSwap->materialSwaps.ForEach([&](BGSMaterialSwap::MaterialSwap * swap) + { + RemapData swapData; + swapData.Set("source", swap->source); + swapData.Set("target", swap->target); + swapData.Set("colorIndex", swap->colorRemapIndex); + result.Push(&swapData); + return true; + }); + return result; + } + + void SetRemapData(BGSMaterialSwap * materialSwap, VMArray newData) + { + if(materialSwap && newData.Length()) + { + materialSwap->materialSwaps.Clear(); + + for(int i = 0; i < newData.Length(); ++i) + { + RemapData swapData; + newData.Get(&swapData, i); + + BGSMaterialSwap::MaterialSwap matSwap; + swapData.Get("source", &matSwap.source); + swapData.Get("target", &matSwap.target); + swapData.Get("colorIndex", &matSwap.colorRemapIndex); + matSwap.unk14 = 0; + materialSwap->materialSwaps.Add(&matSwap); + } + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusMaterialSwap::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterForm(BGSMaterialSwap::kTypeID, "MatSwap"); + + vm->RegisterFunction( + new NativeFunction0 >("GetRemapData", "MatSwap", papyrusMaterialSwap::GetRemapData, vm)); + + vm->RegisterFunction( + new NativeFunction1 >("SetRemapData", "MatSwap", papyrusMaterialSwap::SetRemapData, vm)); +} diff --git a/f4se/f4se/PapyrusMaterialSwap.h b/f4se/f4se/PapyrusMaterialSwap.h new file mode 100644 index 0000000..88a669c --- /dev/null +++ b/f4se/f4se/PapyrusMaterialSwap.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusMaterialSwap +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusMath.cpp b/f4se/f4se/PapyrusMath.cpp new file mode 100644 index 0000000..d4cb9d2 --- /dev/null +++ b/f4se/f4se/PapyrusMath.cpp @@ -0,0 +1,85 @@ +#include "f4se/PapyrusMath.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include + +namespace papyrusMath +{ + UInt32 LeftShift(StaticFunctionTag* base, UInt32 value, UInt32 shiftBy) + { + return (shiftBy >= 32) ? 0 : value << shiftBy; + } + + UInt32 RightShift(StaticFunctionTag* base, UInt32 value, UInt32 shiftBy) + { + return (shiftBy >= 32) ? 0 : value >> shiftBy; + } + + UInt32 LogicalAnd(StaticFunctionTag* base, UInt32 arg1, UInt32 arg2) + { + return arg1 & arg2; + } + + UInt32 LogicalOr(StaticFunctionTag* base, UInt32 arg1, UInt32 arg2) + { + return arg1 | arg2; + } + + UInt32 LogicalXor(StaticFunctionTag* base, UInt32 arg1, UInt32 arg2) + { + return arg1 ^ arg2; + } + + UInt32 LogicalNot(StaticFunctionTag* base, UInt32 arg1) + { + return ~arg1; + } + + float Log(StaticFunctionTag* base, float arg1) + { + return log(arg1); + } + + float Exp(StaticFunctionTag* base, float arg1) + { + return exp(arg1); + } +} + +void papyrusMath::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction2 ("LeftShift", "Math", papyrusMath::LeftShift, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("RightShift", "Math", papyrusMath::RightShift, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("LogicalAnd", "Math", papyrusMath::LogicalAnd, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("LogicalOr", "Math", papyrusMath::LogicalOr, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("LogicalXor", "Math", papyrusMath::LogicalXor, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("LogicalNot", "Math", papyrusMath::LogicalNot, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("Log", "Math", papyrusMath::Log, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("Exp", "Math", papyrusMath::Exp, vm)); + + vm->SetFunctionFlags("Math", "LeftShift", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Math", "RightShift", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Math", "LogicalAnd", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Math", "LogicalOr", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Math", "LogicalXor", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Math", "LogicalNot", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Math", "Log", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Math", "Exp", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusMath.h b/f4se/f4se/PapyrusMath.h new file mode 100644 index 0000000..ebc595e --- /dev/null +++ b/f4se/f4se/PapyrusMath.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusMath +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusMiscObject.cpp b/f4se/f4se/PapyrusMiscObject.cpp new file mode 100644 index 0000000..b26c47b --- /dev/null +++ b/f4se/f4se/PapyrusMiscObject.cpp @@ -0,0 +1,72 @@ +#include "f4se/papyrusMiscObject.h" + +#include "f4se/PapyrusArgs.h" +#include "f4se/PapyrusStruct.h" + +#include "f4se/GameExtraData.h" +#include "f4se/GameForms.h" +#include "f4se/GameObjects.h" +#include "f4se/GameRTTI.h" + +namespace papyrusMiscObject +{ + DECLARE_STRUCT(MiscComponent, "MiscObject") + + VMArray GetMiscComponents(TESObjectMISC * thisObject) + { + VMArray result; + if(!thisObject) + return result; + + if(!thisObject->components) + return result; + + for(UInt32 i = 0; i < thisObject->components->count; i++) + { + TESObjectMISC::Component cp; + thisObject->components->GetNthItem(i, cp); + + MiscComponent comp; + comp.Set("object", cp.component); + comp.Set("count", (UInt32)cp.count); + result.Push(&comp); + } + + return result; + } + + void SetMiscComponents(TESObjectMISC * thisObject, VMArray components) + { + if(thisObject) { + if(!thisObject->components) + thisObject->components = new tArray(); + + thisObject->components->Clear(); + + for(UInt32 i = 0; i < components.Length(); i++) + { + MiscComponent comp; + components.Get(&comp, i); + + UInt32 count; + TESObjectMISC::Component cp; + comp.Get("object", &cp.component); + comp.Get("count", &count); + cp.count = count; + thisObject->components->Push(cp); + } + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusMiscObject::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 >("GetMiscComponents", "MiscObject", papyrusMiscObject::GetMiscComponents, vm)); + + vm->RegisterFunction( + new NativeFunction1 >("SetMiscComponents", "MiscObject", papyrusMiscObject::SetMiscComponents, vm)); +} diff --git a/f4se/f4se/PapyrusMiscObject.h b/f4se/f4se/PapyrusMiscObject.h new file mode 100644 index 0000000..fce0ba4 --- /dev/null +++ b/f4se/f4se/PapyrusMiscObject.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusMiscObject +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusNativeFunctionDef.inl b/f4se/f4se/PapyrusNativeFunctionDef.inl new file mode 100644 index 0000000..30658ab --- /dev/null +++ b/f4se/f4se/PapyrusNativeFunctionDef.inl @@ -0,0 +1,27 @@ +// Native + +#define CLASS_NAME __MACRO_JOIN__(NativeFunction, NUM_PARAMS) + +#define VOID_SPEC 0 +#include "PapyrusNativeFunctionDef_Base.inl" + +#define VOID_SPEC 1 +#include "PapyrusNativeFunctionDef_Base.inl" + +#undef CLASS_NAME + +// Latent native + +#define CLASS_NAME __MACRO_JOIN__(LatentNativeFunction, NUM_PARAMS) +#define LATENT_SPEC 1 + +#define VOID_SPEC 0 +#include "PapyrusNativeFunctionDef_Base.inl" + +#define VOID_SPEC 1 +#include "PapyrusNativeFunctionDef_Base.inl" + +#undef LATENT_SPEC +#undef CLASS_NAME + +#undef NUM_PARAMS diff --git a/f4se/f4se/PapyrusNativeFunctionDef_Base.inl b/f4se/f4se/PapyrusNativeFunctionDef_Base.inl new file mode 100644 index 0000000..e7c5048 --- /dev/null +++ b/f4se/f4se/PapyrusNativeFunctionDef_Base.inl @@ -0,0 +1,308 @@ +#if NUM_PARAMS > 10 +#error PapyrusNativeFunctionDef: too many params +#endif + +template = 1 +,typename T_Arg0 +#endif +#if NUM_PARAMS >= 2 +,typename T_Arg1 +#endif +#if NUM_PARAMS >= 3 +,typename T_Arg2 +#endif +#if NUM_PARAMS >= 4 +,typename T_Arg3 +#endif +#if NUM_PARAMS >= 5 +,typename T_Arg4 +#endif +#if NUM_PARAMS >= 6 +,typename T_Arg5 +#endif +#if NUM_PARAMS >= 7 +,typename T_Arg6 +#endif +#if NUM_PARAMS >= 8 +,typename T_Arg7 +#endif +#if NUM_PARAMS >= 9 +,typename T_Arg8 +#endif +#if NUM_PARAMS >= 10 +,typename T_Arg9 +#endif + +> + +class CLASS_NAME +#if VOID_SPEC + + = 1 + , T_Arg0 +#endif +#if NUM_PARAMS >= 2 + , T_Arg1 +#endif +#if NUM_PARAMS >= 3 + , T_Arg2 +#endif +#if NUM_PARAMS >= 4 + , T_Arg3 +#endif +#if NUM_PARAMS >= 5 + , T_Arg4 +#endif +#if NUM_PARAMS >= 6 + , T_Arg5 +#endif +#if NUM_PARAMS >= 7 + , T_Arg6 +#endif +#if NUM_PARAMS >= 8 + , T_Arg7 +#endif +#if NUM_PARAMS >= 9 + , T_Arg8 +#endif +#if NUM_PARAMS >= 10 + , T_Arg9 +#endif + + > + +#endif + : public NativeFunction +{ +public: + + typedef +#if LATENT_SPEC + bool +#elif VOID_SPEC + void +#else + T_Result +#endif + (* CallbackType)( +#if LATENT_SPEC + VirtualMachine * vm, UInt32 stackId, T_Base * base +#else + T_Base * base +#endif +#if NUM_PARAMS >= 1 + , T_Arg0 arg0 +#endif +#if NUM_PARAMS >= 2 + , T_Arg1 arg1 +#endif +#if NUM_PARAMS >= 3 + , T_Arg2 arg2 +#endif +#if NUM_PARAMS >= 4 + , T_Arg3 arg3 +#endif +#if NUM_PARAMS >= 5 + , T_Arg4 arg4 +#endif +#if NUM_PARAMS >= 6 + , T_Arg5 arg5 +#endif +#if NUM_PARAMS >= 7 + , T_Arg6 arg6 +#endif +#if NUM_PARAMS >= 8 + , T_Arg7 arg7 +#endif +#if NUM_PARAMS >= 9 + , T_Arg8 arg8 +#endif +#if NUM_PARAMS >= 10 + , T_Arg9 arg9 +#endif + ); + + CLASS_NAME(const char * fnName, const char * className, CallbackType callback, VirtualMachine * vm) + : NativeFunction(fnName, className, IsStaticType ::value, NUM_PARAMS) + { + // store callback + m_callback = (void *)callback; + + InitParams(vm); + } + + void InitParams(VirtualMachine * vm) + { +#if NUM_PARAMS >= 1 + m_params.data[0].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 2 + m_params.data[1].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 3 + m_params.data[2].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 4 + m_params.data[3].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 5 + m_params.data[4].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 6 + m_params.data[5].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 7 + m_params.data[6].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 8 + m_params.data[7].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 9 + m_params.data[8].type64 = GetTypeID (vm); +#endif +#if NUM_PARAMS >= 10 + m_params.data[9].type64 = GetTypeID (vm); +#endif + +#if LATENT_SPEC + m_isLatent = true; +#endif + +#if VOID_SPEC + m_retnType = GetTypeID (vm); +#else + m_retnType = GetTypeID (vm); +#endif + } + + virtual ~CLASS_NAME() { } + + virtual bool Run(VMValue * baseValue, VirtualMachine * vm, UInt32 stackId, VMValue * resultValue, VMState * state) + { + // get argument list + UInt32 argOffset = CALL_MEMBER_FN(state->argList, GetOffset)(state); + + T_Base * base = NULL; + + // extract base object pointer for non-static types + if (! IsStaticType ::value) + { + UnpackValue(&base, baseValue); + if (!base) return false; + } + + // extract parameters +#if NUM_PARAMS >= 1 + T_Arg0 arg0; + UnpackValue(&arg0, CALL_MEMBER_FN(state->argList, Get)(state, 0, argOffset)); +#endif +#if NUM_PARAMS >= 2 + T_Arg1 arg1; + UnpackValue(&arg1, CALL_MEMBER_FN(state->argList, Get)(state, 1, argOffset)); +#endif +#if NUM_PARAMS >= 3 + T_Arg2 arg2; + UnpackValue(&arg2, CALL_MEMBER_FN(state->argList, Get)(state, 2, argOffset)); +#endif +#if NUM_PARAMS >= 4 + T_Arg3 arg3; + UnpackValue(&arg3, CALL_MEMBER_FN(state->argList, Get)(state, 3, argOffset)); +#endif +#if NUM_PARAMS >= 5 + T_Arg4 arg4; + UnpackValue(&arg4, CALL_MEMBER_FN(state->argList, Get)(state, 4, argOffset)); +#endif +#if NUM_PARAMS >= 6 + T_Arg5 arg5; + UnpackValue(&arg5, CALL_MEMBER_FN(state->argList, Get)(state, 5, argOffset)); +#endif +#if NUM_PARAMS >= 7 + T_Arg6 arg6; + UnpackValue(&arg6, CALL_MEMBER_FN(state->argList, Get)(state, 6, argOffset)); +#endif +#if NUM_PARAMS >= 8 + T_Arg7 arg7; + UnpackValue(&arg7, CALL_MEMBER_FN(state->argList, Get)(state, 7, argOffset)); +#endif +#if NUM_PARAMS >= 9 + T_Arg8 arg8; + UnpackValue(&arg8, CALL_MEMBER_FN(state->argList, Get)(state, 8, argOffset)); +#endif +#if NUM_PARAMS >= 10 + T_Arg9 arg9; + UnpackValue(&arg9, CALL_MEMBER_FN(state->argList, Get)(state, 9, argOffset)); +#endif + + +#if LATENT_SPEC + bool result = +#elif !VOID_SPEC + T_Result result = +#endif + ((CallbackType)m_callback)( +#if LATENT_SPEC + vm, stackId, base +#else + base +#endif +#if NUM_PARAMS >= 1 + , arg0 +#endif +#if NUM_PARAMS >= 2 + , arg1 +#endif +#if NUM_PARAMS >= 3 + , arg2 +#endif +#if NUM_PARAMS >= 4 + , arg3 +#endif +#if NUM_PARAMS >= 5 + , arg4 +#endif +#if NUM_PARAMS >= 6 + , arg5 +#endif +#if NUM_PARAMS >= 7 + , arg6 +#endif +#if NUM_PARAMS >= 8 + , arg7 +#endif +#if NUM_PARAMS >= 9 + , arg8 +#endif +#if NUM_PARAMS >= 10 + , arg9 +#endif + ); + + // pack the result +#if LATENT_SPEC + resultValue->SetBool(result); +#elif VOID_SPEC + resultValue->SetNone(); +#else + PackValue(resultValue, &result, vm); +#endif + if (! IsStaticType ::value) + { + DestroyValue(&base); + } + return true; + } + +private: + // hide + CLASS_NAME(); +}; + +#undef VOID_SPEC diff --git a/f4se/f4se/PapyrusNativeFunctions.cpp b/f4se/f4se/PapyrusNativeFunctions.cpp new file mode 100644 index 0000000..72498f0 --- /dev/null +++ b/f4se/f4se/PapyrusNativeFunctions.cpp @@ -0,0 +1 @@ +#include "PapyrusNativeFunctions.h" diff --git a/f4se/f4se/PapyrusNativeFunctions.h b/f4se/f4se/PapyrusNativeFunctions.h new file mode 100644 index 0000000..715a3da --- /dev/null +++ b/f4se/f4se/PapyrusNativeFunctions.h @@ -0,0 +1,189 @@ +#pragma once + +#include "f4se/GameTypes.h" +#include "f4se/PapyrusArgs.h" + +class VirtualMachine; +class VMState; +class VMValue; + +struct StaticFunctionTag +{ + enum { kTypeID = 0 }; +}; + +class IFunction +{ +public: + enum + { + kFunctionFlag_NoWait = 0x01 // set this only if your function is thread-safe + }; + + IFunction() { } + virtual ~IFunction() { } + +// void ** _vtbl; // 00 + UInt32 refCount; // 08 BSIntrusiveRefCounted + UInt32 pad0C; // 0C + + virtual BSFixedString * GetName() = 0; + virtual BSFixedString * GetClassName() = 0; + virtual BSFixedString * GetStr20() = 0; + virtual UInt64 * GetReturnType(UInt64 * dst) = 0; + virtual UInt64 GetNumParams() = 0; + virtual UInt64 GetParam(UInt32 idx, BSFixedString * outName, UInt64 * outType) = 0; + virtual UInt64 GetNumParams2() = 0; + virtual bool IsNative() = 0; + virtual bool IsStatic() = 0; + virtual bool Unk_0A() = 0; + virtual UInt32 Unk_0B() = 0; + virtual UInt32 GetUnk44() = 0; + virtual BSFixedString * GetStr48() = 0; + virtual void Unk_0E() = 0; + virtual UInt32 Invoke(void * arg0, void * arg1, VirtualMachine * arg2, VMState * arg3) = 0; + virtual BSFixedString * GetSourceFile() = 0; // guess + virtual bool Unk_11(UInt32 arg0, UInt32 * arg1) = 0; + virtual bool GetParamName(UInt32 idx, BSFixedString * out) = 0; + virtual UInt32 GetUnk41() = 0; + virtual void SetUnk41(UInt8 arg) = 0; +}; + +STATIC_ASSERT(sizeof(IFunction) == 0x10); + +// 50 +class NativeFunctionBase : public IFunction +{ +public: + NativeFunctionBase() { } + virtual ~NativeFunctionBase() { } + + // 10 + struct ParameterInfo + { + // 10 + struct Entry + { + BSFixedString name; // 00 + + union // 08 + { + UInt32 type; // 08 + UInt64 type64; // 08 + }; + }; + + Entry * data; // new [] + UInt16 numParams; + UInt16 realNumParams; + UInt32 pad0C; + + MEMBER_FN_PREFIX(ParameterInfo); + DEFINE_MEMBER_FN(GetParam, UInt64, 0x0270DE00, UInt32 idx, BSFixedString * outName, UInt64 * outType); + }; + + virtual BSFixedString * GetName() { return &m_fnName; } + virtual BSFixedString * GetClassName() { return &m_className; } + virtual BSFixedString * GetStr20() { return &m_unk20; } + virtual UInt64 * GetReturnType(UInt64 * dst) { *dst = m_retnType; return dst; } + virtual UInt64 GetNumParams() { return m_params.realNumParams; } + virtual UInt64 GetParam(UInt32 idx, BSFixedString * outName, UInt64 * outType) + { return CALL_MEMBER_FN(&m_params, GetParam)(idx, outName, outType); } + virtual UInt64 GetNumParams2() { return m_params.numParams; } + virtual bool IsNative() { return true; } + virtual bool IsStatic() { return m_isStatic; } + virtual bool Unk_0A() { return false; } + virtual UInt32 Unk_0B() { return 0; } + virtual UInt32 GetUnk44() { return m_unk44; } + virtual BSFixedString * GetStr48() { return &m_unk48; } + virtual void Unk_0E() { } + virtual UInt32 Invoke(void * arg0, void * arg1, VirtualMachine * arg2, VMState * arg3) + { return CALL_MEMBER_FN(this, Impl_Invoke)(arg0, arg1, arg2, arg3); } + virtual BSFixedString * GetSourceFile() { return CALL_MEMBER_FN(this, Impl_GetSourceFile)(); } + virtual bool Unk_11(UInt32 arg0, UInt32 * arg1) { *arg1 = 0; return false; } + virtual bool GetParamName(UInt32 idx, BSFixedString * out) + { return CALL_MEMBER_FN(this, Impl_GetParamName)(idx, out); } + virtual UInt32 GetUnk41() { return m_unk41; } + virtual void SetUnk41(UInt8 arg) { m_unk41 = arg; } + virtual bool HasCallback() = 0; + virtual bool Run(VMValue * baseValue, VirtualMachine * vm, UInt32 arg2, VMValue * resultValue, VMState * state) = 0; + + MEMBER_FN_PREFIX(NativeFunctionBase); + DEFINE_MEMBER_FN(Impl_Invoke, UInt32, 0x0270D550, void * arg0, void * arg1, VirtualMachine * arg2, VMState * arg3); + DEFINE_MEMBER_FN(Impl_GetSourceFile, BSFixedString *, 0x0270D420); + DEFINE_MEMBER_FN(Impl_GetParamName, bool, 0x0270D440, UInt32 idx, BSFixedString * out); + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free); + +protected: + BSFixedString m_fnName; // 10 + BSFixedString m_className; // 18 + BSFixedString m_unk20; // 20 + UInt64 m_retnType; // 28 + ParameterInfo m_params; // 30 + bool m_isStatic; // 40 + UInt8 m_unk41; // 41 + bool m_isLatent; // 42 - is latent + UInt8 m_pad43; // 43 + UInt32 m_unk44; // 44 + BSFixedString m_unk48; // 48 +}; + +STATIC_ASSERT(sizeof(NativeFunctionBase) == 0x50); + +// 58 +class NativeFunction : public NativeFunctionBase +{ +public: + NativeFunction(const char * fnName, const char * className, bool isStatic, UInt32 numParams) + { CALL_MEMBER_FN(this, Impl_ctor)(fnName, className, isStatic, numParams); } + virtual ~NativeFunction() { CALL_MEMBER_FN(this, Impl_dtor)(); } + + virtual bool HasCallback() { return m_callback != nullptr; } + virtual bool Run(VMValue * baseValue, VirtualMachine * vm, UInt32 arg2, VMValue * resultValue, VMState * state) = 0; + + MEMBER_FN_PREFIX(NativeFunction); + DEFINE_MEMBER_FN(Impl_ctor, NativeFunction *, 0x0270DA50, const char * fnName, const char * className, UInt32 unk0, UInt32 numParams); + DEFINE_MEMBER_FN(Impl_dtor, void, 0x0270DC70); + +protected: + void * m_callback; // 50 + + // hide + NativeFunction(); +}; + +STATIC_ASSERT(sizeof(NativeFunction) == 0x58); + +#define NUM_PARAMS 0 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 1 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 2 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 3 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 4 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 5 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 6 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 7 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 8 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 9 +#include "PapyrusNativeFunctionDef.inl" + +#define NUM_PARAMS 10 +#include "PapyrusNativeFunctionDef.inl" diff --git a/f4se/f4se/PapyrusObjectMod.cpp b/f4se/f4se/PapyrusObjectMod.cpp new file mode 100644 index 0000000..7140ccd --- /dev/null +++ b/f4se/f4se/PapyrusObjectMod.cpp @@ -0,0 +1,179 @@ +#include "f4se/PapyrusObjectMod.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" +#include "f4se/GameObjects.h" + +#include "f4se/PapyrusStruct.h" + +namespace papyrusObjectMod +{ + DECLARE_STRUCT(PropertyModifier, "ObjectMod") + + VMArray GetPropertyModifiers(BGSMod::Attachment::Mod * thisMod) + { + VMArray result; + if(!thisMod) + return result; + + UInt32 targetOffset = 0; + switch(thisMod->targetType) { + case BGSMod::Attachment::Mod::kTargetType_Weapon: + targetOffset = BGSMod::Container::kWeaponTarget_Offset; + break; + case BGSMod::Attachment::Mod::kTargetType_Armor: + targetOffset = BGSMod::Container::kArmorTarget_Offset; + break; + case BGSMod::Attachment::Mod::kTargetType_Actor: + targetOffset = BGSMod::Container::kActorTarget_Offset; + break; + } + + for(UInt32 i = 0; i < thisMod->modContainer.dataSize / sizeof(BGSMod::Container::Data); i++) + { + PropertyModifier propMod; + + UInt32 targetType = targetOffset; + UInt32 op = 0; + TESForm * form = nullptr; + float value1 = 0.0f; + float value2 = 0.0f; + + BGSMod::Container::Data * data = &thisMod->modContainer.data[i]; + targetType += data->target; + + switch(data->op) + { + case BGSMod::Container::Data::kOpFlag_Set_Bool: + case BGSMod::Container::Data::kOpFlag_Or_Bool: + case BGSMod::Container::Data::kOpFlag_And_Bool: + case BGSMod::Container::Data::kOpFlag_Set_Enum: + case BGSMod::Container::Data::kOpFlag_Set_Int: + case BGSMod::Container::Data::kOpFlag_Add_Int: + { + value1 = data->value.i.v1; + value2 = data->value.i.v2; + + switch(data->op) + { + case BGSMod::Container::Data::kOpFlag_Set_Bool: op = BGSMod::Container::kOperator_Set; break; + case BGSMod::Container::Data::kOpFlag_Or_Bool: op = BGSMod::Container::kOperator_Or; break; + case BGSMod::Container::Data::kOpFlag_And_Bool: op = BGSMod::Container::kOperator_And; break; + case BGSMod::Container::Data::kOpFlag_Set_Enum: op = BGSMod::Container::kOperator_Set; break; + case BGSMod::Container::Data::kOpFlag_Set_Int: op = BGSMod::Container::kOperator_Set; break; + case BGSMod::Container::Data::kOpFlag_Add_Int: op = BGSMod::Container::kOperator_Add; break; + } + } + break; + case BGSMod::Container::Data::kOpFlag_Mul_Add_Float: + //case BGSMod::Container::Data::kOpFlag_Mul_Add_Int: // Yes, mult int is treated the same as float + { + value1 = data->value.f.v1; + value2 = data->value.f.v2; + + switch(data->op) + { + case BGSMod::Container::Data::kOpFlag_Mul_Add_Float: op = BGSMod::Container::kOperator_Mult_Add; break; + case BGSMod::Container::Data::kOpFlag_Set_Float: op = BGSMod::Container::kOperator_Set; break; + case BGSMod::Container::Data::kOpFlag_Add_Float: op = BGSMod::Container::kOperator_Add; break; + } + } + break; + case BGSMod::Container::Data::kOpFlag_Set_Form: + case BGSMod::Container::Data::kOpFlag_Add_Form: + case BGSMod::Container::Data::kOpFlag_Rem_Form: + { + form = data->value.form; + + switch(data->op) + { + case BGSMod::Container::Data::kOpFlag_Set_Form: op = BGSMod::Container::kOperator_Set; break; + case BGSMod::Container::Data::kOpFlag_Add_Form: op = BGSMod::Container::kOperator_Add; break; + case BGSMod::Container::Data::kOpFlag_Rem_Form: op = BGSMod::Container::kOperator_Rem; break; + } + } + break; + case BGSMod::Container::Data::kOpFlag_Set_Form_Float: + case BGSMod::Container::Data::kOpFlag_Add_Form_Float: + case BGSMod::Container::Data::kOpFlag_Mul_Add_Form_Float: + { + form = LookupFormByID(data->value.ff.formId); + value1 = data->value.ff.v2; + + switch(data->op) + { + case BGSMod::Container::Data::kOpFlag_Set_Form_Float: op = BGSMod::Container::kOperator_Set; break; + case BGSMod::Container::Data::kOpFlag_Add_Form_Float: op = BGSMod::Container::kOperator_Add; break; + case BGSMod::Container::Data::kOpFlag_Mul_Add_Form_Float: op = BGSMod::Container::kOperator_Mult_Add; break; + } + } + break; + } + + propMod.Set("target", targetType); + propMod.Set("operator", op); + propMod.Set("object", form); + propMod.Set("value1", value1); + propMod.Set("value2", value2); + + result.Push(&propMod); + } + + return result; + } + + UInt32 GetMaxRank(BGSMod::Attachment::Mod * thisMod) + { + return thisMod ? thisMod->maxRank : 0; + } + + void SetMaxRank(BGSMod::Attachment::Mod * thisMod, UInt32 nuDamage) + { + if(thisMod) { + thisMod->maxRank = max(0, min(nuDamage, 0xFF)); + } + } + + UInt32 GetPriority(BGSMod::Attachment::Mod * thisMod) + { + return thisMod ? thisMod->priority : 0; + } + + void SetPriority(BGSMod::Attachment::Mod * thisMod, UInt32 nuDamage) + { + if(thisMod) { + thisMod->priority = max(0, min(nuDamage, 0xFF)); + } + } + + TESObjectMISC * GetLooseMod(BGSMod::Attachment::Mod * thisMod) + { + auto pair = g_modAttachmentMap->Find(&thisMod); + if(pair) { + return pair->miscObject; + } + + return nullptr; + } +} + +void papyrusObjectMod::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 >("GetPropertyModifiers", "ObjectMod", papyrusObjectMod::GetPropertyModifiers, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetMaxRank", "ObjectMod", papyrusObjectMod::GetMaxRank, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetMaxRank", "ObjectMod", papyrusObjectMod::SetMaxRank, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetPriority", "ObjectMod", papyrusObjectMod::GetPriority, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetPriority", "ObjectMod", papyrusObjectMod::SetPriority, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetLooseMod", "ObjectMod", papyrusObjectMod::GetLooseMod, vm)); +} diff --git a/f4se/f4se/PapyrusObjectMod.h b/f4se/f4se/PapyrusObjectMod.h new file mode 100644 index 0000000..66a70f0 --- /dev/null +++ b/f4se/f4se/PapyrusObjectMod.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusObjectMod +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusObjectReference.cpp b/f4se/f4se/PapyrusObjectReference.cpp new file mode 100644 index 0000000..0bd2bd6 --- /dev/null +++ b/f4se/f4se/PapyrusObjectReference.cpp @@ -0,0 +1,750 @@ +#include "f4se/PapyrusObjectReference.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" +#include "f4se/PapyrusUtilities.h" + +#include "f4se/GameReferences.h" +#include "f4se/GameObjects.h" +#include "f4se/GameExtraData.h" +#include "f4se/GameRTTI.h" +#include "f4se/GameData.h" +#include "f4se/GameWorkshop.h" + +#include "f4se/BSGeometry.h" +#include "f4se/NiExtraData.h" +#include "f4se/NiObjects.h" +#include "f4se/NiNodes.h" +#include "f4se/NiMaterials.h" +#include "f4se/bhkWorld.h" + +#include "f4se/Serialization.h" + +#include "f4se/PapyrusDelayFunctors.h" + +#include +#include +#include + +DECLARE_STRUCT(ConnectPoint, "ObjectReference"); +DECLARE_EXTERN_STRUCT(RemapData); + +namespace papyrusObjectReference { + + VMArray GetAllMods(VMRefOrInventoryObj * thisObj) + { + TESForm * baseForm = nullptr; + ExtraDataList * extraDataList = nullptr; + thisObj->GetExtraData(&baseForm, &extraDataList); + + VMArray result; + if(extraDataList) + { + BSExtraData * extraData = extraDataList->GetByType(ExtraDataType::kExtraData_ObjectInstance); + if(extraData) + { + BGSObjectInstanceExtra * objectModData = DYNAMIC_CAST(extraData, BSExtraData, BGSObjectInstanceExtra); + if(objectModData) + { + auto data = objectModData->data; + if(!data || !data->forms) + return result; + + for(UInt32 i = 0; i < data->blockSize / sizeof(BGSObjectInstanceExtra::Data::Form); i++) + { + BGSMod::Attachment::Mod * objectMod = (BGSMod::Attachment::Mod *)Runtime_DynamicCast(LookupFormByID(data->forms[i].formId), RTTI_TESForm, RTTI_BGSMod__Attachment__Mod); + result.Push(&objectMod); + } + } + } + } + return result; + } + + TESObjectREFR* AttachWireLatent(UInt32 stackId, TESObjectREFR* refA, TESObjectREFR* refB, TESForm* splineForm) + { + TESObjectREFR * wireRef = nullptr; + VirtualMachine* vm = (*g_gameVM)->m_virtualMachine; + + if(!splineForm) { + BGSDefaultObject * splineDefault = (*g_defaultObjectMap)->GetDefaultObject("WorkshopSplineObject"); + if(splineDefault) { + splineForm = splineDefault->form; + } + } + + // No specified spline, no refs, refs are same item, or no 3D loaded + if(!splineForm || !refA || !refB || refA == refB || !refA->GetObjectRootNode() || !refB->GetObjectRootNode()) { + return nullptr; + } + + // See if the two references are already linked by the same wire i.e. they have the same entry in their PowerLinks listing + std::set linkedWires; + ExtraDataList * extraDataRefA = refA->extraDataList; + ExtraDataList * extraDataRefB = refB->extraDataList; + if(extraDataRefA && extraDataRefB) + { + ExtraPowerLinks * powerLinksA = (ExtraPowerLinks*)extraDataRefA->GetByType(kExtraData_PowerLinks); + ExtraPowerLinks * powerLinksB = (ExtraPowerLinks*)extraDataRefB->GetByType(kExtraData_PowerLinks); + if(powerLinksA && powerLinksB) // Both items must have power links to check + { + tArray * connectionSearch; + tArray * connectionPopulate; + if(powerLinksA->connections.count < powerLinksB->connections.count) // Pick the smaller list to be the set + { + connectionPopulate = &powerLinksA->connections; + connectionSearch = &powerLinksB->connections; + } + else + { + connectionPopulate = &powerLinksB->connections; + connectionSearch = &powerLinksA->connections; + } + + // Add the items from the smaller list to the set + for(int i = 0; i < connectionPopulate->count; i++) + { + UInt64 formID = 0; + connectionPopulate->GetNthItem(i, formID); + linkedWires.insert(formID); + } + + // Search the other listing for items that exist in the set + for(int i = 0; i < connectionSearch->count; i++) + { + UInt64 formID = 0; + connectionSearch->GetNthItem(i, formID); + + // This wire exists in the other list, it is invalid to wire the same objects twice + if(linkedWires.find(formID) != linkedWires.end()) { + return nullptr; + } + } + } + } + + BGSBendableSpline * spline = DYNAMIC_CAST(splineForm, TESForm, BGSBendableSpline); + BGSBendableSpline * splineA = DYNAMIC_CAST(refA->baseForm, TESForm, BGSBendableSpline); + BGSBendableSpline * splineB = DYNAMIC_CAST(refB->baseForm, TESForm, BGSBendableSpline); + + BGSKeyword * keyword = nullptr; + BGSDefaultObject * workshopItemDefault = (*g_defaultObjectMap)->GetDefaultObject("WorkshopItem"); + if(workshopItemDefault) { + keyword = DYNAMIC_CAST(workshopItemDefault->form, TESForm, BGSKeyword); + } + + // No workshop keyword is bad + // Connecting a wire to another wire or connecting a non-wire is invalid + if(!keyword || !spline || splineA || splineB) { + return nullptr; + } + + // Get the workshop by keyword + TESObjectREFR * workshopRef = GetLinkedRef_Native(refA, keyword); + if(!workshopRef) { + return nullptr; + } + + // Workshop ref isn't a workshop! + BSExtraData* extraDataWorkshop = workshopRef->extraDataList->GetByType(ExtraDataType::kExtraData_WorkshopExtraData); + if(!extraDataWorkshop) { + return nullptr; + } + + // Create our wire instance + wireRef = PlaceAtMe_Native(vm, stackId, &refA, spline, 1, true, true, false); + if(!wireRef) { + return nullptr; + } + + UInt32 nullHandle = *g_invalidRefHandle; + TESObjectCELL* parentCell = wireRef->parentCell; + TESWorldSpace* worldspace = CALL_MEMBER_FN(wireRef,GetWorldspace)(); + + NiPoint3 rot; + MoveRefrToPosition(wireRef, &nullHandle, parentCell, worldspace, &refA->pos, &rot); + + // Set the wire's linked ref to the workshop + SetLinkedRef_Native(wireRef, workshopRef, keyword); + + LocationData locData(*g_player); + FinalizeWireLink(&locData, wireRef, refB, 0, refA, 0); + SetWireEndpoints_Internal(refA, 0, refB, 0, wireRef); + + ExtraBendableSplineParams * splineParams = (ExtraBendableSplineParams*)wireRef->extraDataList->GetByType(kExtraData_BendableSplineParams); + if(splineParams) { + splineParams->thickness = 1.5f; + } + + LinkPower3_Internal(extraDataWorkshop, wireRef); + LinkPower_Internal(extraDataWorkshop, refA, refB, wireRef); + LinkPower2_Internal(refA, extraDataWorkshop); + LinkPower2_Internal(refB, extraDataWorkshop); + LinkPower4_Internal(wireRef); + return wireRef; + } + + DECLARE_DELAY_FUNCTOR(F4SEAttachWireFunctor, 2, AttachWireLatent, TESObjectREFR, TESObjectREFR*, TESObjectREFR*, TESForm*); + + + bool AttachWire(VirtualMachine * vm, UInt32 stackId, TESObjectREFR* refA, TESObjectREFR* refB, TESForm* baseSpline) + { + if(!refA || !refB) + return false; + + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEAttachWireFunctor(AttachWireLatent, vm, stackId, refA, refB, baseSpline)); + return true; + } + + // If this object is a BendableSpline it will link to the two ObjectReferences, otherwise it will link to other BendableSplines + VMArray GetConnectedObjects(TESObjectREFR* thisRef) + { + VMArray result; + if(!thisRef) + return result; + + ExtraDataList * extraDataList = thisRef->extraDataList; + if(extraDataList) + { + if(extraDataList->HasType(kExtraData_PowerLinks)) + { + ExtraPowerLinks * powerLinks = (ExtraPowerLinks*)extraDataList->GetByType(kExtraData_PowerLinks); + if(powerLinks) + { + for(int i = 0; i < powerLinks->connections.count; i++) + { + UInt64 formID = 0; + powerLinks->connections.GetNthItem(i, formID); + + TESForm * form = LookupFormByID(formID); + if(form) + { + TESObjectREFR * refr = DYNAMIC_CAST(form, TESForm, TESObjectREFR); + if(refr) + result.Push(&refr); + } + } + } + } + } + + return result; + } + + BSFixedString GetDisplayName(VMRefOrInventoryObj * thisObj) + { + TESForm * baseForm = nullptr; + ExtraDataList * extraDataList = nullptr; + thisObj->GetExtraData(&baseForm, &extraDataList); + + if(baseForm) + { + if(extraDataList) + { + BSExtraData * extraData = extraDataList->GetByType(ExtraDataType::kExtraData_TextDisplayData); + if(extraData) + { + ExtraTextDisplayData * displayText = DYNAMIC_CAST(extraData, BSExtraData, ExtraTextDisplayData); + if(displayText) + { + return *CALL_MEMBER_FN(displayText, GetReferenceName)(baseForm); + } + } + } + + TESFullName* pFullName = DYNAMIC_CAST(baseForm, TESForm, TESFullName); + if (pFullName) + return pFullName->name; + } + + return BSFixedString(); + } + + + void SetMaterialSwap(VMRefOrInventoryObj * thisObj, BGSMaterialSwap * newSwap) + { + if(thisObj) + { + TESForm * baseForm = nullptr; + ExtraDataList * extraDataList = nullptr; + thisObj->GetExtraData(&baseForm, &extraDataList); + + if(baseForm) + { + if(extraDataList) + { + BSExtraData * extraData = extraDataList->GetByType(ExtraDataType::kExtraData_MaterialSwap); + if(extraData) + { + if(newSwap) + { + ExtraMaterialSwap * materialSwap = DYNAMIC_CAST(extraData, BSExtraData, ExtraMaterialSwap); + if(materialSwap) + { + materialSwap->materialSwap = newSwap; + } + } + else + { + extraDataList->Remove(ExtraDataType::kExtraData_MaterialSwap, extraData); + Heap_Free(extraData); + } + } + else if(newSwap) + { + extraDataList->Add(ExtraDataType::kExtraData_MaterialSwap, ExtraMaterialSwap::Create(newSwap)); + } + } + } + } + } + + BGSMaterialSwap * GetMaterialSwap(VMRefOrInventoryObj * thisObj) + { + if(!thisObj) + return nullptr; + + TESForm * baseForm = nullptr; + ExtraDataList * extraDataList = nullptr; + thisObj->GetExtraData(&baseForm, &extraDataList); + + if(baseForm) + { + if(extraDataList) + { + BSExtraData * extraData = extraDataList->GetByType(ExtraDataType::kExtraData_MaterialSwap); + if(extraData) + { + ExtraMaterialSwap * materialSwap = DYNAMIC_CAST(extraData, BSExtraData, ExtraMaterialSwap); + if(materialSwap) + { + return materialSwap->materialSwap; + } + } + } + } + + return nullptr; + } + + + VMArray GetInventoryItemsLatent(UInt32 stackId, TESObjectREFR * refr) + { + VMArray results; + if(!refr) + return results; + + auto inventory = refr->inventoryList; + if(inventory) { + inventory->inventoryLock.LockForRead(); + + for(int i = 0; i < inventory->items.count; i++) { + BGSInventoryItem item; + inventory->items.GetNthItem(i, item); + + results.Push(&item.form); + } + + inventory->inventoryLock.Unlock(); + } + + return results; + } + + float GetInventoryWeight(TESObjectREFR * refr) + { + return refr ? CALL_MEMBER_FN(refr, GetInventoryWeight)() : 0.0f; + } + + DECLARE_DELAY_FUNCTOR(F4SEInventoryFunctor, 0, GetInventoryItemsLatent, TESObjectREFR, VMArray); + + bool GetInventoryItems(VirtualMachine * vm, UInt32 stackId, TESObjectREFR* refr) + { + if(!refr) + return false; + + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEInventoryFunctor(GetInventoryItemsLatent, vm, stackId, refr)); + return true; + } + + VMArray GetConnectPointsLatent(UInt32 stackId, TESObjectREFR * refr) + { + VMArray results; + if(!refr) + return results; + + NiNode * root = refr->GetObjectRootNode(); + if(!root) + return results; + + BSConnectPoint::Parents * extraData = (BSConnectPoint::Parents *)Runtime_DynamicCast(root->GetExtraData("CPA"), RTTI_NiExtraData, RTTI_BSConnectPoint__Parents); + if(extraData) + { + for(UInt32 i = 0; i < extraData->points.count; i++) + { + BSConnectPoint::Parents::ConnectPoint * connectPoint = extraData->points[i]; + if(connectPoint) + { + ConnectPoint point; + + point.Set("parent", connectPoint->parent); + point.Set("name", connectPoint->name); + + float yaw, pitch, roll; + connectPoint->rot.GetEulerAngles(roll, pitch, yaw); + yaw *= 180.0 / MATH_PI; + pitch *= 180.0 / MATH_PI; + roll *= 180.0 / MATH_PI; + + point.Set("roll", roll); + point.Set("pitch", pitch); + point.Set("yaw", yaw); + + NiPoint3 localPos = connectPoint->pos; + + NiAVObject * parent = nullptr; + if(connectPoint->parent == BSFixedString("")) + { + parent = root; + } + else + { + NiAVObject * child = root->GetObjectByName(&connectPoint->parent); + if(child) + parent = child; + } + + NiPoint3 worldPos = localPos; + if(parent) { + worldPos = parent->m_worldTransform.rot.Transpose() * localPos + parent->m_worldTransform.pos; + point.Set("x", worldPos.x); + point.Set("y", worldPos.y); + point.Set("z", worldPos.z); + } else { + point.Set("x", 0.0f); + point.Set("y", 0.0f); + point.Set("z", 0.0f); + } + + point.Set("scale", connectPoint->scale); + + point.Set("object", nullptr); + + if(parent != root && refr->parentCell) { + bhkWorld * world = CALL_MEMBER_FN(refr->parentCell, GetHavokWorld)(); + if(world) { + TESObjectREFR * connected = GetObjectAtConnectPoint(refr, &worldPos, world, 8.0f); + if(connected) { + point.Set("object", connected); + } + } + } + + results.Push(&point); + } + } + } + + return results; + } + + DECLARE_DELAY_FUNCTOR(F4SEConnectPointsFunctor, 0, GetConnectPointsLatent, TESObjectREFR, VMArray); + + bool GetConnectPoints(VirtualMachine * vm, UInt32 stackId, TESObjectREFR* refr) + { + if(!refr) + return false; + + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEConnectPointsFunctor(GetConnectPointsLatent, vm, stackId, refr)); + return true; + } + + bool TransmitConnectedPowerLatent(UInt32 stackId, TESObjectREFR * refr) + { + if(!refr) + return false; + + NiNode * root = refr->GetObjectRootNode(); + if(!root) { + return false; + } + + BGSKeyword * keyword = nullptr; + BGSDefaultObject * workshopItemDefault = (*g_defaultObjectMap)->GetDefaultObject("WorkshopItem"); + if(workshopItemDefault) { + keyword = DYNAMIC_CAST(workshopItemDefault->form, TESForm, BGSKeyword); + } + + // No workshop keyword is bad + if(!keyword) { + return false; + } + + // Get the workshop by keyword + TESObjectREFR * workshopRef = GetLinkedRef_Native(refr, keyword); + if(!workshopRef) { + return false; + } + + // Workshop ref isn't a workshop! + BSExtraData* extraDataWorkshop = workshopRef->extraDataList->GetByType(ExtraDataType::kExtraData_WorkshopExtraData); + if(!extraDataWorkshop) { + return false; + } + + BSConnectPoint::Parents * extraData = (BSConnectPoint::Parents *)Runtime_DynamicCast(root->GetExtraData("CPA"), RTTI_NiExtraData, RTTI_BSConnectPoint__Parents); + if(!extraData) { + return false; + } + + for(UInt32 i = 0; i < extraData->points.count; i++) + { + BSConnectPoint::Parents::ConnectPoint * connectPoint = extraData->points[i]; + if(!connectPoint) + continue; + + NiPoint3 localPos = connectPoint->pos; + NiAVObject * parent = nullptr; + if(connectPoint->parent == "") + parent = root; + else + { + NiAVObject * child = root->GetObjectByName(&connectPoint->parent); + if(child) + parent = child; + } + + NiPoint3 worldPos = localPos; + if(parent) { + worldPos = parent->m_worldTransform.rot.Transpose() * localPos + parent->m_worldTransform.pos; + } + + float scale = connectPoint->scale; + if(parent != root && refr->parentCell) { + bhkWorld * world = CALL_MEMBER_FN(refr->parentCell, GetHavokWorld)(); + if(world) { + TESObjectREFR * connected = GetObjectAtConnectPoint(refr, &worldPos, world, 8.0f); + if(connected) { + try // Probably wont make a difference but doesnt hurt to try + { + LinkPower_Internal(extraDataWorkshop, refr, connected, nullptr); + LinkPower2_Internal(connected, extraDataWorkshop); + } + catch (...) + { + _MESSAGE("Power link error!"); + } + } + } + } + } + + LinkPower2_Internal(refr, extraDataWorkshop); + return true; + } + + DECLARE_DELAY_FUNCTOR(F4SETransmitConnectedPowerFunctor, 0, TransmitConnectedPowerLatent, TESObjectREFR, bool); + + bool TransmitConnectedPower(VirtualMachine * vm, UInt32 stackId, TESObjectREFR* refr) + { + if(!refr) + return false; + + F4SEDelayFunctorManagerInstance().Enqueue(new F4SETransmitConnectedPowerFunctor(TransmitConnectedPowerLatent, vm, stackId, refr)); + return true; + } + + struct CompareMaterial { + bool operator()(const std::pair lhs, const std::pair rhs) { + return lhs.first->source < rhs.first->source; + } + }; + + VMArray ApplyMaterialSwapLatent(UInt32 stackId, VMRefOrInventoryObj * thisObj, BGSMaterialSwap * materialSwap, bool renameMaterial) + { + // If the item is a reference use that, otherwise use the owner + TESObjectREFR * refr = thisObj->GetObjectReference(); + if(!refr) + refr = thisObj->GetOwner(); + + NiNode * rootNode[2]; + rootNode[0] = refr ? refr->GetActorRootNode(false) : nullptr; + rootNode[1] = refr ? refr->GetActorRootNode(true) : nullptr; + + int maxRoots = 2; + if(rootNode[0] == rootNode[1]) + maxRoots = 1; + + std::set, CompareMaterial> success; + + for(int i = 0; i < maxRoots; ++i) + { + if(!rootNode[i]) + continue; + + rootNode[i]->Visit([&](NiAVObject * object) + { + BSGeometry * geometry = object->GetAsBSGeometry(); + if(geometry) + { + NiPointer shaderProperty = ni_cast(geometry->shaderProperty, BSShaderProperty); + if(shaderProperty) + { + const char * shaderPath = shaderProperty->m_name.c_str(); + std::string fullPath(shaderPath ? shaderPath : ""); + if(fullPath.length() == 0) + return false; + + fullPath = std::regex_replace(fullPath, std::regex("/+|\\\\+"), "\\"); // Replace multiple slashes or forward slashes with one backslash + fullPath = std::regex_replace(fullPath, std::regex("^\\\\+"), ""); // Remove all backslashes from the front + fullPath = std::regex_replace(fullPath, std::regex(".*?materials\\\\", std::regex_constants::icase), ""); // Remove everything before and including the materials path + BSFixedString fixedPath(fullPath.c_str()); + + auto materialLookup = materialSwap->materialSwaps.Find(&fixedPath); + if(materialLookup) + { + // Pull the old colorRemapIndex + float lastColorIndex = FLT_MAX; + BSLightingShaderProperty * lightingShader = ni_cast(shaderProperty, BSLightingShaderProperty); + if(lightingShader) { + BSLightingShaderMaterialBase * shaderMaterialBase = static_cast(shaderProperty->shaderMaterial); + lastColorIndex = shaderMaterialBase->fLookupScale; + } + + std::string materialPath("Materials\\"); + materialPath.append(materialLookup->target.c_str()); + BSShaderData shaderData; + if(!LoadMaterialFile(materialPath.c_str(), &shaderData, 0)) + CALL_MEMBER_FN(&shaderData, ApplyMaterialData)(geometry, false); + + // Shader property will be the same pointer unless it changes shader type + BSShaderProperty * newShaderProperty = ni_cast(geometry->shaderProperty, BSShaderProperty); + if(newShaderProperty) + { + // Renames the material after swap + if(renameMaterial) { + newShaderProperty->m_name = materialPath.c_str(); + } + + // Fill in the new colorRemapIndex + if(materialLookup->colorRemapIndex != FLT_MAX) + { + BSLightingShaderProperty * newLightingShader = ni_cast(newShaderProperty, BSLightingShaderProperty); + if(newLightingShader) { + BSLightingShaderMaterialBase * shaderMaterialBase = static_cast(newLightingShader->shaderMaterial); + shaderMaterialBase->fLookupScale = materialLookup->colorRemapIndex; + } + } + } + + success.emplace(std::make_pair(materialLookup, lastColorIndex)); + } + } + + } + return false; + }); + } + + VMArray result; + for(auto & material : success) + { + RemapData res; + res.Set("source", material.first->target); + res.Set("target", material.first->source); + res.Set("colorIndex", material.second); + result.Push(&res); + } + return result; + } + + DECLARE_DELAY_FUNCTOR(F4SEMaterialSwapFunctor, 2, ApplyMaterialSwapLatent, VMRefOrInventoryObj, VMArray, BGSMaterialSwap*, bool); + + bool ApplyMaterialSwap(VirtualMachine * vm, UInt32 stackId, VMRefOrInventoryObj* refr, BGSMaterialSwap * materialSwap, bool renameMaterial) + { + if(!refr || !materialSwap) + return false; + + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEMaterialSwapFunctor(ApplyMaterialSwapLatent, vm, stackId, refr, materialSwap, renameMaterial)); + return true; + } + +#ifdef _DEBUG + void ScrapLatent(UInt32 stackId, TESObjectREFR * refr) + { + LocationData locData(*g_player); + ScrapReference(&locData, &refr, nullptr); + } + + DECLARE_DELAY_FUNCTOR(F4SEScrapFunctor, 0, ScrapLatent, TESObjectREFR, void); + + bool Scrap(VirtualMachine * vm, UInt32 stackId, TESObjectREFR* refr) + { + if(!refr) + return false; + + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEScrapFunctor(ScrapLatent, vm, stackId, refr)); + return true; + } +#endif +} + +void papyrusObjectReference::RegisterFuncs(VirtualMachine* vm) +{ + F4SEObjectRegistry& f4seObjRegistry = F4SEObjectRegistryInstance(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + +#ifdef _DEBUG + f4seObjRegistry.RegisterClass(); +#endif + + vm->RegisterFunction( + new NativeFunction0>("GetAllMods", "ObjectReference", papyrusObjectReference::GetAllMods, vm)); + + vm->RegisterFunction( + new NativeFunction0>("GetConnectedObjects", "ObjectReference", papyrusObjectReference::GetConnectedObjects, vm)); + + vm->RegisterFunction( + new LatentNativeFunction2("AttachWire", "ObjectReference", papyrusObjectReference::AttachWire, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetDisplayName", "ObjectReference", papyrusObjectReference::GetDisplayName, vm)); + + vm->RegisterFunction( + new LatentNativeFunction0>("GetInventoryItems", "ObjectReference", papyrusObjectReference::GetInventoryItems, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetInventoryWeight", "ObjectReference", papyrusObjectReference::GetInventoryWeight, vm)); + + vm->RegisterFunction( + new NativeFunction1("SetMaterialSwap", "ObjectReference", papyrusObjectReference::SetMaterialSwap, vm)); + + vm->RegisterFunction( + new NativeFunction0("GetMaterialSwap", "ObjectReference", papyrusObjectReference::GetMaterialSwap, vm)); + + vm->RegisterFunction( + new LatentNativeFunction0>("GetConnectPoints", "ObjectReference", papyrusObjectReference::GetConnectPoints, vm)); + + vm->RegisterFunction( + new LatentNativeFunction0("TransmitConnectedPower", "ObjectReference", papyrusObjectReference::TransmitConnectedPower, vm)); + + vm->RegisterFunction( + new LatentNativeFunction2, BGSMaterialSwap*, bool>("ApplyMaterialSwap", "ObjectReference", papyrusObjectReference::ApplyMaterialSwap, vm)); + + vm->SetFunctionFlags("ObjectReference", "AttachWire", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("ObjectReference", "GetInventoryItems", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("ObjectReference", "GetConnectPoints", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("ObjectReference", "TransmitConnectedPower", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("ObjectReference", "ApplyMaterialSwap", IFunction::kFunctionFlag_NoWait); + +#ifdef _DEBUG + vm->RegisterFunction( + new LatentNativeFunction0("Scrap", "ObjectReference", papyrusObjectReference::Scrap, vm)); + + vm->SetFunctionFlags("ObjectReference", "Scrap", IFunction::kFunctionFlag_NoWait); +#endif +} diff --git a/f4se/f4se/PapyrusObjectReference.h b/f4se/f4se/PapyrusObjectReference.h new file mode 100644 index 0000000..2ed54fa --- /dev/null +++ b/f4se/f4se/PapyrusObjectReference.h @@ -0,0 +1,10 @@ +#pragma once + +class VirtualMachine; + +#include "f4se/GameTypes.h" + +namespace papyrusObjectReference +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusObjects.cpp b/f4se/f4se/PapyrusObjects.cpp new file mode 100644 index 0000000..751b1bf --- /dev/null +++ b/f4se/f4se/PapyrusObjects.cpp @@ -0,0 +1,334 @@ +#include "PapyrusObjects.h" + +#include "Serialization.h" + +#include "PapyrusVM.h" + +namespace +{ + static const size_t kMaxNameLen = 1024; +} + +/// +/// F4SEObjectRegistry +/// + +bool F4SEObjectRegistry::RegisterFactory(IF4SEObjectFactory * factory) +{ + uintptr_t vtbl = *reinterpret_cast(factory); + std::string className( factory->ClassName() ); + auto it = factoryMap_.emplace(className, vtbl); + return it.second; +} + +const IF4SEObjectFactory* F4SEObjectRegistry::GetFactoryByName(const char* name) const +{ + std::string t( name ); + const IF4SEObjectFactory* result = NULL; + FactoryMapT::const_iterator it = factoryMap_.find(t); + if (it != factoryMap_.end()) + { + result = reinterpret_cast(&it->second); + } + + return result; +} + +/// +/// F4SEPersistentObjectStorage +/// + +void F4SEPersistentObjectStorage::CleanDroppedStacks() +{ + VirtualMachine* vm = (*g_gameVM)->m_virtualMachine; + + for (UInt32 i=0; iHasStack(e.owningStackId) != NULL) + continue; + + // Stack no longer active, drop this entry + + delete e.obj; + e.obj = NULL; + + freeIndices_.push_back(i); + + _MESSAGE("F4SEPersistentObjectStorage::CleanDroppedStacks: Freed object at index %d.", i); + } +} + +void F4SEPersistentObjectStorage::ClearAndRelease() +{ + freeIndices_.clear(); + + for (DataT::iterator it = data_.begin(); it != data_.end(); ++it) + { + Entry& e = *it; + if (e.obj != NULL) + delete e.obj; + } + + data_.clear(); +} + +bool F4SEPersistentObjectStorage::Save(const F4SESerializationInterface* intfc) +{ + using namespace Serialization; + + // Before saving, purge entries whose owning stack is no longer running. + // This can happen if someone forgot to release an object. + // We don't want these resource leaks to pile up in the co-save. + CleanDroppedStacks(); + + // Save data + UInt32 dataSize = data_.size(); + if (! WriteData(intfc, &dataSize)) + return false; + + UInt32 filledSize = data_.size() - freeIndices_.size(); + if (! WriteData(intfc, &filledSize)) + return false; + + for (UInt32 i=0; i= data_.size()) + { + _MESSAGE("F4SEPersistentObjectStorage::AccessObject(%d): Invalid handle.", handle); + return NULL; + } + + Entry& e = data_[index]; + + if (e.obj == NULL) + { + _MESSAGE("F4SEPersistentObjectStorage::AccessObject(%d): Object was NULL.", handle); + return NULL; + } + + IF4SEObject* result = e.obj; + if (result == NULL) + { + _MESSAGE("F4SEPersistentObjectStorage::AccessObject(%d): Invalid type (%s).", handle, e.obj->ClassName()); + return NULL; + } + + return result; +} + +IF4SEObject* F4SEPersistentObjectStorage::Take(SInt32 handle) +{ + IScopedCriticalSection scopedLock( &lock_ ); + + SInt32 index = handle - 1; + + if (index < 0 || index >= data_.size()) + { + _MESSAGE("F4SEPersistentObjectStorage::AccessObject(%d): Invalid handle.", handle); + return NULL; + } + + Entry& e = data_[index]; + + if (e.obj == NULL) + { + _MESSAGE("F4SEPersistentObjectStorage::TakeObject(%d): Object was NULL.", handle); + return NULL; + } + + IF4SEObject* result = e.obj; + if (result != NULL) + { + e.obj = NULL; + freeIndices_.push_back(index); + } + else + { + _MESSAGE("F4SEPersistentObjectStorage::TakeObject(%d): Invalid type (%s).", handle, e.obj->ClassName()); + return NULL; + } + + return result; +} + +/// +/// Serialization helpers +/// + +bool WriteF4SEObject(const F4SESerializationInterface* intfc, IF4SEObject* obj) +{ + using namespace Serialization; + + const char* name = obj->ClassName(); + const UInt32 version = obj->ClassVersion(); + + intfc->OpenRecord('OBJE', version); + + size_t rawLen = strlen(name); + UInt32 len = min(rawLen, kMaxNameLen); + + if (! WriteData(intfc, &len)) + return false; + + if (! intfc->WriteRecordData(name, len)) + return false; + + return obj->Save(intfc); +} + +bool ReadF4SEObject(const F4SESerializationInterface* intfc, IF4SEObject*& objOut) +{ + UInt32 type, length, objVersion; + + if (! intfc->GetNextRecordInfo(&type, &objVersion, &length)) + return false; + + if (type != 'OBJE') + { + _MESSAGE("ReadF4SEObject: Error loading unexpected chunk type %08X (%.4s)", type, &type); + return false; + } + + // Read the name of the serialized class + UInt32 len; + if (! intfc->ReadRecordData(&len, sizeof(len))) + return false; + + if (len > kMaxNameLen) + { + _MESSAGE("ReadF4SEObject: Serialization error. Class name len extended kMaxNameLen."); + return false; + } + + char buf[kMaxNameLen+1] = { 0 }; + if (! intfc->ReadRecordData(&buf, len)) + return false; + + // Get the factory + const IF4SEObjectFactory* factory = F4SEObjectRegistryInstance().GetFactoryByName(buf); + if (factory == NULL) + { + _MESSAGE("ReadF4SEObject: Serialization error. Factory missing for %s.", &buf); + return false; + } + + // Intantiate and load the actual data + IF4SEObject* obj = factory->Create(); + if (! obj->Load(intfc, objVersion)) + { + // Load failed. clean up. + objOut = NULL; + factory->Free(obj); + return false; + } + + objOut = obj; + return true; +} + +/// +/// Global instances +/// + +F4SEObjectRegistry& F4SEObjectRegistryInstance() +{ + static F4SEObjectRegistry instance; + return instance; +} + +F4SEPersistentObjectStorage& F4SEObjectStorageInstance() +{ + static F4SEPersistentObjectStorage instance; + return instance; +} diff --git a/f4se/f4se/PapyrusObjects.h b/f4se/f4se/PapyrusObjects.h new file mode 100644 index 0000000..d112b0d --- /dev/null +++ b/f4se/f4se/PapyrusObjects.h @@ -0,0 +1,170 @@ +#pragma once + +#include +#include +#include + +#include "common/ICriticalSection.h" + +struct F4SESerializationInterface; +class TESForm; +class IF4SEDelayFunctor; + +// Serializable classes deriving from IF4SEObject must define a +// serialization constructor that takes SerializationTag as the single argument. +// After it has been called, either Load() is used to fill in the data, +// or in case the load failed, the instance is destroyed again. +struct SerializationTag {}; + +/// +/// IF4SEObject - Interface for a heap allocated, co-save serializable object. +/// + +class IF4SEObject +{ +public: + virtual ~IF4SEObject() {} + + virtual const char* ClassName() const = 0; + virtual UInt32 ClassVersion() const = 0; + virtual IF4SEDelayFunctor * GetAsDelayFunctor() { return nullptr; } + + virtual bool Save(const F4SESerializationInterface* intfc) = 0; + virtual bool Load(const F4SESerializationInterface* intfc, UInt32 version) = 0; +}; + +/// +/// F4SEObjectFactory +/// + +class IF4SEObjectFactory +{ +public: + virtual ~IF4SEObjectFactory() {} + + virtual IF4SEObject* Create() const = 0; + virtual void Free(IF4SEObject* obj) const = 0; + virtual const char* ClassName() const = 0; +}; + +template +class ConcreteF4SEObjectFactory : public IF4SEObjectFactory +{ +public: + virtual IF4SEObject* Create() const + { + SerializationTag tag; + return new T( tag ); + } + virtual const char* ClassName() const + { + SerializationTag tag; + T tempInstance(tag); + return tempInstance.ClassName(); + } + virtual void Free(IF4SEObject* obj) const + { + delete obj; + } +}; + +/// +/// F4SEObjectRegistry +/// + +class F4SEObjectRegistry +{ +private: + typedef std::map FactoryMapT; + +public: + template + bool RegisterClass() + { + ConcreteF4SEObjectFactory factory; + return RegisterFactory(&factory); + } + + virtual bool RegisterFactory(IF4SEObjectFactory * factory); + virtual const IF4SEObjectFactory* GetFactoryByName(const char* name) const; + +private: + // Stores the vtables directly + FactoryMapT factoryMap_; +}; + +/// +/// F4SEPersistentObjectStorage +/// + +class F4SEPersistentObjectStorage +{ + struct Entry; + + // Note: handle = index + 1 + // +1, because in previous versions the invalid handle was 0 so people + // might test for != 0 +public: + + // Transfer ownership to registry + template + SInt32 StoreObject(T* obj, UInt32 owningStackId) + {// lock_ + return Store(obj, owningStackId); + }// ~lock_ + + // Access object while keeping in storage + template + T* AccessObject(SInt32 handle) + {// lock_ + return reinterpret_cast(Access(handle)); + }// ~lock_ + + // Remove object from storage and take ownership of the pointer + template + T* TakeObject(SInt32 handle) + {// lock_ + return reinterpret_cast(Take(handle)); + }// lock_ + + virtual SInt32 Store(IF4SEObject* obj, UInt32 owningStackId); + virtual IF4SEObject* Access(SInt32 handle); + virtual IF4SEObject* Take(SInt32 handle); + + bool Save(const F4SESerializationInterface* intfc); + bool Load(const F4SESerializationInterface* intfc, UInt32 version); + + void CleanDroppedStacks(); + void ClearAndRelease(); + + enum { kSaveVersion = 1 }; + +private: + struct Entry + { + IF4SEObject* obj; + UInt32 owningStackId; + }; + + typedef std::vector DataT; + typedef std::vector IndexCacheT; + + ICriticalSection lock_; + DataT data_; + IndexCacheT freeIndices_; +}; + +/// +/// Serialization helpers +/// + +bool WriteF4SEObject(const F4SESerializationInterface* intfc, IF4SEObject* obj); +bool ReadF4SEObject(const F4SESerializationInterface* intfc, IF4SEObject*& objOut); + +/// +/// Global instances +/// + +F4SEObjectRegistry& F4SEObjectRegistryInstance(); + +F4SEPersistentObjectStorage& F4SEObjectStorageInstance(); diff --git a/f4se/f4se/PapyrusPerk.cpp b/f4se/f4se/PapyrusPerk.cpp new file mode 100644 index 0000000..e66870a --- /dev/null +++ b/f4se/f4se/PapyrusPerk.cpp @@ -0,0 +1,78 @@ +#include "f4se/PapyrusPerk.h" + +#include "f4se/GameForms.h" +#include "f4se/GameFormComponents.h" +#include "f4se/GameReferences.h" + +namespace papyrusPerk +{ + bool IsPlayable(BGSPerk* thisPerk) + { + return thisPerk ? (thisPerk->playable >= 0) : false; + } + + bool IsHidden(BGSPerk* thisPerk) + { + return thisPerk ? (thisPerk->hidden >= 0) : false; + } + + UInt32 GetLevel(BGSPerk* thisPerk) + { + return thisPerk ? thisPerk->perkLevel : 0; + } + + UInt32 GetNumRanks(BGSPerk* thisPerk) + { + return thisPerk ? thisPerk->numRanks : 0; + } + + BGSPerk* GetNextPerk(BGSPerk* thisPerk) + { + return thisPerk ? thisPerk->nextPerk : nullptr; + } + + BSFixedString GetSWFPath(BGSPerk* thisPerk) + { + return thisPerk ? thisPerk->swfPath : BSFixedString(); + } + + bool IsEligible(BGSPerk* thisPerk, Actor * actor) + { + if(!thisPerk || !actor) + return false; + + Condition ** condition = &thisPerk->condition; + if(condition) + return EvaluationConditions(condition, actor, actor); + + // No conditions to fulfill + return true; + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusPerk::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("IsPlayable", "Perk", papyrusPerk::IsPlayable, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("IsHidden", "Perk", papyrusPerk::IsHidden, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetLevel", "Perk", papyrusPerk::GetLevel, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetNumRanks", "Perk", papyrusPerk::GetNumRanks, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetNextPerk", "Perk", papyrusPerk::GetNextPerk, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetSWFPath", "Perk", papyrusPerk::GetSWFPath, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("IsEligible", "Perk", papyrusPerk::IsEligible, vm)); +} diff --git a/f4se/f4se/PapyrusPerk.h b/f4se/f4se/PapyrusPerk.h new file mode 100644 index 0000000..7a4777e --- /dev/null +++ b/f4se/f4se/PapyrusPerk.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusPerk +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusScaleformAdapter.cpp b/f4se/f4se/PapyrusScaleformAdapter.cpp new file mode 100644 index 0000000..7d67c84 --- /dev/null +++ b/f4se/f4se/PapyrusScaleformAdapter.cpp @@ -0,0 +1,283 @@ +#include "f4se/PapyrusScaleformAdapter.h" + +#include "f4se/GameTypes.h" + +#include "f4se/PapyrusValue.h" +#include "f4se/PapyrusArgs.h" +#include "f4se/ScaleformValue.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/ScaleformMovie.h" + +namespace PlatformAdapter +{ + bool ConvertPapyrusValue(GFxValue * dest, VMValue * src, GFxMovieRoot * root) + { + switch(src->GetTypeEnum()) + { + case VMValue::kType_String: + { + BSFixedString * str = src->data.GetStr(); + root->CreateString(dest, str->c_str()); + return true; + } + break; + case VMValue::kType_Int: + dest->SetInt(src->data.i); + return true; + break; + case VMValue::kType_Float: + dest->SetNumber(src->data.f); + return true; + break; + case VMValue::kType_Bool: + dest->SetBool(src->data.b); + return true; + break; + case VMValue::kType_Identifier: + { + VMObjectTypeInfo * objectType = static_cast(src->GetComplexType()); + VMIdentifier * id = src->data.id; + if(objectType && id) { + UInt64 handle = id->GetHandle(); + BSFixedString typeName = objectType->m_typeName; + + root->CreateObject(dest); + dest->SetMember("__handleHigh__", &GFxValue((UInt32)(handle >> 32))); + dest->SetMember("__handleLow__", &GFxValue((UInt32)(handle & 0xFFFFFFFF))); + + GFxValue type; + root->CreateString(&type, typeName.c_str()); + + dest->SetMember("__type__", &type); + return true; + } + } + break; + case VMValue::kType_Struct: + { + VMStructTypeInfo * structType = static_cast(src->GetComplexType()); + VMValue::StructData * structData = src->data.strct; + if(structType && structData) { + root->CreateObject(dest); + GFxValue gStructObject; + root->CreateObject(&gStructObject); + dest->SetMember("__struct__", &gStructObject); + + GFxValue gStructName; + root->CreateString(&gStructName, structType->m_typeName.c_str()); + gStructObject.SetMember("__type__", &gStructName); + + GFxValue gStructPairs; + root->CreateObject(&gStructPairs); + gStructObject.SetMember("__data__", &gStructPairs); + + structType->m_members.ForEach([&gStructPairs, &structData, &root](VMStructTypeInfo::MemberItem * item) + { + GFxValue value; + ConvertPapyrusValue(&value, &structData->GetStruct()[item->index], root); + gStructPairs.SetMember(item->name.c_str(), &value); + return true; + }); + return true; + } + } + break; + case VMValue::kType_Variable: + { + VMValue * source = src->data.var; + if(source) { + root->CreateObject(dest); + GFxValue type; + ConvertPapyrusValue(&type, source, root); + dest->SetMember("__var__", &type); + return true; + } + } + break; + case VMValue::kType_StringArray: + case VMValue::kType_IntArray: + case VMValue::kType_FloatArray: + case VMValue::kType_BoolArray: + case VMValue::kType_IdentifierArray: + case VMValue::kType_StructArray: + case VMValue::kType_VariableArray: + { + VMValue::ArrayData * arrayData = src->data.arr; + if(arrayData) { + root->CreateArray(dest); + + for(UInt32 i = 0; i < arrayData->arr.count; i++) { + VMValue vmValue; + arrayData->arr.GetNthItem(i, vmValue); + + GFxValue value; + ConvertPapyrusValue(&value, &vmValue, root); + dest->PushBack(&value); + } + + return true; + } + } + break; + } + + return false; + } + + class StructMemberVisitor : public GFxValue::ObjectInterface::ObjVisitor + { + public: + StructMemberVisitor(VirtualMachine * _vm, VMStructTypeInfo * _typeObject, VMValue::StructData * _strct) : vm(_vm), typeObject(_typeObject), strct(_strct) { } + + virtual void Visit(const char * member, GFxValue * value) override + { + if(strct) + { + BSFixedString memberName(member); + auto memberItem = typeObject->m_members.Find(&memberName); + if(memberItem) { + VMValue * structValues = strct->GetStruct(); + VMValue data; + + // Must be the same type, otherwise we are type clobbering! + if(ConvertScaleformValue(&data, value, vm) && data.type.value == structValues[memberItem->index].type.value) { + structValues[memberItem->index] = data; + } + } + } + } + VirtualMachine * vm; + VMStructTypeInfo * typeObject; + VMValue::StructData * strct; + }; + + bool ConvertScaleformValue(VMValue * dest, GFxValue * src, VirtualMachine * vm) + { + switch(src->GetType()) + { + case GFxValue::kType_Null: + case GFxValue::kType_Undefined: + dest->SetVariable(new VMValue); // Var to None + return true; + break; + case GFxValue::kType_Int: + case GFxValue::kType_UInt: + dest->SetInt(src->GetInt()); + return true; + break; + case GFxValue::kType_Number: + dest->SetFloat(src->GetNumber()); + return true; + break; + case GFxValue::kType_String: + dest->SetString(src->GetString()); + return true; + break; + case GFxValue::kType_Bool: + dest->SetBool(src->GetBool()); + return true; + break; + case GFxValue::kType_Object: + { + if(src->HasMember("__var__")) + { + VMValue * value = new VMValue; + + GFxValue gValue; + src->GetMember("__var__", &gValue); + + ConvertScaleformValue(value, &gValue, vm); + + dest->SetVariable(value); + return true; + } + else if(src->HasMember("__type__")) + { + UInt64 handle = 0; + GFxValue gType; + src->GetMember("__type__", &gType); + GFxValue gHandle; + src->GetMember("__handleHigh__", &gHandle); + handle |= ((UInt64)gHandle.GetUInt()) << 32; + src->GetMember("__handleLow__", &gHandle); + handle |= (UInt64)gHandle.GetUInt(); + + BSFixedString typeName(gType.GetString()); + + if(GetIdentifier(dest, handle, &typeName, vm)) + { + return true; + } + } + else if(src->HasMember("__struct__")) + { + GFxValue gStructObject; + src->GetMember("__struct__", &gStructObject); + + GFxValue gTypeName; + gStructObject.GetMember("__type__", &gTypeName); + + GFxValue gStructData; + gStructObject.GetMember("__data__", &gStructData); + + BSFixedString structName(gTypeName.GetString()); + VMStructTypeInfo * typeObject = nullptr; + if(vm->GetStructTypeInfo(&structName, &typeObject)) + if(typeObject) + typeObject->Release(); + + dest->SetComplexType(typeObject); + + // Make sure our type still exists + VMValue::StructData * strct = nullptr; + if(typeObject) { + vm->CreateStruct(&structName, &strct); + + // Setup all the empty types? Need to confirm whether CreateStruct already does this + typeObject->m_members.ForEach([&strct, &typeObject](VMStructTypeInfo::MemberItem * item) + { + strct->GetStruct()[item->index].type.value = typeObject->m_data.entries[item->index].m_type; + return true; + }); + dest->data.strct = strct; + } + + StructMemberVisitor visitor(vm, typeObject, strct); + gStructData.VisitMembers(&visitor); + return true; + } + else + { + // Our object isn't valid, just make it a var to None + dest->SetVariable(new VMValue); + } + } + break; + case GFxValue::kType_Array: + { + UInt32 length = src->GetArraySize(); + VMValue::ArrayData * arrayData = nullptr; + vm->CreateArray(dest, length, &arrayData); + + dest->type.value = VMValue::kType_VariableArray; + dest->data.arr = arrayData; + + for(UInt32 i = 0; i < length; i++) + { + GFxValue value; + src->GetElement(i, &value); + + VMValue * var = new VMValue; + ConvertScaleformValue(var, &value, vm); + arrayData->arr.entries[i].SetVariable(var); + } + + return true; + } + break; + } + + return false; + } +}; diff --git a/f4se/f4se/PapyrusScaleformAdapter.h b/f4se/f4se/PapyrusScaleformAdapter.h new file mode 100644 index 0000000..65ef51c --- /dev/null +++ b/f4se/f4se/PapyrusScaleformAdapter.h @@ -0,0 +1,15 @@ +#pragma once + +class GFxValue; +class VMValue; +class GFxMovieRoot; +class VirtualMachine; + +namespace PlatformAdapter +{ + // Converts a Papyrus value to a Scaleform Value + bool ConvertPapyrusValue(GFxValue * dest, VMValue * src, GFxMovieRoot * root); + + // Converts a Scaleform value to a Papyrus value + bool ConvertScaleformValue(VMValue * dest, GFxValue * src, VirtualMachine * vm); +}; diff --git a/f4se/f4se/PapyrusScriptObject.cpp b/f4se/f4se/PapyrusScriptObject.cpp new file mode 100644 index 0000000..29274bf --- /dev/null +++ b/f4se/f4se/PapyrusScriptObject.cpp @@ -0,0 +1,263 @@ +#include "f4se/PapyrusScriptObject.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/PapyrusEvents.h" + +#include "f4se/GameReferences.h" +#include "f4se/GameRTTI.h" + +namespace papyrusScriptObject +{ + void RegisterForKey(VMObject * thisObject, UInt32 key) + { + if(!thisObject) + return; + + g_inputKeyEventRegs.Register(key, thisObject->GetHandle(), thisObject->GetObjectType()); + } + + void UnregisterForKey(VMObject * thisObject, UInt32 key) + { + if(!thisObject) + return; + g_inputKeyEventRegs.Unregister(key, thisObject->GetHandle(), thisObject->GetObjectType()); + } + + void RegisterForControl(VMObject * thisObject, BSFixedString control) + { + if(!thisObject) + return; + g_inputControlEventRegs.Register(control, thisObject->GetHandle(), thisObject->GetObjectType()); + } + + void UnregisterForControl(VMObject * thisObject, BSFixedString control) + { + if(!thisObject) + return; + g_inputControlEventRegs.Unregister(control, thisObject->GetHandle(), thisObject->GetObjectType()); + } + + void RegisterForExternalEvent(VMObject * thisObject, BSFixedString eventName, BSFixedString callback) + { + if(!thisObject) + return; + + ExternalEventParameters params; + params.callbackName = callback; + g_externalEventRegs.Register(eventName, thisObject->GetHandle(), thisObject->GetObjectType(), ¶ms); + } + + void UnregisterForExternalEvent(VMObject * thisObject, BSFixedString eventName) + { + if(!thisObject) + return; + g_externalEventRegs.Unregister(eventName, thisObject->GetHandle(), thisObject->GetObjectType()); + } + + void RegisterForCameraState(VMObject * thisObject) + { + if(!thisObject) + return; + + g_cameraEventRegs.Register(thisObject->GetHandle(), thisObject->GetObjectType()); + } + + void UnregisterForCameraState(VMObject * thisObject) + { + if(!thisObject) + return; + + g_cameraEventRegs.Unregister(thisObject->GetHandle(), thisObject->GetObjectType()); + } + + void RegisterForFurnitureEvent(VMObject * thisObject, VMVariable var) + { + if(!thisObject) + return; + + EventRegistration p; + p.handle = thisObject->GetHandle(); + p.scriptName = thisObject->GetObjectType(); + FormParameters * params = &p.params; + + g_furnitureEventRegs.Lock(); + + // Lookup the existing params from the registrations if it exists + // Set contents are normally const, we are casting because the order of the set is unchanged by this + auto it = g_furnitureEventRegs.m_data.find(p); + if(it != g_furnitureEventRegs.m_data.end()) { + params = const_cast(&it->params); + } + + if(var.IsNone()) + { + // ObjectReference + TESObjectREFR* refr = nullptr; + if(var.Get(&refr)) + { + if(refr) + params->AddFilter(refr); + } + + // This one might not be possible with ObjectReference[] as Var + VMArray arr; + if(var.Get(&arr)) + { + for(UInt32 i = 0; i < arr.Length(); i++) + { + TESObjectREFR * refr = nullptr; + arr.Get(&refr, i); + if(refr) + params->AddFilter(refr); + } + } + + // Var[] + VMArray varr; + if(var.Get(&varr)) + { + for(UInt32 i = 0; i < varr.Length(); i++) + { + VMVariable var; + varr.Get(&var, i); + + TESObjectREFR * refr = nullptr; + var.Get(&refr); + if(refr) + params->AddFilter(refr); + } + } + + // FormList + BGSListForm * formList = nullptr; + if(var.Get(&formList) && formList) + { + for(UInt32 i = 0; i < formList->forms.count; i++) + { + TESForm * form = nullptr; + formList->forms.GetNthItem(i, form); + TESObjectREFR * refr = DYNAMIC_CAST(form, TESForm, TESObjectREFR); + if(refr) + params->AddFilter(refr); + } + } + } + else // We want to add a new entry + g_furnitureEventRegs.Register(thisObject->GetHandle(), thisObject->GetObjectType(), params); + + g_furnitureEventRegs.Release(); + } + + void UnregisterForFurnitureEvent(VMObject * thisObject, VMVariable var) + { + if(!thisObject) + return; + + EventRegistration p; + p.handle = thisObject->GetHandle(); + p.scriptName = thisObject->GetObjectType(); + FormParameters * params = &p.params; + + g_furnitureEventRegs.Lock(); + + // Lookup the existing params from the registrations if it exists + // Set contents are normally const, we are casting because the order of the set is unchanged by this + auto it = g_furnitureEventRegs.m_data.find(p); + if(it != g_furnitureEventRegs.m_data.end()) { + params = const_cast(&it->params); + } + + if(!var.IsNone()) + { + // ObjectReference + TESObjectREFR* refr = nullptr; + if(var.Get(&refr)) + { + if(refr) + params->RemoveFilter(refr); + } + + // This one might not be possible with ObjectReference[] as Var + VMArray arr; + if(var.Get(&arr)) + { + for(UInt32 i = 0; i < arr.Length(); i++) + { + TESObjectREFR * refr = nullptr; + arr.Get(&refr, i); + if(refr) + params->RemoveFilter(refr); + } + } + + // Var[] + VMArray varr; + if(var.Get(&varr)) + { + for(UInt32 i = 0; i < varr.Length(); i++) + { + VMVariable var; + varr.Get(&var, i); + + TESObjectREFR * refr = nullptr; + var.Get(&refr); + if(refr) + params->RemoveFilter(refr); + } + } + + // FormList + BGSListForm * formList = nullptr; + if(var.Get(&formList) && formList) + { + for(UInt32 i = 0; i < formList->forms.count; i++) + { + TESForm * form = nullptr; + formList->forms.GetNthItem(i, form); + TESObjectREFR * refr = DYNAMIC_CAST(form, TESForm, TESObjectREFR); + if(refr) + params->RemoveFilter(refr); + } + } + } + else // No parameter, remove all registrations + g_furnitureEventRegs.Unregister(thisObject->GetHandle(), thisObject->GetObjectType()); + + g_furnitureEventRegs.Release(); + } +} + +void papyrusScriptObject::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction1 ("RegisterForKey", "ScriptObject", papyrusScriptObject::RegisterForKey, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("UnregisterForKey", "ScriptObject", papyrusScriptObject::UnregisterForKey, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("RegisterForControl", "ScriptObject", papyrusScriptObject::RegisterForControl, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("UnregisterForControl", "ScriptObject", papyrusScriptObject::UnregisterForControl, vm)); + + vm->RegisterFunction( + new NativeFunction2 ("RegisterForExternalEvent", "ScriptObject", papyrusScriptObject::RegisterForExternalEvent, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("UnregisterForExternalEvent", "ScriptObject", papyrusScriptObject::UnregisterForExternalEvent, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("RegisterForCameraState", "ScriptObject", papyrusScriptObject::RegisterForCameraState, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("UnregisterForCameraState", "ScriptObject", papyrusScriptObject::UnregisterForCameraState, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("RegisterForFurnitureEvent", "ScriptObject", papyrusScriptObject::RegisterForFurnitureEvent, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("UnregisterForFurnitureEvent", "ScriptObject", papyrusScriptObject::UnregisterForFurnitureEvent, vm)); +} diff --git a/f4se/f4se/PapyrusScriptObject.h b/f4se/f4se/PapyrusScriptObject.h new file mode 100644 index 0000000..509acc4 --- /dev/null +++ b/f4se/f4se/PapyrusScriptObject.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusScriptObject +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusSerialization.cpp b/f4se/f4se/PapyrusSerialization.cpp new file mode 100644 index 0000000..878c7ca --- /dev/null +++ b/f4se/f4se/PapyrusSerialization.cpp @@ -0,0 +1,522 @@ +#include "f4se/PapyrusSerialization.h" + +#include "f4se/PapyrusArgs.h" + +namespace Serialization +{ + bool WriteVMData(const F4SESerializationInterface* intfc, const VMValue * val) + { + UInt8 typeId = val->GetTypeEnum(); + if(!WriteData(intfc, &typeId)) + return false; + + switch(typeId) + { + case VMValue::kType_Identifier: + { + UInt64 handle = 0; + BSFixedString typeName; + + VMIdentifier * id = val->data.id; + if(id) { + handle = id->GetHandle(); + VMObjectTypeInfo * typeObject = id->m_typeInfo; + if(typeObject) + typeName = typeObject->m_typeName; + } + + if(!WriteData(intfc, &typeName)) + return false; + if(!WriteData(intfc, &handle)) + return false; + } + break; + case VMValue::kType_String: + { + if(!WriteData(intfc, val->data.GetStr())) + return false; + } + break; + case VMValue::kType_Int: + { + if(!WriteData(intfc, &val->data.i)) + return false; + } + break; + case VMValue::kType_Float: + { + if(!WriteData(intfc, &val->data.f)) + return false; + } + break; + case VMValue::kType_Bool: + { + if(!WriteData(intfc, &val->data.b)) + return false; + } + break; + case VMValue::kType_Variable: + { + if(!WriteVMData(intfc, val->data.var)) + return false; + } + break; + case VMValue::kType_Struct: + { + VMValue::StructData * strct = val->data.strct; + VMValue * entries = strct->GetStruct(); + VMStructTypeInfo * typeObject = strct->m_type; + UInt32 members = typeObject->m_members.Size(); + + if(!WriteData(intfc, &typeObject->m_typeName)) + return false; + + if(!WriteData(intfc, &members)) + return false; + + typeObject->m_members.ForEach([&intfc, &entries](VMStructTypeInfo::MemberItem * item) + { + WriteData(intfc, &item->name); // Write Key + WriteVMData(intfc, &entries[item->index]); // Write Value + return true; + }); + } + break; + case VMValue::kType_IdentifierArray: + { + UInt32 length = 0; + VMValue::ArrayData * arr = val->data.arr; + if(arr) { + length = arr->arr.count; + } + + if(!WriteData(intfc, &length)) + return false; + + BSFixedString typeName; + IComplexType * typeObject = val->GetComplexType(); + if(typeObject) { + typeName = typeObject->m_typeName; + } + + if(!WriteData(intfc, &typeObject->m_typeName)) + return false; + + if(arr) + { + for(UInt32 i = 0; i < length; i++) + { + VMValue entry; + arr->arr.GetNthItem(i, entry); + + UInt64 handle = 0; + VMIdentifier * id = entry.data.id; + if(id) { + handle = id->GetHandle(); + } + + if(!WriteData(intfc, &handle)) + return false; + } + } + } + break; + case VMValue::kType_StringArray: + case VMValue::kType_IntArray: + case VMValue::kType_FloatArray: + case VMValue::kType_BoolArray: + { + UInt32 length = 0; + VMValue::ArrayData * arr = val->data.arr; + if(arr) { + length = arr->arr.count; + } + + if(!WriteData(intfc, &length)) + return false; + + if(arr) + { + for(UInt32 i = 0; i < length; i++) + { + VMValue entry; + arr->arr.GetNthItem(i, entry); + switch(typeId) { + case VMValue::kType_StringArray: + if(!WriteData(intfc, entry.data.GetStr())) + return false; + break; + case VMValue::kType_IntArray: + if(!WriteData(intfc, &entry.data.i)) + return false; + break; + case VMValue::kType_FloatArray: + if(!WriteData(intfc, &entry.data.f)) + return false; + break; + case VMValue::kType_BoolArray: + if(!WriteData(intfc, &entry.data.b)) + return false; + break; + } + } + } + } + break; + case VMValue::kType_VariableArray: + { + UInt32 length = 0; + VMValue::ArrayData * arr = val->data.arr; + if(arr) { + length = arr->arr.count; + } + + if(!WriteData(intfc, &length)) + return false; + + if(arr) + { + for(UInt32 i = 0; i < length; i++) + { + VMValue entry; + arr->arr.GetNthItem(i, entry); + + if(!WriteVMData(intfc, &entry)) + return false; + } + } + } + break; + case VMValue::kType_StructArray: + { + UInt32 length = 0; + VMValue::ArrayData * arr = val->data.arr; + IComplexType * typeArrObject = val->GetComplexType(); + if(arr) { + length = arr->arr.count; + } + + BSFixedString typeName; + VMStructTypeInfo * typeObject = nullptr; + if(typeArrObject && typeArrObject->GetType() == VMValue::kType_Struct) { + typeObject = static_cast(typeArrObject); + typeName = typeObject->m_typeName; + } + + if(!WriteData(intfc, &length)) + return false; + + if(!WriteData(intfc, &typeName)) + return false; + + UInt32 members = typeObject ? typeObject->m_members.Size() : 0; + if(!WriteData(intfc, &members)) + return false; + + for(UInt32 i = 0; i < length; i++) + { + VMValue entry; + arr->arr.GetNthItem(i, entry); + + VMValue::StructData * strct = entry.data.strct; + VMValue * entries = strct->GetStruct(); + if(typeObject) + { + typeObject->m_members.ForEach([&intfc, &entries](VMStructTypeInfo::MemberItem * item) + { + WriteData(intfc, &item->name); // Write Key + WriteVMData(intfc, &entries[item->index]); // Write Value + return true; + }); + } + } + } + break; + } + return true; + } + + bool ReadVMData(const F4SESerializationInterface* intfc, VMValue * val) + { + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + + UInt8 typeId = 0; + if(!ReadData(intfc, &typeId)) + return false; + + switch(typeId) + { + case VMValue::kType_Identifier: + { + BSFixedString typeName; + if(!ReadData(intfc, &typeName)) + return false; + + UInt64 handle = 0; + if(!ReadData(intfc, &handle)) + return false; + + if(intfc->ResolveHandle(handle, &handle)) { + GetIdentifier(val, handle, &typeName, vm); + } + } + break; + case VMValue::kType_String: + { + BSFixedString str; + if(!ReadData(intfc, &str)) + return false; + val->SetString(str); + } + break; + case VMValue::kType_Int: + { + SInt32 i = 0; + if(!ReadData(intfc, &i)) + return false; + val->SetInt(i); + } + break; + case VMValue::kType_Float: + { + float f = 0.0f; + if(!WriteData(intfc, &f)) + return false; + val->SetFloat(f); + } + break; + case VMValue::kType_Bool: + { + bool b = false; + if(!WriteData(intfc, &b)) + return false; + val->SetBool(b); + } + break; + case VMValue::kType_Variable: + { + VMValue * value = new VMValue; + if(!ReadVMData(intfc, value)) { + delete value; + return false; + } + val->SetVariable(value); + } + break; + case VMValue::kType_Struct: + { + BSFixedString structName; + if(!ReadData(intfc, &structName)) + return false; + + UInt32 members; + if(!ReadData(intfc, &members)) + return false; + + VMStructTypeInfo * typeObject = nullptr; + if(vm->GetStructTypeInfo(&structName, &typeObject)) + if(typeObject) + typeObject->Release(); + + val->SetComplexType(typeObject); + + // Make sure our type still exists + VMValue::StructData * strct = nullptr; + if(typeObject) { + vm->CreateStruct(&structName, &strct); + + // Setup all the empty types? Need to confirm whether CreateStruct already does this + typeObject->m_members.ForEach([&strct, &typeObject](VMStructTypeInfo::MemberItem * item) + { + strct->GetStruct()[item->index].type.value = typeObject->m_data.entries[item->index].m_type; + return true; + }); + } + + for(UInt32 i = 0; i < members; i++) + { + BSFixedString member; + if(!ReadData(intfc, &member)) // Read Key + return false; + + VMValue data; + if(!ReadVMData(intfc, &data)) // Read Value + return false; + + if(strct) + { + auto memberItem = typeObject->m_members.Find(&member); + if(memberItem) + strct->GetStruct()[memberItem->index] = data; + } + } + + val->data.strct = strct; + } + break; + case VMValue::kType_IdentifierArray: + { + UInt32 length = 0; + if(!ReadData(intfc, &length)) + return false; + + BSFixedString typeName; + if(!ReadData(intfc, &typeName)) + return false; + + VMValue::ArrayData * data = nullptr; + vm->CreateArray(val, length, &data); + + VMObjectTypeInfo * typeInfo = nullptr; + if(vm->GetObjectTypeInfoByName(&typeName, &typeInfo)) + if(typeInfo) + typeInfo->Release(); + + for(UInt32 i = 0; i < length; i++) + { + UInt64 handle = 0; + if(!ReadData(intfc, &handle)) + return false; + + if(intfc->ResolveHandle(handle, &handle) && data) { + GetIdentifier(&data->arr.entries[i], handle, typeInfo, (*g_gameVM)->m_virtualMachine); + } + } + + val->SetComplexType(typeInfo); + val->data.arr = data; + } + break; + case VMValue::kType_StringArray: + case VMValue::kType_IntArray: + case VMValue::kType_FloatArray: + case VMValue::kType_BoolArray: + { + UInt32 length = 0; + if(!ReadData(intfc, &length)) + return false; + + VMValue::ArrayData * data = nullptr; + vm->CreateArray(val, length, &data); + + val->type.value = typeId; + + for(UInt32 i = 0; i < length; i++) + { + switch(typeId) { + case VMValue::kType_StringArray: + { + BSFixedString str; + if(!ReadData(intfc, &str)) + return false; + data->arr.entries[i].SetString(str); + } + break; + case VMValue::kType_IntArray: + { + SInt32 i = 0; + if(!ReadData(intfc, &i)) + return false; + data->arr.entries[i].SetInt(i); + } + break; + case VMValue::kType_FloatArray: + { + float f = 0.0f; + if(!ReadData(intfc, &f)) + return false; + data->arr.entries[i].SetFloat(f); + } + break; + case VMValue::kType_BoolArray: + { + bool b = false; + if(!ReadData(intfc, &b)) + return false; + data->arr.entries[i].SetBool(b); + } + break; + } + } + } + break; + case VMValue::kType_VariableArray: + { + UInt32 length = 0; + if(!WriteData(intfc, &length)) + return false; + + VMValue::ArrayData * data = nullptr; + vm->CreateArray(val, length, &data); + + val->type.value = typeId; + + for(UInt32 i = 0; i < length; i++) + { + if(!ReadVMData(intfc, &data->arr.entries[i])) + return false; + } + } + break; + case VMValue::kType_StructArray: + { + UInt32 length = 0; + if(!ReadData(intfc, &length)) + return false; + + BSFixedString structName; + if(!ReadData(intfc, &structName)) + return false; + + UInt32 members = 0; + if(!ReadData(intfc, &members)) + return false; + + VMStructTypeInfo * typeObject = nullptr; + if(vm->GetStructTypeInfo(&structName, &typeObject)) + if(typeObject) + typeObject->Release(); + + if(typeObject) + val->SetComplexType(reinterpret_cast((UInt64)typeObject | 1)); + + // Entire array requires the type + VMValue::ArrayData * data = nullptr; + if(typeObject) + vm->CreateArray(val, length, &data); + + for(UInt32 i = 0; i < length; i++) + { + VMValue::StructData * strct = nullptr; + if(typeObject) + vm->CreateStruct(&structName, &strct); + + for(UInt32 j = 0; j < members; j++) + { + BSFixedString member; + if(!ReadData(intfc, &member)) // Read Key + return false; + + VMValue data; + if(!ReadVMData(intfc, &data)) // Read Value + return false; + + // Set the member data if we both had the type and created the struct + if(strct) { + auto memberItem = typeObject->m_members.Find(&member); + if(memberItem) + strct->GetStruct()[memberItem->index] = data; + } + } + + if(typeObject) { + data->arr.entries[i].SetComplexType(typeObject); + data->arr.entries[i].data.strct = strct; + } + } + } + break; + } + return true; + } +} diff --git a/f4se/f4se/PapyrusSerialization.h b/f4se/f4se/PapyrusSerialization.h new file mode 100644 index 0000000..ed9b405 --- /dev/null +++ b/f4se/f4se/PapyrusSerialization.h @@ -0,0 +1,13 @@ +#pragma once + +#include "f4se/Serialization.h" +#include "f4se/PluginAPI.h" +#include "f4se/PapyrusArgs.h" + +class VMVariable; + +namespace Serialization +{ + bool WriteVMData(const F4SESerializationInterface* intfc, const VMValue * val); + bool ReadVMData(const F4SESerializationInterface* intfc, VMValue * val); +}; diff --git a/f4se/f4se/PapyrusStruct.cpp b/f4se/f4se/PapyrusStruct.cpp new file mode 100644 index 0000000..a50cd84 --- /dev/null +++ b/f4se/f4se/PapyrusStruct.cpp @@ -0,0 +1,23 @@ +#include "f4se/PapyrusStruct.h" + +bool CreateStruct(VMValue * dst, BSFixedString * structName, VirtualMachine * vm, bool bNone) +{ + dst->SetNone(); + + VMValue::StructData * structData = nullptr; + VMStructTypeInfo * typeInfo = nullptr; + if(vm->GetStructTypeInfo(structName, &typeInfo)) { + dst->type.id = typeInfo; // Always set the type info if its valid + if(!bNone) { + vm->CreateStruct(structName, &structData); + if(structData) { + dst->data.strct = structData; + } + } + + typeInfo->Release(); + return structData ? true : false; + } + + return false; +} diff --git a/f4se/f4se/PapyrusStruct.h b/f4se/f4se/PapyrusStruct.h new file mode 100644 index 0000000..862b651 --- /dev/null +++ b/f4se/f4se/PapyrusStruct.h @@ -0,0 +1,197 @@ +#pragma once + +#include "f4se/PapyrusArgs.h" + +bool CreateStruct(VMValue * dst, BSFixedString * structName, VirtualMachine * vm, bool bNone); + +template +class VMStruct +{ +public: + VMStruct() : m_none(false), m_struct(nullptr) { }; + ~VMStruct() { } + + enum { kTypeID = 0 }; + + // Will make the VM return None instead of a structure with nothing defined + void SetNone(bool bNone) { m_none = bNone; } + bool IsNone() const { return m_none; } + + template + bool Get(BSFixedString name, T * value) + { + BSFixedString structName(T_structName); + VMStructTypeInfo * typeInfo = nullptr; + + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + if(vm->GetStructTypeInfo(&structName, &typeInfo)) + { + VMStructTypeInfo::MemberItem * item = typeInfo->m_members.Find(&name); + if(item) { + UnpackValue(value, &m_data[item->name]); + typeInfo->Release(); + structName.Release(); + return true; + } +#if _DEBUG + else { + + _DMESSAGE("Failed to unpack %s argument (%s) struct member not found.", structName.c_str(), name.c_str()); + } +#endif + typeInfo->Release(); + } + + structName.Release(); + return false; + }; + + template + bool Set(BSFixedString name, T a1, bool bReference = true) + { + BSFixedString structName(T_structName); + VMStructTypeInfo * typeInfo = nullptr; + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + if(vm->GetStructTypeInfo(&structName, &typeInfo)) + { + VMStructTypeInfo::MemberItem * item = typeInfo->m_members.Find(&name); + if(item) { + if(m_struct && bReference) { + VMValue * value = m_struct->GetStruct(); + PackValue(&value[item->index], &a1, vm); + } + + PackValue(&m_data[item->name], &a1, vm); + + typeInfo->Release(); + structName.Release(); + return true; + } +#if _DEBUG + else { + + _DMESSAGE("Failed to pack %s argument (%s) struct member not found.", structName.c_str(), name.c_str()); + } +#endif + + typeInfo->Release(); + } + + structName.Release(); + return false; + } + + void PackStruct(VMValue * dst, VirtualMachine * vm) + { + // Clean out the old value + dst->SetNone(); + + BSFixedString structName(T_structName); + VMStructTypeInfo * typeInfo = nullptr; + if(vm->GetStructTypeInfo(&structName, &typeInfo)) + { + if(CreateStruct(dst, &structName, vm, m_none)) + { + VMValue * values = dst->data.strct->GetStruct(); + + typeInfo->m_members.ForEach([&](VMStructTypeInfo::MemberItem * item) + { + UInt64 memberType = typeInfo->m_data[item->index].m_type; + if(memberType == m_data[item->name].type.value) + { + values[item->index] = m_data[item->name]; + } +#if _DEBUG + else { + + _DMESSAGE("Failed to pack %s argument (%s) struct member type mismatch got (%016I64X) expected (%016I64X).", structName.c_str(), item->name.c_str(), m_data[item->name].type, memberType); + } +#endif + + values[item->index].type.value = memberType; // Always force the type so that we don't get None types on struct + return true; + }); + } + + typeInfo->Release(); + } + + structName.Release(); + } + + void UnpackStruct(VMValue * src) + { + IComplexType * complexType = src->GetComplexType(); + BSFixedString structName(T_structName); + VMStructTypeInfo * typeInfo = nullptr; + VirtualMachine * vm = (*g_gameVM)->m_virtualMachine; + if(vm->GetStructTypeInfo(&structName, &typeInfo) && complexType == typeInfo) + { + if(src->data.strct) + { + VMValue * values = src->data.strct->GetStruct(); + + typeInfo->m_members.ForEach([&](VMStructTypeInfo::MemberItem * item) + { + m_data[item->name] = values[item->index]; + return true; + }); + + m_struct = src->data.strct; + } + else + { + typeInfo->m_members.ForEach([&](VMStructTypeInfo::MemberItem * item) + { + m_data[item->name].type.value = typeInfo->m_data[item->index].m_type; + return true; + }); + m_none = true; + } + + typeInfo->Release(); + } + + structName.Release(); + } + +protected: + bool m_none; + VMValue::StructData * m_struct; + std::map m_data; +}; + +template +struct IsStructType +{ + static const bool value = false; + static inline const char* name() { return nullptr; }; +}; + +template +struct IsStructType> +{ + static const bool value = true; + static inline const char* name() { return NAME; }; +}; + +#define DECLARE_STRUCT_STRING(structName) #structName +#define DECLARE_STRUCT(structName, scriptOwner) \ + char StructName_##structName##[] = ##scriptOwner## "#" DECLARE_STRUCT_STRING(structName); \ + typedef VMStruct ##structName##; +#define DECLARE_EXTERN_STRUCT(structName) \ + extern char StructName_##structName##[]; \ + typedef VMStruct ##structName##; + +template +void PackValue(VMValue * dst, VMStruct * src, VirtualMachine * vm) +{ + src->PackStruct(dst, vm); +} + + +template +void UnpackValue(VMStruct * dst, VMValue * src) +{ + dst->UnpackStruct(src); +} diff --git a/f4se/f4se/PapyrusUI.cpp b/f4se/f4se/PapyrusUI.cpp new file mode 100644 index 0000000..680a22f --- /dev/null +++ b/f4se/f4se/PapyrusUI.cpp @@ -0,0 +1,372 @@ +#include "f4se/PapyrusUI.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/PapyrusDelayFunctors.h" +#include "f4se/PapyrusEvents.h" + +#include "f4se/Serialization.h" +#include "f4se/PapyrusSerialization.h" + +#include "f4se/GameMenus.h" +#include "f4se/PapyrusScaleformAdapter.h" + +#include "f4se/CustomMenu.h" + +namespace papyrusUI +{ + DECLARE_STRUCT(MenuData, "UI") + + bool RegisterCustomMenu(StaticFunctionTag *, BSFixedString menuName, BSFixedString menuPath, BSFixedString rootPath, MenuData menuData) + { + if(!(*g_ui)->IsMenuRegistered(menuName) && !menuData.IsNone()) + { + BSReadAndWriteLocker locker(&g_customMenuLock); + g_customMenuData[menuName.c_str()].menuPath = menuPath; + g_customMenuData[menuName.c_str()].rootPath = rootPath; + menuData.Get("menuFlags", &g_customMenuData[menuName.c_str()].menuFlags); + menuData.Get("movieFlags", &g_customMenuData[menuName.c_str()].movieFlags); + menuData.Get("extendedFlags", &g_customMenuData[menuName.c_str()].extFlags); + menuData.Get("depth", &g_customMenuData[menuName.c_str()].depth); + (*g_ui)->Register(menuName.c_str(), CreateCustomMenu); + return true; + } + + return false; + } + + bool IsMenuOpen(StaticFunctionTag *, BSFixedString menuName) + { + return (*g_ui)->IsMenuOpen(menuName); + } + + bool OpenMenu(StaticFunctionTag *, BSFixedString menuName) + { + if((*g_ui)->IsMenuRegistered(menuName)) { + CALL_MEMBER_FN(*g_uiMessageManager, SendUIMessage)(menuName, kMessage_Open); + return true; + } + return false; + } + + bool CloseMenu(StaticFunctionTag *, BSFixedString menuName) + { + if((*g_ui)->IsMenuRegistered(menuName)) { + CALL_MEMBER_FN(*g_uiMessageManager, SendUIMessage)(menuName, kMessage_Close); + return true; + } + return false; + } + + bool IsMenuRegistered(StaticFunctionTag *, BSFixedString menuName) + { + return (*g_ui)->IsMenuRegistered(menuName); + } + + bool UI_LatentSet(UInt32 stackId, StaticFunctionTag *, BSFixedString menuName, BSFixedString varPath, VMVariable var) + { + BSReadLocker locker(g_menuTableLock); + IMenu * menu = (*g_ui)->GetMenu(menuName); + if(!menu) + return false; + + auto movie = menu->movie; + if(!movie) + return false; + + auto root = movie->movieRoot; + if(!root) + return false; + + GFxValue value; + PlatformAdapter::ConvertPapyrusValue(&value, &var.GetValue(), root); + return root->SetVariable(varPath.c_str(), &value); + } + + VMVariable UI_LatentGet(UInt32 stackId, StaticFunctionTag *, BSFixedString menuName, BSFixedString varPath) + { + BSReadLocker locker(g_menuTableLock); + VMVariable result; + IMenu * menu = (*g_ui)->GetMenu(menuName); + if(!menu) + return result; + + auto movie = menu->movie; + if(!movie) + return result; + + auto root = movie->movieRoot; + if(!root) + return result; + + GFxValue value; + root->GetVariable(&value, varPath.c_str()); + PlatformAdapter::ConvertScaleformValue(&result.GetValue(), &value, (*g_gameVM)->m_virtualMachine); + return result; + } + + VMVariable UI_LatentInvoke(UInt32 stackId, StaticFunctionTag *, BSFixedString menuName, BSFixedString varPath, VMArray arguments) + { + BSReadLocker locker(g_menuTableLock); + VMVariable result; + IMenu * menu = (*g_ui)->GetMenu(menuName); + if(!menu) + return result; + + auto movie = menu->movie; + if(!movie) + return result; + + auto root = movie->movieRoot; + if(!root) + return result; + + UInt32 argCount = arguments.Length(); + GFxValue * args = nullptr; + if(argCount > 0) + { + args = new GFxValue[argCount]; + for(UInt32 i = 0; i < argCount; i++) + { + VMVariable var; + arguments.Get(&var, i); + PlatformAdapter::ConvertPapyrusValue(&args[i], &var.GetValue(), root); + } + } + + GFxValue value; + root->Invoke(varPath, &value, args, argCount); + PlatformAdapter::ConvertScaleformValue(&result.GetValue(), &value, (*g_gameVM)->m_virtualMachine); + if(args) + delete [] args; + return result; + } + + class F4SEScaleform_OnLoadCompleted : public GFxFunctionHandler + { + public: + F4SEScaleform_OnLoadCompleted(UInt64 handle, BSFixedString objectType, BSFixedString callback, BSFixedString menuName, BSFixedString varPath, BSFixedString assetPath) : _handle(handle), _objectType(objectType), _callback(callback), _menuName(menuName), _varPath(varPath), _assetPath(assetPath) { } + + virtual void Invoke(Args * args) + { + if(args->numArgs >= 1) + { + GFxValue target; + args->args[0].GetMember("target", &target); + + GFxValue loader; + target.GetMember("loader", &loader); + + GFxValue content; + loader.GetMember("content", &content); + + GFxValue contentLoaderInfo; + loader.GetMember("contentLoaderInfo", &contentLoaderInfo); + + GFxValue loaderURL; + contentLoaderInfo.GetMember("loaderURL", &loaderURL); + + GFxValue url; + contentLoaderInfo.GetMember("url", &url); + + GFxValue name; + content.GetMember("name", &name); + + std::string contentInstance = name.GetString(); + + GFxValue parent; + content.GetMember("parent", &parent); + + while(parent.IsDisplayObject()) + { + GFxValue name; + parent.GetMember("name", &name); + if(!name.IsString()) + break; + + std::string parentInstance = name.GetString(); + contentInstance.insert(0, parentInstance + "."); + parent.GetMember("parent", &parent); + } + + bool success = true; + BSFixedString contentVar(contentInstance.c_str()); + SendPapyrusEvent5(_handle, _objectType, _callback, success, _menuName, _varPath, contentVar, _assetPath); + } + } + protected: + UInt64 _handle; + BSFixedString _objectType; + BSFixedString _callback; + BSFixedString _menuName; + BSFixedString _varPath; + BSFixedString _assetPath; + }; + + class F4SEScaleform_OnLoadFailed : public GFxFunctionHandler + { + public: + F4SEScaleform_OnLoadFailed(UInt64 handle, BSFixedString objectType, BSFixedString callback, BSFixedString menuName, BSFixedString varPath, BSFixedString assetPath) : _handle(handle), _objectType(objectType), _callback(callback), _menuName(menuName), _varPath(varPath), _assetPath(assetPath) { } + + virtual void Invoke(Args * args) + { + if(args->numArgs >= 1) + { + GFxValue target; + args->args[0].GetMember("target", &target); + + // Load failed, remove the loader we created + GFxValue loader; + target.GetMember("loader", &loader); + + GFxValue loaderParent; + loader.GetMember("parent", &loaderParent); + + loaderParent.Invoke("removeChild", nullptr, &loader, 1); + + bool success = false; + BSFixedString contentVar; + SendPapyrusEvent5(_handle, _objectType, _callback, success, _menuName, _varPath, contentVar, _assetPath); + } + } + protected: + UInt64 _handle; + BSFixedString _objectType; + BSFixedString _callback; + BSFixedString _menuName; + BSFixedString _varPath; + BSFixedString _assetPath; + }; + + bool UI_LatentLoad(UInt32 stackId, StaticFunctionTag *, BSFixedString menuName, BSFixedString varPath, BSFixedString assetPath, VMObject receiver, BSFixedString callback) + { + BSReadLocker locker(g_menuTableLock); + IMenu * menu = (*g_ui)->GetMenu(menuName); + if(!menu) + return false; + + auto movie = menu->movie; + if(!movie) + return false; + + auto root = movie->movieRoot; + if(!root) + return false; + + GFxValue target; + if(!root->GetVariable(&target, varPath.c_str())) + return false; + + GFxValue filePath; + root->CreateString(&filePath, assetPath.c_str()); + + // Create the Loader object + GFxValue loader; + root->CreateObject(&loader, "flash.display.Loader"); + + GFxValue contentLoaderInfo; + loader.GetMember("contentLoaderInfo", &contentLoaderInfo); + + GFxValue listener[2]; + + F4SEScaleform_OnLoadCompleted * onCompleted = new F4SEScaleform_OnLoadCompleted(receiver.GetHandle(), receiver.GetObjectType(), callback, menuName, varPath, assetPath); + F4SEScaleform_OnLoadFailed * onFailed = new F4SEScaleform_OnLoadFailed(receiver.GetHandle(), receiver.GetObjectType(), callback, menuName, varPath, assetPath); + + root->CreateString(&listener[0], "complete"); + root->CreateFunction(&listener[1], onCompleted); + contentLoaderInfo.Invoke("addEventListener", nullptr, listener, 2); + InterlockedDecrement(&onCompleted->refCount); // Let these be garbage collected + + root->CreateString(&listener[0], "ioError"); + root->CreateFunction(&listener[1], onFailed); + contentLoaderInfo.Invoke("addEventListener", nullptr, listener, 2); + InterlockedDecrement(&onFailed->refCount); + + // Add the loader to the target + if(!target.Invoke("addChild", nullptr, &loader, 1)) + return false; + + GFxValue loadArgs[2]; + // Create a load request for the new movie + root->CreateObject(&loadArgs[0], "flash.net.URLRequest", &filePath, 1); + + loadArgs[1].SetNull(); + + return loader.Invoke("load", nullptr, loadArgs, 2); + } + + DECLARE_DELAY_FUNCTOR(F4SEUISetFunctor, 3, UI_LatentSet, StaticFunctionTag, bool, BSFixedString, BSFixedString, VMVariable); + DECLARE_DELAY_FUNCTOR(F4SEUIGetFunctor, 2, UI_LatentGet, StaticFunctionTag, VMVariable, BSFixedString, BSFixedString); + DECLARE_DELAY_FUNCTOR(F4SEUIInvokeFunctor, 3, UI_LatentInvoke, StaticFunctionTag, VMVariable, BSFixedString, BSFixedString, VMArray); + DECLARE_DELAY_FUNCTOR(F4SEUILoadFunctor, 5, UI_LatentLoad, StaticFunctionTag, bool, BSFixedString, BSFixedString, BSFixedString, VMObject, BSFixedString); + + // These are the functions that enqueue the Latent functions + bool Set(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag * tag, BSFixedString menuName, BSFixedString varPath, VMVariable var) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEUISetFunctor(UI_LatentSet, vm, stackId, tag, menuName, varPath, var)); + return true; + } + + bool Get(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag * tag, BSFixedString menuName, BSFixedString varPath) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEUIGetFunctor(UI_LatentGet, vm, stackId, tag, menuName, varPath)); + return true; + } + + bool Invoke(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag * tag, BSFixedString menuName, BSFixedString varPath, VMArray args) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEUIInvokeFunctor(UI_LatentInvoke, vm, stackId, tag, menuName, varPath, args)); + return true; + } + + bool Load(VirtualMachine * vm, UInt32 stackId, StaticFunctionTag * tag, BSFixedString menuName, BSFixedString varPath, BSFixedString assetPath, VMObject receiver, BSFixedString callback) + { + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEUILoadFunctor(UI_LatentLoad, vm, stackId, tag, menuName, varPath, assetPath, receiver, callback)); + return true; + } +} + +void papyrusUI::RegisterFuncs(VirtualMachine* vm) +{ + F4SEObjectRegistry& f4seObjRegistry = F4SEObjectRegistryInstance(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + f4seObjRegistry.RegisterClass(); + + vm->RegisterFunction( + new LatentNativeFunction3 ("Set", "UI", papyrusUI::Set, vm)); + + vm->RegisterFunction( + new LatentNativeFunction2 ("Get", "UI", papyrusUI::Get, vm)); + + vm->RegisterFunction( + new LatentNativeFunction3 >("Invoke", "UI", papyrusUI::Invoke, vm)); + + vm->RegisterFunction( + new LatentNativeFunction5 ("Load", "UI", papyrusUI::Load, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("IsMenuOpen", "UI", papyrusUI::IsMenuOpen, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("OpenMenu", "UI", papyrusUI::OpenMenu, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("CloseMenu", "UI", papyrusUI::CloseMenu, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("IsMenuRegistered", "UI", papyrusUI::IsMenuRegistered, vm)); + + vm->RegisterFunction( + new NativeFunction4 ("RegisterCustomMenu", "UI", papyrusUI::RegisterCustomMenu, vm)); + + vm->SetFunctionFlags("UI", "Set", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("UI", "Get", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("UI", "Invoke", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("UI", "Load", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("UI", "IsMenuOpen", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("UI", "IsMenuRegistered", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("UI", "RegisterCustomMenu", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusUI.h b/f4se/f4se/PapyrusUI.h new file mode 100644 index 0000000..1c30a36 --- /dev/null +++ b/f4se/f4se/PapyrusUI.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusUI +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusUtilities.cpp b/f4se/f4se/PapyrusUtilities.cpp new file mode 100644 index 0000000..ce44c95 --- /dev/null +++ b/f4se/f4se/PapyrusUtilities.cpp @@ -0,0 +1,22 @@ +#include "f4se/PapyrusUtilities.h" +#include "f4se/PapyrusVM.h" + +UInt64 PapyrusVM::GetHandleFromObject(void * src, UInt32 typeID) +{ + VirtualMachine * registry = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = registry->GetHandlePolicy(); + + return policy->Create(typeID, (void*)src); +} + +void * PapyrusVM::GetObjectFromHandle(UInt64 handle, UInt32 typeID) +{ + VirtualMachine * registry = (*g_gameVM)->m_virtualMachine; + IObjectHandlePolicy * policy = registry->GetHandlePolicy(); + + if(handle == policy->GetInvalidHandle()) { + return NULL; + } + + return policy->Resolve(typeID, handle); +} diff --git a/f4se/f4se/PapyrusUtilities.h b/f4se/f4se/PapyrusUtilities.h new file mode 100644 index 0000000..94322d8 --- /dev/null +++ b/f4se/f4se/PapyrusUtilities.h @@ -0,0 +1,7 @@ +#pragma once + +namespace PapyrusVM +{ +UInt64 GetHandleFromObject(void * src, UInt32 typeID); +void * GetObjectFromHandle(UInt64 handle, UInt32 typeID); +}; diff --git a/f4se/f4se/PapyrusUtility.cpp b/f4se/f4se/PapyrusUtility.cpp new file mode 100644 index 0000000..caf4906 --- /dev/null +++ b/f4se/f4se/PapyrusUtility.cpp @@ -0,0 +1,33 @@ +#include "f4se/PapyrusUtility.h" + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +namespace papyrusUtility +{ + VMArray VarToVarArray(StaticFunctionTag * tag, VMVariable var) + { + VMArray result; + var.Get>(&result); + return result; + } + + VMVariable VarArrayToVar(StaticFunctionTag * tag, VMArray vars) + { + VMVariable result; + result.Set>(&vars); + return result; + } +} + +void papyrusUtility::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction1 , VMVariable>("VarToVarArray", "Utility", papyrusUtility::VarToVarArray, vm)); + + vm->RegisterFunction( + new NativeFunction1 >("VarArrayToVar", "Utility", papyrusUtility::VarArrayToVar, vm)); + + vm->SetFunctionFlags("Utility", "VarToVarArray", IFunction::kFunctionFlag_NoWait); + vm->SetFunctionFlags("Utility", "VarArrayToVar", IFunction::kFunctionFlag_NoWait); +} diff --git a/f4se/f4se/PapyrusUtility.h b/f4se/f4se/PapyrusUtility.h new file mode 100644 index 0000000..da8637b --- /dev/null +++ b/f4se/f4se/PapyrusUtility.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusUtility +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusVM.cpp b/f4se/f4se/PapyrusVM.cpp new file mode 100644 index 0000000..32d1a9c --- /dev/null +++ b/f4se/f4se/PapyrusVM.cpp @@ -0,0 +1,10 @@ +#include "PapyrusVM.h" + +// 1E364B1377EA84E44CC933976A8BE6D548B7D5AF+C +RelocPtr g_gameVM(0x058D3308); + +bool VirtualMachine::HasStack(UInt32 stackId) +{ + BSReadLocker locker(&stackLock); + return m_allStacks.Find(&stackId) != NULL; +} diff --git a/f4se/f4se/PapyrusVM.h b/f4se/f4se/PapyrusVM.h new file mode 100644 index 0000000..e10c7ad --- /dev/null +++ b/f4se/f4se/PapyrusVM.h @@ -0,0 +1,302 @@ +#pragma once + +#include "f4se/GameUtilities.h" +#include "f4se/GameTypes.h" + +#include "f4se/PapyrusInterfaces.h" +#include "f4se/PapyrusValue.h" + +// skipping the BSScript::Internal namespace stuff +#include + +class IFunction; + +// 08 +class Lock +{ +public: + UInt32 m_threadID; + UInt32 m_lockCount; +}; + +// BSScript::Internal::VirtualMachine: ??_7VirtualMachine@Internal@BSScript@@6B@ +class VirtualMachine +{ +public: + VirtualMachine(); + virtual ~VirtualMachine(); + + virtual void Unk_01(); // take m_lockC0 and then calls this+C8 + virtual void Unk_02(UInt64 unk); // take m_lockC0 and then sets m_unkE8 + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(UInt8 unk); // set m_unk82BC + virtual bool Unk_06(); // take m_lockBDC4, return m_BDF4 & 2 + virtual void RegisterForm(UInt32 typeID, const char * papyrusClassName); + virtual void Unk_08(); + virtual bool GetObjectTypeInfo(UInt32 formType, VMObjectTypeInfo ** outTypeInfo); // Must release outTypeInfo explicitly + virtual bool GetObjectTypeInfoByName(const BSFixedString * name, VMObjectTypeInfo ** outTypeInfo); // Must release outTypeInfo explicitly + virtual void Unk_0B(); + virtual bool Unk_0C(BSFixedString * typeName0, UInt32 * unk); + virtual void Unk_0D(); + virtual void Unk_0E(); + virtual void Unk_0F(); + virtual void Unk_10(); + virtual void Unk_11(); + virtual void Unk_12(); + virtual bool GetStructTypeInfo(const BSFixedString * name, VMStructTypeInfo ** outTypeInfo); // Must release outTypeInfo explicitly + virtual void Unk_14(); + virtual void Unk_15(); + virtual void Unk_16(); + virtual bool CreateObjectIdentifier(const BSFixedString * className, VMIdentifier ** identifier); + virtual bool CreateStruct(const BSFixedString * name, VMValue::StructData ** value); + virtual bool CreateArray(VMValue * value, UInt32 size, VMValue::ArrayData ** data); + virtual void Unk_1A(); + virtual void RegisterFunction(IFunction * fn); + virtual void SetFunctionFlagsEx(const char * className, UInt32 unk0, const char * fnName, UInt32 flags); + virtual void SetFunctionFlags(const char * className, const char * fnName, UInt32 flags); +#if _MSC_VER == 1700 + virtual void ForEachIdentifier(UInt64 handle, const std::function & functor); // return 1 to continue +#else + virtual void ForEachIdentifier(UInt64 handle, void * stdFunction); +#endif + virtual bool GetObjectIdentifier(UInt64 handle, const char * typeName, UInt64 unk1, VMIdentifier ** identifier, UInt8 unk2); + virtual void Unk_20(); + virtual void Unk_21(); + virtual void CastAs(VMIdentifier** idInOut, VMObjectTypeInfo ** typeAs, UInt64 unk1); // checks (typeAs->unk40 & 3) == 3 first + virtual bool SetPropertyValue(VMIdentifier** identifier, const char* propertyName, VMValue * newValue, UInt64* unk4); + virtual bool GetPropertyValue(VMIdentifier** identifier, const char* propertyName, VMValue * result); + virtual bool GetPropertyValueByIndex(VMIdentifier** identifier, SInt32 idx, VMValue* outValue); + virtual void Unk_26(); + virtual void Unk_27(); + virtual void Unk_28(); + virtual void Unk_29(); + virtual void Unk_2A(); + virtual void QueueEvent(UInt64 handle, BSFixedString * eventName, void * unk1); + virtual void Unk_2C(); + virtual void Unk_2D(); + virtual void Unk_2E(); + virtual void Unk_2F(); + virtual void Unk_30(); + virtual void ResumeStack(UInt32 stackId, VMValue* result); + virtual void Unk_32(); + virtual IObjectHandlePolicy * GetHandlePolicy(void); + virtual void Unk_34(); + virtual IObjectBindPolicy * GetObjectBindPolicy(void); + virtual void Unk_36(); + virtual void Unk_37(); + virtual void Unk_38(); + virtual void Unk_39(); + virtual void Unk_3A(); + virtual void Unk_3B(); + virtual void Unk_3C(); + virtual void Unk_3D(); + +private: + +// void ** _vtbl; // 00 + UInt64 pad08[(0xC0 - 0x08) >> 3]; // 08 + Lock m_lockC0; // C0 + UInt64 padC8[(0xE8 - 0xC8) >> 3]; // C8 + UInt64 m_unkE8; // E8 + UInt64 unkF0[(0x168 - 0xF0) >> 3]; + +public: + class ComplexTypeInfoItem + { + public: + BSFixedString name; // 00 + IComplexType * typeInfo; // 08 + + bool operator==(const ComplexTypeInfoItem & rhs) const { return name == rhs.name; } + bool operator==(const BSFixedString a_name) const { return name == a_name; } + operator UInt64() const { return (UInt64)name.data->Get(); } + + static inline UInt32 GetHash(BSFixedString * key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)key->data, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tname: %s", name.data->Get()); + _MESSAGE("\t\tinstance: %08X", typeInfo); + } + }; + class FormTypeName + { + public: + UInt32 typeId; // 00 + BSFixedString name; // 08 + + bool operator==(const FormTypeName & rhs) const { return typeId == rhs.typeId; } + operator UInt32() const { return typeId; } + + static inline UInt32 GetHash(UInt32 * key) + { + UInt32 hash; + CalculateCRC32_32(&hash, (UInt32)*key, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\ttypeId: %d", typeId); + _MESSAGE("\t\tname: %s", name.c_str()); + } + }; + + class StackTableItem + { + public: + UInt32 stackId; + void * data; + + bool operator==(const StackTableItem & rhs) const { return stackId == rhs.stackId; } + operator UInt32() const { return stackId; } + + static inline UInt32 GetHash(UInt32* pStackId) + { + UInt32 hash; + CalculateCRC32_32(&hash, (UInt32)*pStackId, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tstack: %d", stackId); + _MESSAGE("\t\tdata: %016I64X", data); + } + }; + + class IdentifierItem + { + public: + UInt64 handle; // 00 + UInt32 count; // 08 + UInt32 pad0C; // 0C + union + { + VMIdentifier * one; + VMIdentifier ** many; + } identifier; + + class IScriptVisitor + { + public: + virtual ~IScriptVisitor() { } + + virtual bool Visit(VMIdentifier * obj) = 0; + }; + + inline VMIdentifier * GetScriptObject(VMIdentifier * identifier) + { + return reinterpret_cast(reinterpret_cast(identifier) & ~1LL); + } + + void ForEachScript(IScriptVisitor * visitor) + { + if(count == 1) + { + visitor->Visit(GetScriptObject(identifier.one)); + } + else + { + for(UInt32 i = 0; i < count; ++i) + { + if(!visitor->Visit(GetScriptObject(identifier.many[i]))) + return; + } + } + } + + bool operator==(const IdentifierItem & rhs) const { return handle == rhs.handle; } + operator UInt64() const { return handle; } + + static inline UInt64 GetHash(UInt64* pHandle) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)*pHandle, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\thandle: %016I64X", handle); + _MESSAGE("\t\tscripts:"); + + class DumpVisitor : public IScriptVisitor + { + public: + virtual bool Visit(VMIdentifier* obj) + { + _MESSAGE("\t\t\t%s", obj->m_typeInfo->m_typeName.c_str()); + return true; + } + }; + DumpVisitor visitor; + ForEachScript(&visitor); + } + }; + + tHashSet m_objectTypes; // 168 + tHashSet m_structTypes; // 198 + tHashSet m_typeNames; // 1C8 + + bool HasStack(UInt32 stackId); + + UInt64 unk1F8[(0xBD58 - 0x1F8) >> 3]; + BSReadWriteLock stackLock; // BD58 + tHashSet m_allStacks; // BD60 + tHashSet m_waitStacks; // BD90 + UInt64 unkBDC0[(0xBDF8 - 0xBDC0) >> 3]; // BDC0 + BSReadWriteLock scriptsLock; // BDF8 + tHashSet m_attachedScripts; // BE00 +}; +STATIC_ASSERT(offsetof(VirtualMachine, m_objectTypes) == 0x168); +STATIC_ASSERT(offsetof(VirtualMachine, m_structTypes) == 0x198); +STATIC_ASSERT(offsetof(VirtualMachine, m_typeNames) == 0x1C8); + +// 87A0? +class GameVM : public IClientVM +{ +public: + virtual ~GameVM(); + + virtual void Unk_01(); + virtual void Unk_02(); + + IStackCallbackSaveInterface m_callbackSaveInterface; // 08 + + enum + { + kEventSink_Stats = 0, + kEventSink_InputEnableLayerDestroyed, + kEventSink_PositionPlayer, + kEventSink_FormDelete, + kEventSink_FormIDRemap, + kEventSink_InitScript, + kEventSink_ResolveNPCTemplates, + kEventSink_UniqueIDChange, + kEventSink_NumEvents + }; + + void * m_eventSinks[kEventSink_NumEvents]; // 10 + UInt64 unk50[(0xB0 - 0x50) >> 3]; // 50 + + VirtualMachine * m_virtualMachine; // B0 + IVMSaveLoadInterface * m_saveLoadInterface; // B8 + IVMDebugInterface * m_debugInterface; // C0 + SimpleAllocMemoryPagePolicy m_memoryPagePolicy; // C8 + + UInt64 unkF0[(0x1E0 - 0xF0) >> 3]; // F0 + IObjectHandlePolicy * m_objectHandlePolicy; // 1E0 + // ... + + MEMBER_FN_PREFIX(GameVM); + DEFINE_MEMBER_FN(SendPapyrusEvent, void, 0x01374FB0, UInt64 handle, const BSFixedString & eventName, std::function functor); // Signature not correct yet +}; + +extern RelocPtr g_gameVM; diff --git a/f4se/f4se/PapyrusValue.cpp b/f4se/f4se/PapyrusValue.cpp new file mode 100644 index 0000000..575f9dd --- /dev/null +++ b/f4se/f4se/PapyrusValue.cpp @@ -0,0 +1,297 @@ +#include "f4se/PapyrusValue.h" +#include "f4se/PapyrusInterfaces.h" + +VMValue::VMValue(const VMValue & other) +{ + if(&other != this) + { + type.value = kType_None; + data.p = nullptr; + CALL_MEMBER_FN(this, Set)(&other); + } +} +VMValue& VMValue::operator=(const VMValue& other) +{ + if(&other == this) + return *this; + + CALL_MEMBER_FN(this, Set)(&other); + return *this; +} + +void VMValue::SetNone(void) +{ + CALL_MEMBER_FN(this, Destroy)(); + + type.value = kType_None; + data.u = 0; +} + +void VMValue::SetInt(SInt32 i) +{ + CALL_MEMBER_FN(this, Destroy)(); + + type.value = kType_Int; + data.i = i; +} + +void VMValue::SetFloat(float f) +{ + CALL_MEMBER_FN(this, Destroy)(); + + type.value = kType_Float; + data.f = f; +} + +void VMValue::SetBool(bool b) +{ + CALL_MEMBER_FN(this, Destroy)(); + + type.value = kType_Bool; + data.b = b; +} + + +void VMValue::SetString(const char * str) +{ + CALL_MEMBER_FN(this, Destroy)(); + + type.value = kType_String; + CALL_MEMBER_FN(data.GetStr(), Set)(str); +} + +void VMValue::SetVariable(VMValue * value) +{ + CALL_MEMBER_FN(this, Destroy)(); + + type.value = kType_Variable; + data.var = value; +} + +void VMValue::SetComplexType(IComplexType * typeInfo) +{ + CALL_MEMBER_FN(this, Destroy)(); + + type.id = typeInfo; + data.p = nullptr; +} + +void VMValue::SetIdentifier(VMIdentifier ** identifier) +{ + IComplexType * typeInfo = GetComplexType(); + if(typeInfo && typeInfo->GetType() == kType_Identifier) + { + CALL_MEMBER_FN(this, Destroy)(); + + if(*identifier) + (*identifier)->IncrementLock(); + + data.id = *identifier; + } +} + +bool VMValue::IsIntegralType() const +{ + return type.value >= kType_String && type.value <= kType_Variable; +} + +bool VMValue::IsIntegralArrayType() const +{ + return type.value >= kType_IntegralStart && type.value <= kType_IntegralEnd; +} + +bool VMValue::IsComplexArrayType() const +{ + return (IsComplexType() && (type.value & 0x01LL)); +} + +bool VMValue::IsArrayType() const +{ + return IsIntegralArrayType() || IsComplexArrayType(); +} + +bool VMValue::IsComplexType() const +{ + return type.value >= kType_ArrayEnd; +} + +bool VMValue::IsIdentifier() +{ + IComplexType * typeInfo = GetComplexType(); + return typeInfo ? typeInfo->GetType() == kType_Identifier : false; +} + +IComplexType * VMValue::GetComplexType() +{ + return IsComplexType() ? reinterpret_cast(type.value & ~0x01LL) : nullptr; +} + +IComplexType * VMValue::GetComplexType() const +{ + return IsComplexType() ? reinterpret_cast(type.value & ~0x01LL) : nullptr; +} + +UInt8 VMValue::GetTypeEnum() const // Returns the sanitized number +{ + IComplexType * typeInfo = GetComplexType(); + if(typeInfo) + { + UInt32 typeId = typeInfo->GetType(); + if(IsArrayType()) + typeId += kType_ArrayOffset; + return typeId; + } + + return type.value; +} + +UInt64 VMIdentifier::GetHandle(void) +{ + UInt64 result = (*g_objectHandlePolicy)->GetInvalidHandle(); + + SInt32 oldLock = Lock(); + result = m_handle; + Unlock(oldLock); + + return result; +} + +SInt32 VMIdentifier::Lock(void) +{ + UInt32 spinCount = 0; + SInt32 lockValue; + + while(true) + { + lockValue = m_lock; + + if(lockValue & kLockBit) + { + // someone else is holding the lock, sleep + if(spinCount <= kFastSpinThreshold) + { + Sleep(0); + spinCount++; + } + else + { + Sleep(1); + } + } + else + { + // try to take the lock + if(InterlockedCompareExchange(&m_lock, lockValue | kLockBit, lockValue) == lockValue) + break; + } + } + + return lockValue; +} + +void VMIdentifier::Unlock(SInt32 oldLock) +{ + m_lock = oldLock; +} + +// try to increment the lock +void VMIdentifier::IncrementLock(void) +{ + UInt32 spinCount = 0; + + while(true) + { + SInt32 lockValue = m_lock; + + if(lockValue & kLockBit) + { + if(spinCount <= kFastSpinThreshold) + { + Sleep(0); + spinCount++; + } + else + { + Sleep(1); + } + } + else + { + if(lockValue == 1) + { + if(InterlockedCompareExchange(&m_lock, lockValue | kLockBit, lockValue) == lockValue) + { + (*g_objectHandlePolicy)->AddRef(m_handle); + m_lock = 2; + break; + } + } + else + { + if(InterlockedCompareExchange(&m_lock, lockValue + 1, lockValue) == lockValue) + break; + } + } + } +} + +// try to decrement the lock +SInt32 VMIdentifier::DecrementLock(void) +{ + UInt32 spinCount = 0; + + while(true) + { + SInt32 lockValue = m_lock; + + if(lockValue & kLockBit) + { + if(spinCount <= kFastSpinThreshold) + { + Sleep(0); + spinCount++; + } + else + { + Sleep(1); + } + } + else + { + if(lockValue == 2) + { + if(InterlockedCompareExchange(&m_lock, lockValue | kLockBit, lockValue) == lockValue) + { + (*g_objectHandlePolicy)->Release(m_handle); + m_lock = 1; + return 1; + } + } + else + { + if(InterlockedCompareExchange(&m_lock, lockValue - 1, lockValue) == lockValue) + return lockValue - 1; + } + } + } +} + +void VMIdentifier::Destroy(void) +{ + CALL_MEMBER_FN(this, Destroy_Internal)(); + Heap_Free(this); +} + +UInt64 GetArrayType(UInt64 type) +{ + if(type > VMValue::kType_StructArray) + { + return type & ~0x01; + } + else if(type > VMValue::kType_IntegralStart) + { + return type - VMValue::kType_ArrayOffset; + } + + return type; +} diff --git a/f4se/f4se/PapyrusValue.h b/f4se/f4se/PapyrusValue.h new file mode 100644 index 0000000..175f644 --- /dev/null +++ b/f4se/f4se/PapyrusValue.h @@ -0,0 +1,260 @@ +#pragma once + +#include "f4se_common/Utilities.h" +#include "f4se/GameTypes.h" +#include "f4se/GameUtilities.h" + +#include "f4se/PapyrusInterfaces.h" + +class VMIdentifier; + +// 58 +class VMObjectTypeInfo : public IComplexType +{ +public: + virtual ~VMObjectTypeInfo(); + + virtual UInt32 GetType(); // returns 1 (kType_Identifier) + + void * unk20; // 20 + UInt64 unk28; // 28 + UInt64 unk30; // 30 + UInt64 unk38; // 38 + struct MemberData + { + unsigned unk00 : 3; // This == 3 is usually always checked before accessing properties + unsigned unk03 : 5; + unsigned numMembers : 10; // Variables + Properties + unsigned unk19 : 14; + } memberData; + struct PropertyData + { + unsigned numVariables : 10; // Sometimes this is 0 and member != numProperties + unsigned numProperties : 10; // Excludes variables + unsigned unk21: 12; + } propertyData; + UInt32 numFunc; // 48 + UInt32 unk4C; // 4C + + struct PropertyElement + { + BSFixedString propertyName; // 00 + + union // Can be number or IComplexType or IComplexType | 1 (array) + { + UInt64 value; + IComplexType * id; + } type; // 08 + UInt64 unk10; // 10 - Not sure what this is, maybe a hash? + }; + + struct Properties + { + BSFixedString unk00; + BSFixedString unk08; + BSFixedString unk10; + BSFixedString unk18; + BSFixedString unk20; + BSFixedString unk28; + PropertyElement defs[0]; + }; + Properties * properties; // 50 +}; +STATIC_ASSERT(offsetof(VMObjectTypeInfo, memberData) == 0x40); +STATIC_ASSERT(offsetof(VMObjectTypeInfo, propertyData) == 0x44); +STATIC_ASSERT(offsetof(VMObjectTypeInfo, properties) == 0x50); + +// 70 +class VMStructTypeInfo : public IComplexType +{ +public: + virtual ~VMStructTypeInfo(); + + virtual UInt32 GetType(); // returns 7 (kType_Struct) + + struct StructData + { + UInt64 unk00; // 00 + UInt64 unk08; // 08 + UInt64 m_type; // 10 + void * unk18; // 18 + UInt64 unk20; // 20 + }; + tArray m_data; // 20 + + class MemberItem + { + public: + BSFixedString name; // 00 + UInt32 index; // 08 + + bool operator==(const MemberItem & rhs) const { return name == rhs.name; } + bool operator==(const BSFixedString a_name) const { return name == a_name; } + operator UInt64() const { return (UInt64)name.data->Get(); } + + static inline UInt32 GetHash(BSFixedString * key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)key->data, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tname: %s", name.data->Get()); + _MESSAGE("\t\tinstance: %08X", index); + } + }; + + tHashSet m_members; +}; + +// 10 +class VMValue +{ +public: + VMValue() { type.value = kType_None; data.p = nullptr; } + ~VMValue() { CALL_MEMBER_FN(this, Destroy)(); } + + VMValue(const VMValue & other); + VMValue& operator=(const VMValue& other); + + enum + { + kType_None = 0, + kType_Identifier = 1, // Number not actually used by VMValue + kType_String = 2, + kType_Int = 3, + kType_Float = 4, + kType_Bool = 5, + kType_Variable = 6, // Points to a VMValue + kType_Struct = 7, // Number not actually used by VMValue + + kType_ArrayOffset = 10, + + kType_IdentifierArray = 11, + kType_StringArray = 12, + kType_IntArray = 13, + kType_FloatArray = 14, + kType_BoolArray = 15, + kType_VariableArray = 16, + kType_StructArray = 17, // Number not actually used by VMValue + kType_ArrayEnd = 18, + + kType_IntegralStart = kType_StringArray, + kType_IntegralEnd = kType_VariableArray, + }; + + struct ArrayData + { + SInt32 m_refCount; // 00 + UInt32 unk04; // 04 + UInt64 m_type; // 08 - base types 1-6 + UInt64 unk10; // 10 + tArray arr; // 18 + + MEMBER_FN_PREFIX(ArrayData); + DEFINE_MEMBER_FN(Destroy, void, 0x02718050); + }; + + struct StructData + { + SInt32 m_refCount; // 00 + UInt32 unk04; // 04 + UInt64 unk08; // 08 + VMStructTypeInfo * m_type; // 10 + UInt8 unk18; // 18 - set to 1 if unk19 is 1 (3EFCF27952D674A8FA959AABC29A0FE3E726FA91) + UInt8 unk19; // 19 - set to 1 when type+0x68 == 3 + UInt16 unk1A; // 1A + UInt32 unk1C; // 1C + char m_value[0]; // 20 + + VMValue * GetStruct() { return (VMValue *)&m_value[0]; } + }; + + union // Can be number or IComplexType or IComplexType | 1 (array) + { + UInt64 value; + IComplexType * id; + } type; + + union + { + SInt32 i; + UInt32 u; + float f; + bool b; + void * p; + StructData * strct; + ArrayData * arr; + VMValue * var; + VMIdentifier * id; + StringCache::Entry * str; + BSFixedString * GetStr(void) { return (BSFixedString *)(&str); } + BSFixedString * GetStr(void) const { return (BSFixedString *)(&str); } + } data; + + void SetNone(void); + void SetInt(SInt32 i); + void SetFloat(float f); + void SetBool(bool b); + void SetString(const char * str); + + void SetVariable(VMValue * value); + void SetComplexType(IComplexType * typeInfo); + void SetIdentifier(VMIdentifier ** identifier); + + bool IsIntegralType() const; + bool IsIntegralArrayType() const; + bool IsComplexArrayType() const; + bool IsArrayType() const; + bool IsComplexType() const; + bool IsIdentifier(); + + IComplexType * GetComplexType(); + IComplexType * GetComplexType() const; + + UInt8 GetTypeEnum() const; + + MEMBER_FN_PREFIX(VMValue); + DEFINE_MEMBER_FN(Set, void, 0x026F0FB0, const VMValue * src); + DEFINE_MEMBER_FN(Destroy, void, 0x026F0960); + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) +}; + +// 30 - Sized based on number of properties +class VMIdentifier +{ +public: + enum + { + kLockBit = 0x80000000, + kFastSpinThreshold = 10000 + }; + + SInt32 m_refCount; // 00 + UInt32 unk04; // 04 + VMObjectTypeInfo * m_typeInfo; // 08 + void * unk10; // 10 + UInt64 unk18; // 18 + volatile UInt64 m_handle; // 20 + volatile SInt32 m_lock; // 28 + UInt32 unk2C; // 2C + VMValue properties[0]; // 30 + + UInt64 GetHandle(void); + + SInt32 Lock(void); + void Unlock(SInt32 oldLock); + + // lock and refcount? + void IncrementLock(void); + SInt32 DecrementLock(void); + + void Destroy(void); + + MEMBER_FN_PREFIX(VMIdentifier); + DEFINE_MEMBER_FN(Destroy_Internal, void, 0x026EBFA0); +}; + +UInt64 GetArrayType(UInt64 type); diff --git a/f4se/f4se/PapyrusWaterType.cpp b/f4se/f4se/PapyrusWaterType.cpp new file mode 100644 index 0000000..999db7d --- /dev/null +++ b/f4se/f4se/PapyrusWaterType.cpp @@ -0,0 +1,53 @@ +#include "f4se/PapyrusWaterType.h" + +#include "f4se/GameObjects.h" +#include "f4se/GameData.h" + +#include "f4se/PapyrusStruct.h" + +namespace papyrusWaterType +{ + SpellItem* GetConsumeSpell(TESWaterForm* thisWater) + { + return thisWater ? thisWater->consumeSpell : nullptr; + } + + void SetConsumeSpell(TESWaterForm* thisWater, SpellItem* spell) + { + if(thisWater) { + thisWater->consumeSpell = spell; + } + } + + SpellItem* GetContactSpell(TESWaterForm* thisWater) + { + return thisWater ? thisWater->contactSpell : nullptr; + } + + void SetContactSpell(TESWaterForm* thisWater, SpellItem* spell) + { + if(thisWater) { + thisWater->contactSpell = spell; + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusWaterType::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterForm(TESWaterForm::kTypeID, "WaterType"); + + vm->RegisterFunction( + new NativeFunction0 ("GetConsumeSpell", "WaterType", papyrusWaterType::GetConsumeSpell, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetConsumeSpell", "WaterType", papyrusWaterType::SetConsumeSpell, vm)); + + vm->RegisterFunction( + new NativeFunction0 ("GetContactSpell", "WaterType", papyrusWaterType::GetContactSpell, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetContactSpell", "WaterType", papyrusWaterType::SetContactSpell, vm)); +} diff --git a/f4se/f4se/PapyrusWaterType.h b/f4se/f4se/PapyrusWaterType.h new file mode 100644 index 0000000..91f50a1 --- /dev/null +++ b/f4se/f4se/PapyrusWaterType.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusWaterType +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PapyrusWeapon.cpp b/f4se/f4se/PapyrusWeapon.cpp new file mode 100644 index 0000000..de1c335 --- /dev/null +++ b/f4se/f4se/PapyrusWeapon.cpp @@ -0,0 +1,33 @@ +#include "f4se/PapyrusWeapon.h" + +#include "f4se/GameObjects.h" +#include "f4se/GameData.h" + +#include "f4se/PapyrusStruct.h" + +namespace papyrusWeapon +{ + BGSMod::Attachment::Mod * GetEmbeddedMod(TESObjectWEAP* thisWeapon) + { + return thisWeapon ? thisWeapon->weapData.embeddedMod : nullptr; + } + + void SetEmbeddedMod(TESObjectWEAP* thisWeapon, BGSMod::Attachment::Mod * mod) + { + if(thisWeapon) { + thisWeapon->weapData.embeddedMod = mod; + } + } +} + +#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +void papyrusWeapon::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction0 ("GetEmbeddedMod", "Weapon", papyrusWeapon::GetEmbeddedMod, vm)); + + vm->RegisterFunction( + new NativeFunction1 ("SetEmbeddedMod", "Weapon", papyrusWeapon::SetEmbeddedMod, vm)); +} diff --git a/f4se/f4se/PapyrusWeapon.h b/f4se/f4se/PapyrusWeapon.h new file mode 100644 index 0000000..cd106ec --- /dev/null +++ b/f4se/f4se/PapyrusWeapon.h @@ -0,0 +1,9 @@ +#pragma once + +struct StaticFunctionTag; +class VirtualMachine; + +namespace papyrusWeapon +{ + void RegisterFuncs(VirtualMachine* vm); +} diff --git a/f4se/f4se/PluginAPI.h b/f4se/f4se/PluginAPI.h new file mode 100644 index 0000000..6c97960 --- /dev/null +++ b/f4se/f4se/PluginAPI.h @@ -0,0 +1,278 @@ +#pragma once + +typedef UInt32 PluginHandle; // treat this as an opaque type + +class GFxMovieView; +class GFxValue; +class ITaskDelegate; +class F4SEDelayFunctorManager; +class F4SEObjectRegistry; +class F4SEPersistentObjectStorage; + +enum +{ + kPluginHandle_Invalid = 0xFFFFFFFF +}; + +enum +{ + kInterface_Invalid = 0, + kInterface_Messaging, + kInterface_Scaleform, + kInterface_Papyrus, + kInterface_Serialization, + kInterface_Task, + kInterface_Object, + kInterface_Max, +}; + +struct F4SEInterface +{ + UInt32 f4seVersion; + UInt32 runtimeVersion; + UInt32 editorVersion; + UInt32 isEditor; + void * (* QueryInterface)(UInt32 id); + + // call during your Query or Load functions to get a PluginHandle uniquely identifying your plugin + // invalid if called at any other time, so call it once and save the result + PluginHandle (* GetPluginHandle)(void); + + // returns the F4SE build's release index + UInt32 (* GetReleaseIndex)(void); +}; + + +/**** Messaging API docs ******************************************************************** + * + * Messaging API allows inter-plugin communication at run-time. A plugin may register + * one callback for each plugin from which it wishes to receive messages, specifying + * the sender by name in the call to RegisterListener(). RegisterListener returns false + * if the specified plugin is not loaded, true otherwise. Any messages dispatched by + * the specified plugin will then be forwarded to the listener as they occur. Passing NULL as + * the sender registers the calling plugin as a listener to every loaded plugin. + * + * Messages may be dispatched via Dispatch() to either a specific listener (specified + * by name) or to all listeners (with NULL passed as the receiver). The contents and format of + * messageData are left up to the sender, and the receiver is responsible for casting the message + * to the expected type. If no plugins are registered as listeners for the sender, + * Dispatch() returns false, otherwise it returns true. + * + * Calling RegisterListener() or Dispatch() during plugin load is not advised as the requested plugin + * may not yet be loaded at that point. Instead, if you wish to register as a listener or dispatch a + * message immediately after plugin load, use RegisterListener() during load to register to receive + * messages from F4SE (sender name: "F4SE"). You will then receive a message from F4SE once + * all plugins have been loaded, at which point it is safe to establish communications between + * plugins. + * + * Some plugin authors may wish to use strings instead of integers to denote message type. In + * that case the receiver can pass the address of the string as an integer and require the receiver + * to cast it back to a char* on receipt. + * + *********************************************************************************************/ + +struct F4SEMessagingInterface +{ + struct Message { + const char * sender; + UInt32 type; + UInt32 dataLen; + void * data; + }; + + typedef void (* EventCallback)(Message* msg); + + enum { + kInterfaceVersion = 1 + }; + + // F4SE messages + enum { + kMessage_PostLoad, // sent to registered plugins once all plugins have been loaded (no data) + kMessage_PostPostLoad, // sent right after kMessage_PostLoad to facilitate the correct dispatching/registering of messages/listeners + kMessage_PreLoadGame, // dispatched immediately before savegame is read by Fallout + // dataLen: length of file path, data: char* file path of .ess savegame file + kMessage_PostLoadGame, //dispatched after an attempt to load a saved game has finished (the game's LoadGame() routine + //has returned). You will probably want to handle this event if your plugin uses a Preload callback + //as there is a chance that after that callback is invoked the game will encounter an error + //while loading the saved game (eg. corrupted save) which may require you to reset some of your + //plugin state. + //data: bool, true if game successfully loaded, false otherwise + // plugins may register as listeners during the first callback while deferring dispatches until the next + kMessage_PreSaveGame, // right before the game is saved + kMessage_PostSaveGame, // right after the game is saved + kMessage_DeleteGame, // sent right before deleting the .f4se cosave and the .ess save. + // dataLen: length of file path, data: char* file path of .ess savegame file + kMessage_InputLoaded, // sent right after game input is loaded, right before the main menu initializes + kMessage_NewGame, // sent after a new game is created, before the game has loaded (Sends CharGen TESQuest pointer) + kMessage_GameLoaded, // sent after the game has finished loading (only sent once) + kMessage_GameDataReady // sent when the data handler is ready (data is false before loading, true when finished loading) + }; + + UInt32 interfaceVersion; + bool (* RegisterListener)(PluginHandle listener, const char* sender, EventCallback handler); + bool (* Dispatch)(PluginHandle sender, UInt32 messageType, void * data, UInt32 dataLen, const char* receiver); + + // Use this to acquire F4SE's internal EventDispatchers so that you can sink to them + // Currently none implemented yet + void * (* GetEventDispatcher)(UInt32 dispatcherId); +}; + +struct F4SEScaleformInterface +{ + enum + { + kInterfaceVersion = 1 + }; + + UInt32 interfaceVersion; + + // This callback will be called once for every new menu that is created. + // Create your objects relative to the 'root' GFxValue parameter. + typedef bool (* RegisterCallback)(GFxMovieView * view, GFxValue * value); + + // Register your plugin's scaleform API creation callback here. + // The "name" parameter will be used to create an object with the path: + // "f4se.plugins.name" that will be passed to the callback. + // Make sure that the memory it points to is valid from the point the callback + // is registered until the game exits. + bool (* Register)(const char * name, RegisterCallback callback); +}; + + +struct F4SESerializationInterface +{ + enum + { + kInterfaceVersion = 1, + }; + + typedef void (* EventCallback)(const F4SESerializationInterface * intfc); + typedef void (* FormDeleteCallback)(UInt64 handle); + + UInt32 version; + + void (* SetUniqueID)(PluginHandle plugin, UInt32 uid); + + void (* SetRevertCallback)(PluginHandle plugin, EventCallback callback); + void (* SetSaveCallback)(PluginHandle plugin, EventCallback callback); + void (* SetLoadCallback)(PluginHandle plugin, EventCallback callback); + void (* SetFormDeleteCallback)(PluginHandle plugin, FormDeleteCallback callback); + + bool (* WriteRecord)(UInt32 type, UInt32 version, const void * buf, UInt32 length); + bool (* OpenRecord)(UInt32 type, UInt32 version); + bool (* WriteRecordData)(const void * buf, UInt32 length); + + bool (* GetNextRecordInfo)(UInt32 * type, UInt32 * version, UInt32 * length); + UInt32 (* ReadRecordData)(void * buf, UInt32 length); + bool (* ResolveHandle)(UInt64 handle, UInt64 * handleOut); + bool (* ResolveFormId)(UInt32 formId, UInt32 * formIdOut); +}; + +class VirtualMachine; + +struct F4SEPapyrusInterface +{ + enum + { + kInterfaceVersion = 2 + }; + UInt32 interfaceVersion; + + typedef bool (* RegisterFunctions)(VirtualMachine * vm); + bool (* Register)(RegisterFunctions callback); + + typedef void (* RegistrantFunctor)(UInt64 handle, const char * scriptName, const char * callbackName, void * data); + void (* GetExternalEventRegistrations)(const char * eventName, void * data, RegistrantFunctor functor); +}; + +struct F4SETaskInterface +{ + enum + { + kInterfaceVersion = 2 + }; + UInt32 interfaceVersion; + + void (* AddTask)(ITaskDelegate * task); + void (* AddUITask)(ITaskDelegate * task); +}; + +struct F4SEObjectInterface +{ + enum + { + kInterfaceVersion = 1 + }; + + UInt32 interfaceVersion; + + F4SEDelayFunctorManager & (* GetDelayFunctorManager)(); + F4SEObjectRegistry & (* GetObjectRegistry)(); + F4SEPersistentObjectStorage & (* GetPersistentObjectStorage)(); +}; + +struct PluginInfo +{ + enum + { + kInfoVersion = 1 + }; + + UInt32 infoVersion; + const char * name; + UInt32 version; +}; + +typedef bool (* _F4SEPlugin_Query)(const F4SEInterface * f4se, PluginInfo * info); +typedef bool (* _F4SEPlugin_Load)(const F4SEInterface * f4se); + +/**** plugin API docs ********************************************************** + * + * The base API is pretty simple. Create a project based on the + * f4se_plugin_example project included with the F4SE source code, then define + * and export these functions: + * + * bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) + * + * This primary purposes of this function are to fill out the PluginInfo + * structure, and to perform basic version checks based on the info in the + * F4SEInterface structure. Return false if your plugin is incompatible with + * the version of F4SE or the runtime passed in, otherwise return true. In + * either case, fill out the PluginInfo structure. + * + * Do not do anything other than fill out the PluginInfo structure and return + * true/false in this callback. + * + * If the plugin is being loaded in the context of the editor, isEditor will be + * non-zero, editorVersion will contain the current editor version, and + * runtimeVersion will be zero. In this case you can probably just return + * true, however if you have multiple DLLs implementing the same behavior, for + * example one for each version of ther runtime, only one of them should return + * true. + * + * The PluginInfo fields should be filled out as follows: + * - infoVersion should be set to PluginInfo::kInfoVersion + * - name should be a pointer to a null-terminated string uniquely identifying + * your plugin, it will be used in the plugin querying API + * - version is only used by the plugin query API, and will be returned to + * scripts requesting the current version of your plugin + * + * bool F4SEPlugin_Load(const F4SEInterface * f4se) + * + * In this function, use the interfaces above to register your commands, patch + * memory, generally do whatever you need to for integration with the runtime. + * + * At this time, or at any point forward you can call the QueryInterface + * callback to retrieve an interface structure for the base services provided + * by the F4SE core. + * + * You may optionally return false from this function to unload your plugin, + * but make sure that you DO NOT register any commands if you do. + * + * Note that all structure versions are backwards-compatible, so you only need + * to check against the latest version that you need. New fields will be only + * added to the end, and all old fields will remain compatible with their + * previous implementations. + * + ******************************************************************************/ diff --git a/f4se/f4se/PluginManager.cpp b/f4se/f4se/PluginManager.cpp new file mode 100644 index 0000000..091d9c8 --- /dev/null +++ b/f4se/f4se/PluginManager.cpp @@ -0,0 +1,604 @@ +#include "PluginManager.h" +#include "common/IDirectoryIterator.h" +#include "GameAPI.h" +#include "f4se_common/Utilities.h" +#include "f4se_common/f4se_version.h" + +PluginManager g_pluginManager; + +PluginManager::LoadedPlugin * PluginManager::s_currentLoadingPlugin = NULL; +PluginHandle PluginManager::s_currentPluginHandle = 0; + +static const F4SEInterface g_F4SEInterface = +{ + PACKED_F4SE_VERSION, + +#ifdef RUNTIME + RUNTIME_VERSION, + 0, + 0, +#else + 0, + 0, + 1, +#endif + + PluginManager::QueryInterface, + PluginManager::GetPluginHandle, + PluginManager::GetReleaseIndex +}; + +static const F4SEMessagingInterface g_F4SEMessagingInterface = +{ + F4SEMessagingInterface::kInterfaceVersion, + PluginManager::RegisterListener, + PluginManager::Dispatch_Message, +}; + +#include "Hooks_Scaleform.h" + +static const F4SEScaleformInterface g_F4SEScaleformInterface = +{ + F4SEScaleformInterface::kInterfaceVersion, + RegisterScaleformPlugin +}; + +#include "Hooks_Papyrus.h" + +static const F4SEPapyrusInterface g_F4SEPapyrusInterface = +{ + F4SEPapyrusInterface::kInterfaceVersion, + RegisterPapyrusPlugin, + GetExternalEventRegistrations +}; + +#include "f4se/Serialization.h" + +static const F4SESerializationInterface g_F4SESerializationInterface = +{ + F4SESerializationInterface::kInterfaceVersion, + + Serialization::SetUniqueID, + + Serialization::SetRevertCallback, + Serialization::SetSaveCallback, + Serialization::SetLoadCallback, + Serialization::SetFormDeleteCallback, + + Serialization::WriteRecord, + Serialization::OpenRecord, + Serialization::WriteRecordData, + + Serialization::GetNextRecordInfo, + Serialization::ReadRecordData, + Serialization::ResolveHandle, + Serialization::ResolveFormId +}; + +#include "Hooks_Threads.h" + +static const F4SETaskInterface g_F4SETaskInterface = +{ + F4SETaskInterface::kInterfaceVersion, + + TaskInterface::AddTask, + TaskInterface::AddUITask +}; + +#include "f4se/PapyrusDelayFunctors.h" +#include "f4se/PapyrusObjects.h" + +static const F4SEObjectInterface g_F4SEObjectInterface = +{ + F4SEObjectInterface::kInterfaceVersion, + F4SEDelayFunctorManagerInstance, + F4SEObjectRegistryInstance, + F4SEObjectStorageInstance +}; + +PluginManager::PluginManager() +{ + // +} + +PluginManager::~PluginManager() +{ + DeInit(); +} + +bool PluginManager::Init(void) +{ + bool result = false; + + if(FindPluginDirectory()) + { + _MESSAGE("plugin directory = %s", m_pluginDirectory.c_str()); + + __try + { + InstallPlugins(); + + result = true; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + // something very bad happened + _ERROR("exception occurred while loading plugins"); + } + } + + return result; +} + +void PluginManager::DeInit(void) +{ + for(LoadedPluginList::iterator iter = m_plugins.begin(); iter != m_plugins.end(); ++iter) + { + LoadedPlugin * plugin = &(*iter); + + if(plugin->handle) + { + FreeLibrary(plugin->handle); + } + } + + m_plugins.clear(); +} + +UInt32 PluginManager::GetNumPlugins(void) +{ + UInt32 numPlugins = m_plugins.size(); + + // is one currently loading? + if(s_currentLoadingPlugin) numPlugins++; + + return numPlugins; +} + +PluginInfo * PluginManager::GetInfoByName(const char * name) +{ + for(LoadedPluginList::iterator iter = m_plugins.begin(); iter != m_plugins.end(); ++iter) + { + LoadedPlugin * plugin = &(*iter); + + if(plugin->info.name && !_stricmp(name, plugin->info.name)) + return &plugin->info; + } + + return NULL; +} + +void * PluginManager::QueryInterface(UInt32 id) +{ + void * result = NULL; + +#ifdef RUNTIME + switch(id) + { + case kInterface_Messaging: + result = (void *)&g_F4SEMessagingInterface; + break; + case kInterface_Scaleform: + result = (void *)&g_F4SEScaleformInterface; + break; + case kInterface_Papyrus: + result = (void *)&g_F4SEPapyrusInterface; + break; + case kInterface_Serialization: + result = (void *)&g_F4SESerializationInterface; + break; + case kInterface_Task: + result = (void *)&g_F4SETaskInterface; + break; + case kInterface_Object: + result = (void *)&g_F4SEObjectInterface; + break; + default: + _WARNING("unknown QueryInterface %08X", id); + break; + } +#else + _WARNING("unknown QueryInterface %08X", id); +#endif + + return result; +} + +PluginHandle PluginManager::GetPluginHandle(void) +{ + ASSERT_STR(s_currentPluginHandle, "A plugin has called F4SEInterface::GetPluginHandle outside of its Query/Load handlers"); + + return s_currentPluginHandle; +} + +UInt32 PluginManager::GetReleaseIndex( void ) +{ + return F4SE_VERSION_RELEASEIDX; +} + +bool PluginManager::FindPluginDirectory(void) +{ + bool result = false; + + // find the path /data/f4se/ + std::string runtimeDirectory = GetRuntimeDirectory(); + + if(!runtimeDirectory.empty()) + { + m_pluginDirectory = runtimeDirectory + "Data\\F4SE\\Plugins\\"; + result = true; + } + + return result; +} + +void PluginManager::InstallPlugins(void) +{ + // avoid realloc + m_plugins.reserve(5); + + for(IDirectoryIterator iter(m_pluginDirectory.c_str(), "*.dll"); !iter.Done(); iter.Next()) + { + std::string pluginPath = iter.GetFullPath(); + + _MESSAGE("checking plugin %s", pluginPath.c_str()); + + LoadedPlugin plugin; + memset(&plugin, 0, sizeof(plugin)); + + s_currentLoadingPlugin = &plugin; + s_currentPluginHandle = m_plugins.size() + 1; // +1 because 0 is reserved for internal use + + plugin.handle = (HMODULE)LoadLibrary(pluginPath.c_str()); + if(plugin.handle) + { + bool success = false; + + plugin.query = (_F4SEPlugin_Query)GetProcAddress(plugin.handle, "F4SEPlugin_Query"); + plugin.load = (_F4SEPlugin_Load)GetProcAddress(plugin.handle, "F4SEPlugin_Load"); + + if(plugin.query && plugin.load) + { + const char * loadStatus = NULL; + + loadStatus = SafeCallQueryPlugin(&plugin, &g_F4SEInterface); + + if(!loadStatus) + { + loadStatus = CheckPluginCompatibility(&plugin); + + if(!loadStatus) + { + loadStatus = SafeCallLoadPlugin(&plugin, &g_F4SEInterface); + + if(!loadStatus) + { + loadStatus = "loaded correctly"; + success = true; + } + } + } + else + { + loadStatus = "reported as incompatible during query"; + } + + ASSERT(loadStatus); + + _MESSAGE("plugin %s (%08X %s %08X) %s", + pluginPath.c_str(), + plugin.info.infoVersion, + plugin.info.name ? plugin.info.name : "", + plugin.info.version, + loadStatus); + } + else + { + _MESSAGE("plugin %s does not appear to be an F4SE plugin", pluginPath.c_str()); + } + + if(success) + { + // succeeded, add it to the list + m_plugins.push_back(plugin); + } + else + { + // failed, unload the library + FreeLibrary(plugin.handle); + } + } + else + { + _ERROR("couldn't load plugin %s (Error %d)", pluginPath.c_str(), GetLastError()); + } + } + + s_currentLoadingPlugin = NULL; + s_currentPluginHandle = 0; + + // alert any listeners that plugin load has finished + Dispatch_Message(0, F4SEMessagingInterface::kMessage_PostLoad, NULL, 0, NULL); + // second post-load dispatch + Dispatch_Message(0, F4SEMessagingInterface::kMessage_PostPostLoad, NULL, 0, NULL); +} + +// SEH-wrapped calls to plugin API functions to avoid bugs from bringing down the core +const char * PluginManager::SafeCallQueryPlugin(LoadedPlugin * plugin, const F4SEInterface * f4se) +{ + __try + { + if(!plugin->query(f4se, &plugin->info)) + { + return "reported as incompatible during query"; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + // something very bad happened + return "disabled, fatal error occurred while querying plugin"; + } + + return NULL; +} + +const char * PluginManager::SafeCallLoadPlugin(LoadedPlugin * plugin, const F4SEInterface * f4se) +{ + __try + { + if(!plugin->load(f4se)) + { + return "reported as incompatible during load"; + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + // something very bad happened + return "disabled, fatal error occurred while loading plugin"; + } + + return NULL; +} + +enum +{ + kCompat_BlockFromRuntime = 1 << 0, + kCompat_BlockFromEditor = 1 << 1, +}; + +struct MinVersionEntry +{ + const char * name; + UInt32 minVersion; + const char * reason; + UInt32 compatFlags; +}; + +static const MinVersionEntry kMinVersionList[] = +{ + { NULL, 0, NULL } +}; + +// see if we have a plugin that we know causes problems +const char * PluginManager::CheckPluginCompatibility(LoadedPlugin * plugin) +{ + __try + { + // stupid plugin check + if(!plugin->info.name) + { + return "disabled, no name specified"; + } + + // check for 'known bad' versions of plugins + for(const MinVersionEntry * iter = kMinVersionList; iter->name; ++iter) + { + if(!strcmp(iter->name, plugin->info.name)) + { + if(plugin->info.version < iter->minVersion) + { +#ifdef RUNTIME + if(iter->compatFlags & kCompat_BlockFromRuntime) + { + return iter->reason; + } +#endif + +#ifdef EDITOR + if(iter->compatFlags & kCompat_BlockFromEditor) + { + return iter->reason; + } +#endif + } + + break; + } + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + // paranoia + return "disabled, fatal error occurred while checking plugin compatibility"; + } + + return NULL; +} + +// Plugin communication interface +struct PluginListener { + PluginHandle listener; + F4SEMessagingInterface::EventCallback handleMessage; +}; + +typedef std::vector > PluginListeners; +static PluginListeners s_pluginListeners; + +bool PluginManager::RegisterListener(PluginHandle listener, const char* sender, F4SEMessagingInterface::EventCallback handler) +{ + // because this can be called while plugins are loading, gotta make sure number of plugins hasn't increased + UInt32 numPlugins = g_pluginManager.GetNumPlugins() + 1; + if (s_pluginListeners.size() < numPlugins) + { + s_pluginListeners.resize(numPlugins + 5); // add some extra room to avoid unnecessary re-alloc + } + + _MESSAGE("registering plugin listener for %s at %u of %u", sender, listener, numPlugins); + + // handle > num plugins = invalid + if (listener > g_pluginManager.GetNumPlugins() || !handler) + { + return false; + } + + if (sender) + { + // is target loaded? + PluginHandle target = g_pluginManager.LookupHandleFromName(sender); + if (target == kPluginHandle_Invalid) + { + return false; + } + // is listener already registered? + for (std::vector::iterator iter = s_pluginListeners[target].begin(); iter != s_pluginListeners[target].end(); ++iter) + { + if (iter->listener == listener) + { + return true; + } + } + + // register new listener + PluginListener newListener; + newListener.handleMessage = handler; + newListener.listener = listener; + + s_pluginListeners[target].push_back(newListener); + } + else + { + // register listener to every loaded plugin + UInt32 idx = 0; + for(PluginListeners::iterator iter = s_pluginListeners.begin(); iter != s_pluginListeners.end(); ++iter) + { + // don't add the listener to its own list + if (idx && idx != listener) + { + bool skipCurrentList = false; + for (std::vector::iterator iterEx = iter->begin(); iterEx != iter->end(); ++iterEx) + { + // already registered with this plugin, skip it + if (iterEx->listener == listener) + { + skipCurrentList = true; + break; + } + } + if (skipCurrentList) + { + continue; + } + PluginListener newListener; + newListener.handleMessage = handler; + newListener.listener = listener; + + iter->push_back(newListener); + } + idx++; + } + } + + return true; +} + +bool PluginManager::Dispatch_Message(PluginHandle sender, UInt32 messageType, void * data, UInt32 dataLen, const char* receiver) +{ +#ifdef _DEBUG + _MESSAGE("dispatch message (%d) to plugin listeners", messageType); +#endif + UInt32 numRespondents = 0; + PluginHandle target = kPluginHandle_Invalid; + + if (!s_pluginListeners.size()) // no listeners yet registered + { +#ifdef _DEBUG + _MESSAGE("no listeners registered"); +#endif + return false; + } + else if (sender >= s_pluginListeners.size()) + { +#ifdef _DEBUG + _MESSAGE("sender is not in the list"); +#endif + return false; + } + + if (receiver) + { + target = g_pluginManager.LookupHandleFromName(receiver); + if (target == kPluginHandle_Invalid) + return false; + } + + const char* senderName = g_pluginManager.GetPluginNameFromHandle(sender); + if (!senderName) + return false; + for (std::vector::iterator iter = s_pluginListeners[sender].begin(); iter != s_pluginListeners[sender].end(); ++iter) + { + F4SEMessagingInterface::Message msg; + msg.data = data; + msg.type = messageType; + msg.sender = senderName; + msg.dataLen = dataLen; + + if (target != kPluginHandle_Invalid) // sending message to specific plugin + { + if (iter->listener == target) + { + iter->handleMessage(&msg); + return true; + } + } + else + { +#ifdef _DEBUG + _DMESSAGE("sending message type %u to plugin %u", messageType, iter->listener); +#endif + iter->handleMessage(&msg); + numRespondents++; + } + } +#ifdef _DEBUG + _DMESSAGE("dispatched message."); +#endif + return numRespondents ? true : false; +} + +const char * PluginManager::GetPluginNameFromHandle(PluginHandle handle) +{ + if (handle > 0 && handle <= m_plugins.size()) + return (m_plugins[handle - 1].info.name); + else if (handle == 0) + return "F4SE"; + + return NULL; +} + +PluginHandle PluginManager::LookupHandleFromName(const char* pluginName) +{ + if (!_stricmp("F4SE", pluginName)) + return 0; + + UInt32 idx = 1; + for(LoadedPluginList::iterator iter = m_plugins.begin(); iter != m_plugins.end(); ++iter) + { + LoadedPlugin * plugin = &(*iter); + if(!_stricmp(plugin->info.name, pluginName)) + { + return idx; + } + idx++; + } + return kPluginHandle_Invalid; +} diff --git a/f4se/f4se/PluginManager.h b/f4se/f4se/PluginManager.h new file mode 100644 index 0000000..39f2c7a --- /dev/null +++ b/f4se/f4se/PluginManager.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include "f4se/PluginAPI.h" + +class PluginManager +{ +public: + PluginManager(); + ~PluginManager(); + + bool Init(void); + void DeInit(void); + + PluginInfo * GetInfoByName(const char * name); + const char * GetPluginNameFromHandle(PluginHandle handle); + + PluginHandle LookupHandleFromName(const char* pluginName); + + + UInt32 GetNumPlugins(void); + + static void * QueryInterface(UInt32 id); + static PluginHandle GetPluginHandle(void); + static UInt32 GetReleaseIndex(void); + + static bool Dispatch_Message(PluginHandle sender, UInt32 messageType, void * data, UInt32 dataLen, const char* receiver); + static bool RegisterListener(PluginHandle listener, const char* sender, F4SEMessagingInterface::EventCallback handler); + +private: + struct LoadedPlugin + { + // internals + HMODULE handle; + PluginInfo info; + + _F4SEPlugin_Query query; + _F4SEPlugin_Load load; + }; + + bool FindPluginDirectory(void); + void InstallPlugins(void); + + const char * SafeCallQueryPlugin(LoadedPlugin * plugin, const F4SEInterface * f4se); + const char * SafeCallLoadPlugin(LoadedPlugin * plugin, const F4SEInterface * f4se); + + const char * CheckPluginCompatibility(LoadedPlugin * plugin); + + typedef std::vector LoadedPluginList; + + std::string m_pluginDirectory; + LoadedPluginList m_plugins; + + static LoadedPlugin * s_currentLoadingPlugin; + static PluginHandle s_currentPluginHandle; +}; + +extern PluginManager g_pluginManager; + +extern const F4SESerializationInterface g_F4SESerializationInterface; +extern const F4SEPapyrusInterface g_F4SEPapyrusInterface; +extern const F4SEScaleformInterface g_F4SEScaleformInterface; +extern const F4SEMessagingInterface g_F4SEMessagingInterface; +extern const F4SETaskInterface g_F4SETaskInterface; diff --git a/f4se/f4se/ScaleformAPI.cpp b/f4se/f4se/ScaleformAPI.cpp new file mode 100644 index 0000000..d25daf7 --- /dev/null +++ b/f4se/f4se/ScaleformAPI.cpp @@ -0,0 +1,14 @@ +#include "f4se/ScaleformAPI.h" + +// 2D9A7EEA0F5FCB0ACE8EC9010926351C3F504E26+28 +RelocPtr g_scaleformHeap(0x06577EB0); + +void * ScaleformHeap_Allocate(UInt32 size) +{ + return (*g_scaleformHeap)->Allocate(size); +} + +void ScaleformHeap_Free(void * ptr) +{ + (*g_scaleformHeap)->Free(ptr); +} diff --git a/f4se/f4se/ScaleformAPI.h b/f4se/f4se/ScaleformAPI.h new file mode 100644 index 0000000..429f9aa --- /dev/null +++ b/f4se/f4se/ScaleformAPI.h @@ -0,0 +1,26 @@ +#pragma once + +#include "f4se_common/Relocation.h" + +class ScaleformHeap +{ +public: + virtual void Fn_00(void); + virtual void Fn_01(void); + virtual void Fn_02(void); + virtual void Fn_03(void); + virtual void Fn_04(void); + virtual void Fn_05(void); + virtual void Fn_06(void); + virtual void Fn_07(void); + virtual void Fn_08(void); + virtual void Fn_09(void); + virtual void * Allocate(size_t size, UInt32 unk = 0); // unk is probably align, maybe flags (haven't traced) + virtual void Fn_0B(void); + virtual void Free(void * ptr); +}; + +extern RelocPtr g_scaleformHeap; + +extern void * ScaleformHeap_Allocate(UInt32 size); +extern void ScaleformHeap_Free(void * ptr); diff --git a/f4se/f4se/ScaleformCallbacks.cpp b/f4se/f4se/ScaleformCallbacks.cpp new file mode 100644 index 0000000..ff205b1 --- /dev/null +++ b/f4se/f4se/ScaleformCallbacks.cpp @@ -0,0 +1,15 @@ +#include "f4se/ScaleformCallbacks.h" + +UInt32 g_GFxFunctionHandler_count = 0; + +GFxFunctionHandler::GFxFunctionHandler() +{ + g_GFxFunctionHandler_count++; +} + +GFxFunctionHandler::~GFxFunctionHandler() +{ + g_GFxFunctionHandler_count--; +} + +FunctionHandlerCache g_functionHandlerCache; diff --git a/f4se/f4se/ScaleformCallbacks.h b/f4se/f4se/ScaleformCallbacks.h new file mode 100644 index 0000000..3242b74 --- /dev/null +++ b/f4se/f4se/ScaleformCallbacks.h @@ -0,0 +1,87 @@ +#pragma once + +#include "f4se_common/Utilities.h" + +#include "f4se/ScaleformTypes.h" +#include "f4se/ScaleformMovie.h" + +#include +#include + +class GFxValue; +class GFxMovieView; + +class GFxFunctionHandler : public GRefCountBase +{ +public: + GFxFunctionHandler(); + virtual ~GFxFunctionHandler(); + + // 38 + class Args + { + public: + GFxValue * result; // 00 + GFxMovieView * movie; // 08 + GFxValue * thisObj; // 10 + GFxValue * unk18; // 18 + GFxValue * args; // 20 + UInt32 numArgs; // 28 + UInt32 pad2C; // 2C + UInt32 optionID; // 30 pUserData + }; + + virtual void Invoke(Args * args) = 0; +}; + +typedef std::map FunctionHandlerCache; +extern FunctionHandlerCache g_functionHandlerCache; + +template +void CreateFunction(GFxValue * dst, GFxMovieRoot * movie) +{ + // either allocate the object or retrieve an existing instance from the cache + GFxFunctionHandler * fn = nullptr; + + // check the cache + FunctionHandlerCache::iterator iter = g_functionHandlerCache.find(&typeid(T)); + if(iter != g_functionHandlerCache.end()) + fn = iter->second; + + if(!fn) + { + // not found, allocate a new one + fn = new T; + + // add it to the cache + // cache now owns the object as far as refcounting goes + g_functionHandlerCache[&typeid(T)] = fn; + } + + // create the function object + movie->CreateFunction(dst, fn); +} + +template +void RegisterFunction(GFxValue * dst, GFxMovieRoot * movie, const char * name) +{ + // either allocate the object or retrieve an existing instance from the cache + GFxValue fnValue; + CreateFunction(&fnValue, movie); + dst->SetMember(name, &fnValue); +} + +class BSGFxFunctionHandler : public GFxFunctionHandler +{ +public: + virtual ~BSGFxFunctionHandler() { }; +}; + +class SWFToCodeFunctionHandler : public GFxFunctionHandler +{ +public: + virtual ~SWFToCodeFunctionHandler() { } + virtual void RegisterFunctions() = 0; // 02 + + DEFINE_MEMBER_FN_2(RegisterNativeFunction, void, 0x02110390, const char * name, UInt32 index); +}; \ No newline at end of file diff --git a/f4se/f4se/ScaleformLoader.cpp b/f4se/f4se/ScaleformLoader.cpp new file mode 100644 index 0000000..7ac4d94 --- /dev/null +++ b/f4se/f4se/ScaleformLoader.cpp @@ -0,0 +1,4 @@ +#include "f4se/ScaleformLoader.h" + +// 7A4A1713A39F72AA5098850E74E38B4655E445AF+4D +RelocPtr g_scaleformManager(0x058DE410); diff --git a/f4se/f4se/ScaleformLoader.h b/f4se/f4se/ScaleformLoader.h new file mode 100644 index 0000000..2ccc78c --- /dev/null +++ b/f4se/f4se/ScaleformLoader.h @@ -0,0 +1,49 @@ +#pragma once + +#include "f4se_common/Relocation.h" +#include "f4se_common/Utilities.h" + +#include "f4se/ScaleformState.h" + +class GImageInfoBase; +class IMenu; +class GFxMovieView; + +class GFxImageLoader : public GFxState +{ +public: + virtual GImageInfoBase* LoadImage(const char * url) = 0; +}; + +class BSScaleformImageLoader : public GFxImageLoader +{ +public: + virtual ~BSScaleformImageLoader(); + virtual GImageInfoBase* LoadImage(const char * url); + virtual void Unk_02(void); + virtual void Unk_03(void); + virtual void Unk_04(void); + virtual void Unk_06(void); + + MEMBER_FN_PREFIX(BSScaleformImageLoader); + DEFINE_MEMBER_FN_1(MountImage, bool, 0x022E5AD0, NiTexture **); + DEFINE_MEMBER_FN_1(UnmountImage, bool, 0x022E5DE0, NiTexture **); +}; + +class BSScaleformManager +{ +public: + virtual ~BSScaleformManager(); + virtual void Unk_01(void); // Init image loader? + + UInt64 unk08; // 08 - 0 + GFxStateBag * stateBag; // 10 + void * unk18; // 18 + void * unk20; // 20 + BSScaleformImageLoader * imageLoader; // 28 + + MEMBER_FN_PREFIX(BSScaleformManager); + DEFINE_MEMBER_FN(LoadMovie, bool, 0x02110AD0, IMenu * menu, GFxMovieView *&, const char * name, const char * stagePath, UInt32 flags); +}; + +extern RelocPtr g_scaleformManager; diff --git a/f4se/f4se/ScaleformMovie.cpp b/f4se/f4se/ScaleformMovie.cpp new file mode 100644 index 0000000..2570273 --- /dev/null +++ b/f4se/f4se/ScaleformMovie.cpp @@ -0,0 +1 @@ +#include "f4se/ScaleformMovie.h" diff --git a/f4se/f4se/ScaleformMovie.h b/f4se/f4se/ScaleformMovie.h new file mode 100644 index 0000000..f07879b --- /dev/null +++ b/f4se/f4se/ScaleformMovie.h @@ -0,0 +1,92 @@ +#pragma once + +class GFxFunctionHandler; +class GFxValue; + +// UNTESTED!!! +class GFxMovieRoot +{ +public: + virtual ~GFxMovieRoot(); + + virtual void Unk_01(); + virtual void Unk_02(); + virtual void Unk_03(); + virtual void Unk_04(); + virtual void Unk_05(); + virtual void Unk_06(); + virtual void Unk_07(); + virtual void Unk_08(); + virtual void Unk_09(); + virtual void Unk_0A(); + virtual void Unk_0B(); + virtual void Unk_0C(); + virtual void Unk_0D(); + virtual void Unk_0E(); + virtual void Unk_0F(); + virtual void Unk_10(); + virtual void Unk_11(); + virtual void Unk_12(); + virtual void Unk_13(); + virtual void Unk_14(); + virtual void Unk_15(); + virtual void Unk_16(); + virtual void Unk_17(); + virtual void Unk_18(); + virtual void Unk_19(); + virtual void Unk_1A(); + virtual void Unk_1B(); + virtual void Unk_1C(); + virtual void Unk_1D(); + virtual void Unk_1E(); + virtual void Unk_1F(); + virtual void Unk_20(); + virtual void Unk_21(); + virtual void Unk_22(); + virtual void Unk_23(); + virtual void Unk_24(); + virtual void Unk_25(); + virtual void Unk_26(); + virtual void Unk_27(); + virtual void Unk_28(); + virtual void Unk_29(); + virtual void Unk_2A(); + virtual void Unk_2B(); + virtual void CreateString(GFxValue* pValue, const char* pString); + virtual void CreateStringW(GFxValue* pValue, const wchar_t* pString); + virtual void CreateObject(GFxValue* pValue, const char* className = NULL, const GFxValue* pArgs = NULL, UInt32 nArgs = 0); + virtual void CreateArray(GFxValue* pValue); + virtual void CreateFunction(GFxValue* pValue, GFxFunctionHandler* pFunc, void* puserData = NULL); + virtual bool SetVariable(const char* pVarPath, const GFxValue * value, UInt32 setType = 0); + virtual bool GetVariable(GFxValue *pValue, const char* pVarPath) const; + virtual void Unk_33(); + virtual void Unk_34(); + virtual void Unk_35(); + virtual bool GetVariableArray(UInt32 type, const char* pPathToVar, UInt32 index, void* pData, UInt32 count); + virtual void Unk_37(); + virtual void Unk_38(); + virtual bool Invoke(const char* pPathToMethod, GFxValue *pResult, const GFxValue* pArgs, UInt32 nArgs); + virtual bool InvokeArgs(const char* pPathToMethod, GFxValue *presult, const char* pArgFmt, va_list args); + + bool Invoke(const char* pPathToMethod, GFxValue *presult, const char* pArgFmt, ...) + { + // Delegate to InvokeArgs + va_list args; + va_start(args, pArgFmt); + bool retVal = InvokeArgs(pPathToMethod, presult, pArgFmt, args); + va_end(args); + return retVal; + } +}; + +class GFxMovieView +{ +public: + virtual ~GFxMovieView(); + + UInt32 unk08; // 08 + UInt32 unk0C; // 0C + void * unk10; // 10 + GFxMovieRoot * movieRoot; // 18 + // ... +}; diff --git a/f4se/f4se/ScaleformSerialization.cpp b/f4se/f4se/ScaleformSerialization.cpp new file mode 100644 index 0000000..2480b09 --- /dev/null +++ b/f4se/f4se/ScaleformSerialization.cpp @@ -0,0 +1,81 @@ +#include "f4se/ScaleformSerialization.h" + +#include "f4se/ScaleformCallbacks.h" +#include "f4se/ScaleformValue.h" + +namespace Serialization +{ + template <> + bool WriteData(const F4SESerializationInterface* intfc, const GFxValue* val) + { + UInt32 type = val->GetType(); + if (! WriteData(intfc, &type)) + return false; + + switch (type) + { + case GFxValue::kType_Bool: + { + bool t = val->GetBool(); + return WriteData(intfc, &t); + } + case GFxValue::kType_Number: + { + double t = val->GetNumber(); + return WriteData(intfc, &t); + } + case GFxValue::kType_String: + { + const char* t = val->GetString(); + return WriteData(intfc, &t); + } + default: + // Unsupported + return false; + } + + return false; + } + + template <> + bool ReadData(const F4SESerializationInterface* intfc, GFxValue* val) + { + UInt32 type; + if (! ReadData(intfc, &type)) + return false; + + switch (type) + { + case GFxValue::kType_Bool: + { + bool t; + if (! ReadData(intfc, &t)) + return false; + val->SetBool(t); + return true; + } + case GFxValue::kType_Number: + { + double t; + if (! ReadData(intfc, &t)) + return false; + val->SetNumber(t); + return true; + } + case GFxValue::kType_String: + { + // As usual, using string cache to manage strings + BSFixedString t; + if (! ReadData(intfc, &t)) + return false; + val->SetString(t.c_str()); + return true; + } + default: + // Unsupported + return false; + } + + return false; + } +} diff --git a/f4se/f4se/ScaleformSerialization.h b/f4se/f4se/ScaleformSerialization.h new file mode 100644 index 0000000..4b33140 --- /dev/null +++ b/f4se/f4se/ScaleformSerialization.h @@ -0,0 +1,15 @@ +#pragma once + +#include "f4se/Serialization.h" +#include "f4se/PluginAPI.h" + +class GFxValue; + +namespace Serialization +{ + template <> + bool WriteData(const F4SESerializationInterface* intfc, const GFxValue* val); + + template <> + bool ReadData(const F4SESerializationInterface* intfc, GFxValue* val); +}; diff --git a/f4se/f4se/ScaleformState.cpp b/f4se/f4se/ScaleformState.cpp new file mode 100644 index 0000000..45cd53b --- /dev/null +++ b/f4se/f4se/ScaleformState.cpp @@ -0,0 +1,6 @@ +#include "f4se/ScaleformState.h" + +void F4SEGFxLogger::LogMessageVarg(UInt32 messageType, const char* fmt, va_list args) +{ + gLog.LogNNL(IDebugLog::kLevel_Message, fmt, args); +} diff --git a/f4se/f4se/ScaleformState.h b/f4se/f4se/ScaleformState.h new file mode 100644 index 0000000..9be1308 --- /dev/null +++ b/f4se/f4se/ScaleformState.h @@ -0,0 +1,127 @@ +#pragma once + +#include "f4se/ScaleformAPI.h" +#include "f4se/ScaleformTypes.h" + +// 18 +class GFxState : public GRefCountBase +{ +public: + enum + { + kInterface_None = 0x00, + kInterface_Translator = 0x01, + kInterface_Log = 0x02, + kInterface_FileOpener = 0x0C, + kInterface_ImageLoader = 0x0E, + kInterface_ZlibSupport = 0x1D, + }; + + UInt64 interfaceType; // 10 + + // redirect new/delete to the scaleform heap + static void * operator new(std::size_t size) + { + return ScaleformHeap_Allocate(size); + } + + static void * operator new(std::size_t size, const std::nothrow_t &) + { + return ScaleformHeap_Allocate(size); + } + + // placement new + static void * operator new(std::size_t size, void * ptr) + { + return ptr; + } + + static void operator delete(void * ptr) + { + ScaleformHeap_Free(ptr); + } + + // placement delete + static void operator delete(void *, void *) + { + // + } + + void AddRef(void) + { + InterlockedIncrement(&refCount); + } + + void Release(void) + { + if(InterlockedDecrement(&refCount) == 0) + { + delete this; + } + } +}; + +class GFxStateBag +{ +public: + virtual ~GFxStateBag(); + + virtual GFxStateBag * GetStateBagImpl(void); + virtual void SetState(UInt32 type, void * ptr); + virtual void * GetStateAddRef(UInt32 type); + virtual void Unk_04(void); + virtual void Unk_05(void); +}; + +class GFxLogBase +{ +public: + virtual ~GFxLogBase () { } + + virtual bool IsVerboseActionErrors() const { return true; } +}; + +class GFxLogState : public GFxState +{ +public: + GFxLogState() { } + virtual ~GFxLogState () { } + + virtual void Unk_01(void); + + GFxLogBase * logBase; // 18 + void * logger; // 20 +}; + +class F4SEGFxLogger : public GRefCountBase +{ +public: + F4SEGFxLogger() { } + virtual ~F4SEGFxLogger () { } + + enum + { + Log_Channel_General = 0x10, + Log_Channel_Script = 0x20, + Log_Channel_Parse = 0x30, + Log_Channel_Action = 0x40, + Log_Channel_Debug = 0x50, + Log_Channel_Mask = 0xF0, + Log_MessageType_Error = 0, + Log_MessageType_Warning = 1, + Log_MessageType_Message = 2, + Log_Error = Log_Channel_General | Log_MessageType_Error, + Log_Warning = Log_Channel_General | Log_MessageType_Warning, + Log_Message = Log_Channel_General | Log_MessageType_Message, + Log_ScriptError = Log_Channel_Script | Log_MessageType_Error, + Log_ScriptWarning = Log_Channel_Script | Log_MessageType_Warning, + Log_ScriptMessage = Log_Channel_Script | Log_MessageType_Message, + Log_Parse = Log_Channel_Parse | 0, + Log_ParseShape = Log_Channel_Parse | 1, + Log_ParseMorphShape = Log_Channel_Parse | 2, + Log_ParseAction = Log_Channel_Parse | 3, + Log_Action = Log_Channel_Action | 0 + }; + + virtual void LogMessageVarg(UInt32 messageType, const char* fmt, va_list args); +}; diff --git a/f4se/f4se/ScaleformTranslator.cpp b/f4se/f4se/ScaleformTranslator.cpp new file mode 100644 index 0000000..6bfeb25 --- /dev/null +++ b/f4se/f4se/ScaleformTranslator.cpp @@ -0,0 +1,6 @@ +#include "f4se/ScaleformTranslator.h" + +// E33FAE94A9BB57E033DF51B26B8EC718FD4FAA35+3B +RelocAddr <_CreateEmptyString> CreateEmptyString(0x01B42AC0); +// E33FAE94A9BB57E033DF51B26B8EC718FD4FAA35+73 +RelocAddr <_SetWideString> SetWideString(0x01B42C10); diff --git a/f4se/f4se/ScaleformTranslator.h b/f4se/f4se/ScaleformTranslator.h new file mode 100644 index 0000000..17b8b21 --- /dev/null +++ b/f4se/f4se/ScaleformTranslator.h @@ -0,0 +1,59 @@ +#pragma once + +#include "f4se/ScaleformState.h" +#include "f4se/GameTypes.h" +#include "f4se/GameUtilities.h" + +class GFxTranslator : public GFxState +{ +public: + virtual ~GFxTranslator(); + + virtual void Unk_01(void); + virtual void Unk_02(void); + virtual void Unk_03(void); + virtual void Unk_04(void); +}; + +// 10 +class TranslationTableItem +{ +public: + BSFixedString key; + BSFixedStringW translation; + + TranslationTableItem(BSFixedString a_key, BSFixedStringW a_translation) + : key(a_key), translation(a_translation) {} + + bool operator==(const BSFixedString & a_name) const { return key == a_name; } + operator BSFixedString() const { return key; } + + static inline UInt32 GetHash(BSFixedString * key) + { + UInt32 hash; + CalculateCRC32_64(&hash, (UInt64)key->data, 0); + return hash; + } + + void Dump(void) + { + _MESSAGE("\t\tkey: %S ", key.data ? key.data->Get() : ""); + _MESSAGE("\t\ttranslation: %S", translation.data ? translation.data->Get() : L""); + } +}; + +typedef tHashSet TranslationTable; + +// 50 +class BSScaleformTranslator : public GFxTranslator +{ +public: + UInt64 unk18[(0x20 - 0x18) / 8]; // 18 + TranslationTable translations; // 20 +}; + +typedef void (__cdecl * _CreateEmptyString)(BSFixedString * pOut); +extern RelocAddr<_CreateEmptyString> CreateEmptyString; + +typedef void (__cdecl * _SetWideString)(BSFixedString * pOut, wchar_t * bufIn); +extern RelocAddr<_SetWideString> SetWideString; diff --git a/f4se/f4se/ScaleformTypes.cpp b/f4se/f4se/ScaleformTypes.cpp new file mode 100644 index 0000000..e1f80bb --- /dev/null +++ b/f4se/f4se/ScaleformTypes.cpp @@ -0,0 +1 @@ +#include "f4se/ScaleformTypes.h" diff --git a/f4se/f4se/ScaleformTypes.h b/f4se/f4se/ScaleformTypes.h new file mode 100644 index 0000000..88483e4 --- /dev/null +++ b/f4se/f4se/ScaleformTypes.h @@ -0,0 +1,56 @@ +#pragma once + +// 10 +class GRefCountImplCore +{ +public: + GRefCountImplCore() :refCount(1) { } + virtual ~GRefCountImplCore() { } + +// void ** _vtbl; // 00 + volatile SInt32 refCount; // 08 + UInt32 pad0C; // 0C +}; + +class GRefCountImpl : public GRefCountImplCore +{ +public: + GRefCountImpl() { } + virtual ~GRefCountImpl() { } +}; + +class GRefCountBaseStatImpl : public GRefCountImpl +{ +public: + GRefCountBaseStatImpl() { } + virtual ~GRefCountBaseStatImpl() { } +}; + +class GRefCountBase : public GRefCountBaseStatImpl +{ +public: + GRefCountBase() { } + virtual ~GRefCountBase() { } +}; + +template +class GRect +{ +public: + T left; + T top; + T right; + T bottom; +}; + +class GMatrix3F +{ +public: + float m[3][4]; +}; + +class GMatrix4F +{ +public: + float m[4][4]; +}; diff --git a/f4se/f4se/ScaleformValue.cpp b/f4se/f4se/ScaleformValue.cpp new file mode 100644 index 0000000..4beea1b --- /dev/null +++ b/f4se/f4se/ScaleformValue.cpp @@ -0,0 +1,270 @@ +#include "f4se/ScaleformValue.h" +#include "f4se/GameEvents.h" + +RelocAddr <_GetFilterColorByType> GetFilterColorByType(0x020F2C90); +RelocAddr <_ApplyColorFilter> ApplyColorFilter(0x020F2990); +RelocAddr <_SetDefaultColors> SetDefaultColors(0x020F2BE0); + +RelocAddr <_GetExtDisplayInfo> GetExtDisplayInfo(0x0210DBE0); +RelocAddr <_SetExtDisplayInfoAlpha> SetExtDisplayInfoAlpha(0x0210DEF0); +RelocAddr <_SetExtDisplayInfo> SetExtDisplayInfo(0x0210DD70); + +RelocAddr <_PlayUISound> PlayUISound(0x012BE320); +RelocAddr <_CreateBaseShaderTarget> CreateBaseShaderTarget(0x00B06DA0); + +GFxValue::~GFxValue() +{ + CleanManaged(); +} + +void GFxValue::AddManaged(void) +{ + if(IsManaged()) + CALL_MEMBER_FN(objectInterface, AddManaged_Internal)(this, data.obj); +} + +void GFxValue::CleanManaged(void) +{ + if(IsManaged()) + { + CALL_MEMBER_FN(objectInterface, ReleaseManaged_Internal)(this, data.obj); + + objectInterface = NULL; + type = kType_Undefined; + } +} + +void GFxValue::SetUndefined(void) +{ + CleanManaged(); + + type = kType_Undefined; +} + +void GFxValue::SetNull(void) +{ + CleanManaged(); + + type = kType_Null; +} + +void GFxValue::SetBool(bool value) +{ + CleanManaged(); + + type = kType_Bool; + data.boolean = value; +} + +void GFxValue::SetNumber(double value) +{ + CleanManaged(); + + type = kType_Number; + data.number = value; +} + +void GFxValue::SetInt(SInt32 value) +{ + CleanManaged(); + + type = kType_Int; + data.s32 = value; +} +void GFxValue::SetUInt(UInt32 value) +{ + CleanManaged(); + + type = kType_UInt; + data.u32 = value; +} + +void GFxValue::SetString(const char * value) +{ + CleanManaged(); + + type = kType_String; + data.string = value; +} + +bool GFxValue::GetBool(void) const +{ + switch(GetType()) + { + case kType_Bool: return data.boolean; + case kType_Number: return data.number != 0; + case kType_Int: return data.s32 != 0; + case kType_UInt: return data.u32 != 0; + default: HALT("GFxValue::GetBool: bad type"); return false; + } +} + +const char * GFxValue::GetString(void) const +{ + if(GetType() != kType_String) + return NULL; + + if(IsManaged()) + return *data.managedString; + else + return data.string; +} + +double GFxValue::GetNumber(void) const +{ + switch(GetType()) + { + case kType_Int: return (double)data.s32; + case kType_UInt: return (double)data.u32; + case kType_Number: return data.number; + case kType_Bool: return data.boolean ? 1 : 0; + default: HALT("GFxValue::GetNumber: bad type"); return 0; + } +} + +SInt32 GFxValue::GetInt(void) const +{ + switch(GetType()) + { + case kType_Int: return data.s32; + case kType_UInt: return (SInt32)data.u32; + case kType_Number: return (SInt32)data.number; + case kType_Bool: return data.boolean ? 1 : 0; + default: HALT("GFxValue::GetInt: bad type"); return 0; + } +} + +UInt32 GFxValue::GetUInt(void) const +{ + switch(GetType()) + { + case kType_Int: return (UInt32)data.s32; + case kType_UInt: return data.u32; + case kType_Number: return (UInt32)data.number; + case kType_Bool: return data.boolean ? 1 : 0; + default: HALT("GFxValue::GetUInt: bad type"); return 0; + } +} + +bool GFxValue::HasMember(const char * name) +{ + return CALL_MEMBER_FN(objectInterface, HasMember)(data.obj, name); +} + +bool GFxValue::SetMember(const char * name, GFxValue * value) +{ + return CALL_MEMBER_FN(objectInterface, SetMember)(data.obj, name, value, IsDisplayObject()); +} + +bool GFxValue::GetMember(const char * name, GFxValue * value) +{ + return CALL_MEMBER_FN(objectInterface, GetMember)(data.obj, name, value, IsDisplayObject()); +} + +bool GFxValue::Invoke(const char * name, GFxValue * result, GFxValue * args, UInt32 numArgs) +{ + return CALL_MEMBER_FN(objectInterface, Invoke)(data.obj, result, name, args, numArgs, IsDisplayObject()); +} + +bool GFxValue::CreateEmptyMovieClip(GFxValue* pValue, const char* instanceName, SInt32 depth) +{ + return CALL_MEMBER_FN(objectInterface, CreateEmptyMovieClip)(data.obj, pValue, instanceName, depth); +} + +bool GFxValue::AttachMovie(GFxValue* pValue, const char* symbolName, const char* instanceName, SInt32 depth, const void * initArgs) +{ + return CALL_MEMBER_FN(objectInterface, AttachMovie)(data.obj, pValue, symbolName, instanceName, depth, initArgs); +} + +UInt32 GFxValue::GetArraySize() +{ + return CALL_MEMBER_FN(objectInterface, GetArraySize)(data.obj); +} + +bool GFxValue::SetArraySize(UInt32 size) +{ + return CALL_MEMBER_FN(objectInterface, SetArraySize)(data.obj, size); +} + +bool GFxValue::GetElement(UInt32 index, GFxValue * value) +{ + return CALL_MEMBER_FN(objectInterface, GetElement)(data.obj, index, value); +} + +bool GFxValue::PushBack(GFxValue * value) +{ + return CALL_MEMBER_FN(objectInterface, PushBack)(data.obj, value); +} + +bool GFxValue::PopBack(GFxValue * value) +{ + return CALL_MEMBER_FN(objectInterface, PopBack)(data.obj, value); +} + +void GFxValue::VisitElements(ObjectInterface::ArrayVisitor * visitor, UInt32 idx, SInt32 count) +{ + CALL_MEMBER_FN(objectInterface, VisitElements)(data.obj, visitor, idx, count); +} + +bool GFxValue::GetText(GFxValue * value, bool html) +{ + return CALL_MEMBER_FN(objectInterface, GetText)(data.obj, value, html); +} + +bool GFxValue::SetText(const char * text, bool html) +{ + return CALL_MEMBER_FN(objectInterface, SetText)(data.obj, text, html); +} + +void GFxValue::VisitMembers(ObjectInterface::ObjVisitor * visitor) +{ + CALL_MEMBER_FN(objectInterface, VisitMembers)(data.obj, visitor, IsDisplayObject()); +} + +bool GFxValue::GetDisplayInfo(DisplayInfo * displayInfo) +{ + return CALL_MEMBER_FN(objectInterface, GetDisplayInfo)(data.obj, displayInfo); +} + +bool GFxValue::SetDisplayInfo(DisplayInfo * displayInfo) +{ + return CALL_MEMBER_FN(objectInterface, SetDisplayInfo)(data.obj, displayInfo); +} + +bool GFxValue::GotoLabeledFrame(const char * frameLabel, bool stop) +{ + return CALL_MEMBER_FN(objectInterface, GotoLabeledFrame)(data.obj, frameLabel, stop); +} + +void BSGFxShaderFXTarget::SetFilterColor(bool isHostile) +{ + UInt32 type = kColorNormal; + FilterColor color; + if(isHostile) + type = kColorWarning; + colorType = type; + + GetFilterColorByType(this, &color); + ApplyColorFilter(this, &color, 1.0f); +} + +EventResult BSGFxShaderFXTarget::ReceiveEvent(ApplyColorUpdateEvent * evn, void * dispatcher) +{ + FilterColor color; + if(((colorFlags >> 1) & 1) && colorType != kColorUnk7) + { + FilterColor * filtered = GetFilterColorByType(this, &color); + ApplyColorFilter(this, filtered, blue); + } + if((colorFlags & 1) && unkAC != 4) + { + SetDefaultColors(this); + } + return kEvent_Continue; +}; + +BSGFxShaderFXTarget::~BSGFxShaderFXTarget() +{ + if((*g_colorUpdateDispatcher)) + (*g_colorUpdateDispatcher)->eventDispatcher.RemoveEventSink(this); +} diff --git a/f4se/f4se/ScaleformValue.h b/f4se/f4se/ScaleformValue.h new file mode 100644 index 0000000..23dad96 --- /dev/null +++ b/f4se/f4se/ScaleformValue.h @@ -0,0 +1,381 @@ +#pragma once + +#include "f4se_common/Utilities.h" +#include "f4se/GameEvents.h" +#include "f4se/ScaleformTypes.h" + +class GFxMovieView; +class GFxMovieRoot; + +// 20 +class GFxValue +{ +public: + GFxValue() : objectInterface(NULL), type(kType_Undefined), unk18(nullptr) { data.obj = nullptr; } + GFxValue(int v) : objectInterface(NULL), type(kType_Int), unk18(nullptr) { data.number = v; } + GFxValue(UInt32 v) : objectInterface(NULL), type(kType_UInt), unk18(nullptr) { data.u32 = v; } + GFxValue(SInt32 v) : objectInterface(NULL), type(kType_Int), unk18(nullptr) { data.s32 = v; } + GFxValue(double v) : objectInterface(NULL), type(kType_Number), unk18(nullptr) { data.number = v; } + GFxValue(bool v) : objectInterface(NULL), type(kType_Bool), unk18(nullptr) { data.boolean = v; } + GFxValue(const char* ps) : objectInterface(NULL), type(kType_String), unk18(nullptr) { data.string = ps; } + + GFxValue(GFxValue * value) : objectInterface(value->objectInterface), type(value->type), unk18(value->unk18) { data.obj = value->data.obj; } + ~GFxValue(); + + enum Type + { + kType_Undefined = 0, + kType_Null, + kType_Bool, + kType_Int, + kType_UInt, + kType_Number, + kType_String, + kType_Unknown7, + kType_Object, + kType_Array, + kType_DisplayObject, + kType_Function, + + kTypeFlag_Managed = 1 << 6, + + kMask_Type = 0x8F, // not sure why it checks the top bit + }; + + union Data + { + UInt32 u32; + SInt32 s32; + double number; + bool boolean; + const char * string; + const char ** managedString; + void * obj; + }; + + // D8 + class DisplayInfo + { + public: + DisplayInfo() : _varsSet(0), unkD0(0), unkD6(0) {} + enum + { + kChange_x = (1 << 0), + kChange_y = (1 << 1), + kChange_rotation = (1 << 2), + kChange_xscale = (1 << 3), + kChange_yscale = (1 << 4), + kChange_alpha = (1 << 5), + kChange_visible = (1 << 6), + kChange_z = (1 << 7), + kChange_xrotation = (1 << 8), + kChange_yrotation = (1 << 9), + kChange_zscale = (1 << 10), + kChange_FOV = (1 << 11), + kChange_projMatrix3D = (1 << 12), + kChange_viewMatrix3D = (1 << 13) + }; + + double _x; // 00 + double _y; // 08 + double _rotation; // 10 + double _xScale; // 18 + double _yScale; // 20 + double _alpha; // 28 + bool _visible; // 30 + double _z; // 38 + double _xRotation; // 40 + double _yRotation; // 48 + double _zScale; // 50 + double _perspFOV; // 58 + GMatrix3F _viewMatrix3D; // 60 + GMatrix4F _perspectiveMatrix3D; // A0 + UInt32 unkD0; // D0 + UInt16 _varsSet; // D4 + UInt16 unkD6; // D6 + + void SetX(double x) { SetFlags(kChange_x); _x = x; } + void SetY(double y) { SetFlags(kChange_y); _y = y; } + void SetRotation(double degrees) { SetFlags(kChange_rotation); _rotation = degrees; } + void SetXScale(double xscale) { SetFlags(kChange_xscale); _xScale = xscale; } + void SetYScale(double yscale) { SetFlags(kChange_yscale); _yScale = yscale; } + void SetAlpha(double alpha) { SetFlags(kChange_alpha); _alpha = alpha; } + void SetVisible(bool visible) { SetFlags(kChange_visible); _visible = visible; } + void SetZ(double z) { SetFlags(kChange_z); _z = z; } + void SetXRotation(double degrees) { SetFlags(kChange_xrotation); _xRotation = degrees; } + void SetYRotation(double degrees) { SetFlags(kChange_yrotation); _yRotation = degrees; } + void SetZScale(double zscale) { SetFlags(kChange_zscale); _zScale = zscale; } + void SetFOV(double fov) { SetFlags(kChange_FOV); _perspFOV = fov; } + void SetProjectionMatrix3D(const GMatrix4F *pmat) + { + if (pmat) { + SetFlags(kChange_projMatrix3D); + _perspectiveMatrix3D = *pmat; + } else + ClearFlags(kChange_projMatrix3D); + } + void SetViewMatrix3D(const GMatrix3F *pmat) + { + if (pmat) { + SetFlags(kChange_viewMatrix3D); + _viewMatrix3D = *pmat; + } else + ClearFlags(kChange_viewMatrix3D); + } + + // Convenience functions + void SetPosition(double x, double y) { SetFlags(kChange_x | kChange_y); _x = x; _y = y; } + void SetScale(double xscale, double yscale) { SetFlags(kChange_xscale | kChange_yscale); _xScale = xscale; _yScale = yscale; } + + void SetFlags(UInt32 flags) { _varsSet |= flags; } + void ClearFlags(UInt32 flags) { _varsSet &= ~flags; } + }; + + class ObjectInterface + { + public: + class ArrayVisitor + { + public: + virtual ~ArrayVisitor() {} + virtual void Visit(UInt32 idx, GFxValue * val) = 0; + }; + class ObjVisitor + { + public: + virtual ~ObjVisitor() { } + virtual bool ShowDisplayMembers(void) { return false; } + virtual void Visit(const char * member, GFxValue * value) = 0; + }; + + void * unk00; + GFxMovieView * view; + + MEMBER_FN_PREFIX(ObjectInterface); + DEFINE_MEMBER_FN(HasMember, bool, 0x020AFCB0, void* pData, const char* name); + DEFINE_MEMBER_FN(GetMember, bool, 0x020A8060, void* pData, const char* name, GFxValue* pValue, bool isDisplayObj); + DEFINE_MEMBER_FN(SetMember, bool, 0x020D05E0, void* pData, const char* name, const GFxValue* pValue, bool isDisplayObj); + DEFINE_MEMBER_FN(Invoke, bool, 0x020B1D20, void * pData, GFxValue * result, const char * name, GFxValue * args, UInt64 numArgs, UInt8 isDisplayObj); + DEFINE_MEMBER_FN(CreateEmptyMovieClip, bool, 0x0208A0C0, void* pData, GFxValue* pValue, const char* instanceName, SInt32 depth); + DEFINE_MEMBER_FN(AttachMovie, bool, 0x02081630, void* pData, GFxValue* pValue, const char* symbolName, const char* instanceName, SInt32 depth, const void * initArgs); + DEFINE_MEMBER_FN(GetArraySize, UInt32, 0x020A1E60, void * pData); + // ref CC19A4FFD76032A42FBBC61E80011469E50993D7 (+4) + DEFINE_MEMBER_FN(SetArraySize, bool, 0x0219C890, void * pData, UInt32 size); + DEFINE_MEMBER_FN(GetElement, bool, 0x020A5B50, void * pData, UInt32 index, GFxValue * value); + DEFINE_MEMBER_FN(PushBack, bool, 0x020C2970, void * pData, GFxValue * value); + DEFINE_MEMBER_FN(PopBack, bool, 0x020BE890, void * pData, GFxValue * value); + DEFINE_MEMBER_FN(VisitElements, void, 0x020DB0E0, void * pData, ArrayVisitor * visitor, UInt32 idx, SInt32 count); + DEFINE_MEMBER_FN(GotoLabeledFrame, bool, 0x020AF6F0, void * pData, const char * frameLabel, bool stop); + // ref 1A7DD5D4A014A3E7CBF9A53D55DA751C11218613 (+1E7) + DEFINE_MEMBER_FN(VisitMembers, void, 0x020DB210, void * pData, ObjVisitor * visitor, bool isDisplayObj); + DEFINE_MEMBER_FN(GetText, bool, 0x020AD8D0, void * pData, GFxValue * value, bool html); + DEFINE_MEMBER_FN(SetText, bool, 0x020D36C0, void * pData, const char * text, bool html); + DEFINE_MEMBER_FN(GetDisplayInfo, bool, 0x020A5240, void * pData, DisplayInfo * displayInfo); + DEFINE_MEMBER_FN(SetDisplayInfo, bool, 0x020CEEF0, void * pData, DisplayInfo * displayInfo); + DEFINE_MEMBER_FN(AddManaged_Internal, void, 0x020B9C30, GFxValue * value, void * pData); + DEFINE_MEMBER_FN(ReleaseManaged_Internal, void, 0x020B9C80, GFxValue * value, void * pData); + }; + + ObjectInterface * objectInterface; // 00 + Type type; // 08 + Data data; // 10 + void * unk18; // 18 + + UInt32 GetType(void) const { return type & kMask_Type; } + bool IsManaged(void) const { return (type & kTypeFlag_Managed) != 0; } + void CleanManaged(void); + void AddManaged(void); + + bool IsUndefined() const { return GetType() == kType_Undefined; } + bool IsNull() const { return GetType() == kType_Null; } + bool IsBool() const { return GetType() == kType_Bool; } + bool IsNumber() const { return GetType() == kType_Number; } + bool IsString() const { return GetType() == kType_String; } + bool IsObject() const { return (GetType() == kType_Object) || GetType() == kType_Array || GetType() == kType_DisplayObject; } + bool IsArray() const { return GetType() == kType_Array; } + bool IsDisplayObject() const { return GetType() == kType_DisplayObject; } + bool IsFunction() const { return GetType() == kType_Function; } + + void SetUndefined(void); + void SetNull(void); + void SetBool(bool value); + void SetInt(SInt32 value); + void SetUInt(UInt32 value); + void SetNumber(double value); + void SetString(const char * value); + + bool GetBool(void) const; + const char * GetString(void) const; + double GetNumber(void) const; + SInt32 GetInt(void) const; + UInt32 GetUInt(void) const; + + bool HasMember(const char * name); + bool SetMember(const char * name, GFxValue * value); + bool GetMember(const char * name, GFxValue * value); + bool Invoke(const char * name, GFxValue * result, GFxValue * args, UInt32 numArgs); + + bool CreateEmptyMovieClip(GFxValue* pValue, const char* instanceName, SInt32 depth); + bool AttachMovie(GFxValue* pValue, const char* symbolName, const char* instanceName, SInt32 depth, const void * initArgs); + bool GotoLabeledFrame(const char * frameLabel, bool stop); + + UInt32 GetArraySize(); + bool SetArraySize(UInt32 size); + bool GetElement(UInt32 index, GFxValue * value); + bool PushBack(GFxValue * value); + bool PopBack(GFxValue * value); + void VisitElements(ObjectInterface::ArrayVisitor * visitor, UInt32 idx, SInt32 count); + void VisitMembers(ObjectInterface::ObjVisitor * visitor); + bool GetText(GFxValue * value, bool html); + bool SetText(const char * text, bool html); + bool GetDisplayInfo(DisplayInfo * displayInfo); + bool SetDisplayInfo(DisplayInfo * displayInfo); + + MEMBER_FN_PREFIX(GFxValue); + DEFINE_MEMBER_FN(RemoveChild_Internal, void, 0x0210D340, GFxValue * name); +}; + +// 38 +class BSGFxObject : public GFxValue +{ +public: + BSGFxObject() : unk20(0), unk28(0), unk30(0) { } + BSGFxObject(GFxValue * value) : GFxValue(value), unk20(0), unk28(0), unk30(0) + { + if(value->IsManaged()) + value->AddManaged(); + } + + UInt64 unk20; // 20 + UInt64 unk28; // 28 + UInt64 unk30; // 30 +}; + +// 50 +class BSGFxDisplayObject : public BSGFxObject +{ +public: + BSGFxDisplayObject() : parent(nullptr), unk48(0), unk4C(0) { } + BSGFxDisplayObject(GFxValue * value) : BSGFxObject(value), parent(nullptr) + { + GFxValue width, height; + GetMember("width", &width); + GetMember("height", &height); + unk48 = width.GetNumber(); + unk4C = height.GetNumber(); + } + virtual ~BSGFxDisplayObject() + { + if(parent) + CALL_MEMBER_FN(parent, RemoveChild_Internal)(this); + }; + + struct BSDisplayInfo + { + BSGFxDisplayObject * displayObject; // 00 + BSGFxDisplayObject * unk08; // 08 + GFxValue::DisplayInfo displayInfo1; // 10 + UInt64 unkE8; // E8 + GFxValue::DisplayInfo displayInfo2; // F0 + }; + + GFxValue * parent; // 40 + float unk48; // 48 + float unk4C; // 4C +}; +STATIC_ASSERT(sizeof(BSGFxDisplayObject) == 0x50); +STATIC_ASSERT(offsetof(BSGFxDisplayObject::BSDisplayInfo, displayInfo2) == 0xF0); + +class BSGFxShaderFXTarget; + +struct FilterColor +{ + float r, g, b; +}; + +// B0 +class BSGFxShaderFXTarget : public BSGFxDisplayObject, + public BSTEventSink +{ +public: + BSGFxShaderFXTarget() { } + BSGFxShaderFXTarget(GFxValue * source) : BSGFxDisplayObject(source), + unk58(0), unk60(0), unk68(0), unk6C(0), unk70(0), unk74(0), unk78(0), unk7C(0), red(0), + green(0), blue(0), multiplier(0), unk94(0), unk98(0), unkA0(0), colorType(0), unkAC(0) { }//{ CALL_MEMBER_FN(this, Impl_ctor)(source); } + virtual ~BSGFxShaderFXTarget();// { CALL_MEMBER_FN(this, Impl_dtor)(); }; + + virtual void Unk_01(void * unk1, void * unk2) + { + Impl_Fn1(unk1, unk2); + }; + + virtual EventResult ReceiveEvent(ApplyColorUpdateEvent * evn, void * dispatcher); + + enum ColorTypes + { + kColorUnk1 = 0, + kColorUnk2, + kColorNormal, + kColorUnk3, + kColorUnk4, + kColorWarning, + kColorUnk6, + kColorUnk7 + }; + + UInt64 unk58; // 58 + UInt64 unk60; // 60 + float unk68; // 68 + float unk6C; // 6C + float unk70; // 70 + float unk74; // 74 + float unk78; // 78 + float unk7C; // 7C + float red; // 80 + float green; // 84 + float blue; // 88 + UInt32 colorFlags; // 8C + float multiplier; // 90 + UInt32 unk94; // 94 + UInt64 unk98; // 98 + UInt64 unkA0; // A0 + UInt32 colorType; // A8 + UInt32 unkAC; // AC + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) + + void SetFilterColor(bool isHostile); + + // 98B654B565F35633CBE8804A5CBF84646AE30A1B+9 + DEFINE_MEMBER_FN_1(Impl_ctor, BSGFxShaderFXTarget *, 0x020F1770, GFxValue * source); + DEFINE_MEMBER_FN_0(Impl_dtor, void, 0x020F16D0); + DEFINE_MEMBER_FN_2(Impl_Fn1, void, 0x020F1BF0, void * unk1, void * unk2); +}; +STATIC_ASSERT(offsetof(BSGFxShaderFXTarget, red) == 0x80); +STATIC_ASSERT(sizeof(BSGFxShaderFXTarget) == 0xB0); + +// This function acquires the HUD color by type e.g. normal, PA, hostile +typedef FilterColor * (* _GetFilterColorByType)(BSGFxShaderFXTarget * component, FilterColor * color); +extern RelocAddr <_GetFilterColorByType> GetFilterColorByType; + +// Sets explicit component filter color +typedef void (* _ApplyColorFilter)(BSGFxShaderFXTarget * component, FilterColor * color, float unk1); +extern RelocAddr <_ApplyColorFilter> ApplyColorFilter; + +typedef void (* _SetDefaultColors)(BSGFxShaderFXTarget * component); +extern RelocAddr <_SetDefaultColors> SetDefaultColors; + +typedef void * (* _GetExtDisplayInfo)(BSGFxDisplayObject::BSDisplayInfo * dInfo, BSGFxDisplayObject * target); +extern RelocAddr <_GetExtDisplayInfo> GetExtDisplayInfo; + +typedef void (* _SetExtDisplayInfoAlpha)(void * dInfo, double alpha); +extern RelocAddr <_SetExtDisplayInfoAlpha> SetExtDisplayInfoAlpha; + +typedef void (* _SetExtDisplayInfo)(BSGFxDisplayObject::BSDisplayInfo * dInfo); +extern RelocAddr <_SetExtDisplayInfo> SetExtDisplayInfo; + +typedef void(* _PlayUISound)(const char *); +extern RelocAddr<_PlayUISound> PlayUISound; + +typedef void(*_CreateBaseShaderTarget)(BSGFxShaderFXTarget * & component, GFxValue & stage); +extern RelocAddr<_CreateBaseShaderTarget> CreateBaseShaderTarget; \ No newline at end of file diff --git a/f4se/f4se/Serialization.cpp b/f4se/f4se/Serialization.cpp new file mode 100644 index 0000000..a67e86c --- /dev/null +++ b/f4se/f4se/Serialization.cpp @@ -0,0 +1,634 @@ +#include "f4se/Serialization.h" +#include "common/IFileStream.h" +#include "f4se/PluginManager.h" +#include "f4se/GameAPI.h" +#include "f4se_common/f4se_version.h" +#include +#include +#include +#include "f4se/GameData.h" +#include "f4se/InternalSerialization.h" +#include "f4se/GameSettings.h" +#include "f4se/ScaleformCallbacks.h" +#include "f4se/ScaleformValue.h" + +namespace Serialization +{ + const char * kSavegamePath = "\\My Games\\Fallout4\\"; + + // file format internals + + // general format: + // Header header + // PluginHeader plugin[header.numPlugins] + // ChunkHeader chunk[plugin.numChunks] + // UInt8 data[chunk.length] + + struct Header + { + enum + { + kSignature = MACRO_SWAP32('F4SE'), // endian-swapping so the order matches + kVersion = 1, + + kVersion_Invalid = 0 + }; + + UInt32 signature; + UInt32 formatVersion; + UInt32 f4seVersion; + UInt32 runtimeVersion; + UInt32 numPlugins; + }; + + struct PluginHeader + { + UInt32 signature; + UInt32 numChunks; + UInt32 length; // length of following data including ChunkHeader + }; + + struct ChunkHeader + { + UInt32 type; + UInt32 version; + UInt32 length; + }; + + // locals + + std::string s_savePath; + IFileStream s_currentFile; + + typedef std::vector PluginCallbackList; + PluginCallbackList s_pluginCallbacks; + + PluginHandle s_currentPlugin = 0; + + Header s_fileHeader = { 0 }; + + UInt64 s_pluginHeaderOffset = 0; + PluginHeader s_pluginHeader = { 0 }; + + bool s_chunkOpen = false; + UInt64 s_chunkHeaderOffset = 0; + ChunkHeader s_chunkHeader = { 0 }; + + // utilities + + // make full path from save name + std::string MakeSavePath(std::string name, const char * extension) + { + char path[MAX_PATH]; + ASSERT(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))); + + std::string result = path; + result += kSavegamePath; + Setting* localSavePath = GetINISetting("sLocalSavePath:General"); + if(localSavePath && (localSavePath->GetType() == Setting::kType_String)) + result += localSavePath->data.s; + else + result += "Saves\\"; + + result += "\\"; + result += name; + if (extension) + result += extension; + return result; + } + + PluginCallbacks * GetPluginInfo(PluginHandle plugin) + { + if(plugin >= s_pluginCallbacks.size()) + s_pluginCallbacks.resize(plugin + 1); + + return &s_pluginCallbacks[plugin]; + } + + // plugin API + void SetUniqueID(PluginHandle plugin, UInt32 uid) + { + // check existing plugins + for(PluginCallbackList::iterator iter = s_pluginCallbacks.begin(); iter != s_pluginCallbacks.end(); ++iter) + { + if(iter->hadUID && (iter->uid == uid)) + { + UInt32 collidingID = iter - s_pluginCallbacks.begin(); + + _ERROR("plugin serialization UID collision (uid = %08X, plugins = %d %d)", plugin, uid, collidingID); + } + } + + PluginCallbacks * info = GetPluginInfo(plugin); + + ASSERT(!info->hadUID); + + info->uid = uid; + info->hadUID = true; + } + + void SetRevertCallback(PluginHandle plugin, F4SESerializationInterface::EventCallback callback) + { + GetPluginInfo(plugin)->revert = callback; + } + + void SetSaveCallback(PluginHandle plugin, F4SESerializationInterface::EventCallback callback) + { + GetPluginInfo(plugin)->save = callback; + } + + void SetLoadCallback(PluginHandle plugin, F4SESerializationInterface::EventCallback callback) + { + GetPluginInfo(plugin)->load = callback; + } + + void SetFormDeleteCallback(PluginHandle plugin, F4SESerializationInterface::FormDeleteCallback callback) + { + GetPluginInfo(plugin)->formDelete = callback; + } + + void SetSaveName(const char * name) + { + if(name) + { + _MESSAGE("save name is %s", name); + s_savePath = MakeSavePath(name, ".f4se"); + _MESSAGE("full save path: %s", s_savePath.c_str()); + } + else + { + _MESSAGE("cleared save path"); + s_savePath.clear(); + } + } + + bool WriteRecord(UInt32 type, UInt32 version, const void * buf, UInt32 length) + { + if(!OpenRecord(type, version)) + return false; + + return WriteRecordData(buf, length); + } + + // flush a chunk header to the file if one is currently open + static void FlushWriteChunk(void) + { + if(!s_chunkOpen) + return; + + UInt64 curOffset = s_currentFile.GetOffset(); + UInt64 chunkSize = curOffset - s_chunkHeaderOffset - sizeof(s_chunkHeader); + + ASSERT(chunkSize < 0x80000000); // stupidity check + + s_chunkHeader.length = (UInt32)chunkSize; + + s_currentFile.SetOffset(s_chunkHeaderOffset); + s_currentFile.WriteBuf(&s_chunkHeader, sizeof(s_chunkHeader)); + + s_currentFile.SetOffset(curOffset); + + s_pluginHeader.length += chunkSize + sizeof(s_chunkHeader); + + s_chunkOpen = false; + } + + bool OpenRecord(UInt32 type, UInt32 version) + { + if(!s_pluginHeader.numChunks) + { + ASSERT(!s_chunkOpen); + + s_pluginHeaderOffset = s_currentFile.GetOffset(); + s_currentFile.Skip(sizeof(s_pluginHeader)); + } + + FlushWriteChunk(); + + s_chunkHeaderOffset = s_currentFile.GetOffset(); + s_currentFile.Skip(sizeof(s_chunkHeader)); + + s_pluginHeader.numChunks++; + + s_chunkHeader.type = type; + s_chunkHeader.version = version; + s_chunkHeader.length = 0; + + s_chunkOpen = true; + + return true; + } + + bool WriteRecordData(const void * buf, UInt32 length) + { + s_currentFile.WriteBuf(buf, length); + + return true; + } + + static void FlushReadRecord(void) + { + if(s_chunkOpen) + { + if(s_chunkHeader.length) + { + // _WARNING("plugin didn't finish reading chunk"); + s_currentFile.Skip(s_chunkHeader.length); + } + + s_chunkOpen = false; + } + } + + bool GetNextRecordInfo(UInt32 * type, UInt32 * version, UInt32 * length) + { + FlushReadRecord(); + + if(!s_pluginHeader.numChunks) + return false; + + s_pluginHeader.numChunks--; + + s_currentFile.ReadBuf(&s_chunkHeader, sizeof(s_chunkHeader)); + + *type = s_chunkHeader.type; + *version = s_chunkHeader.version; + *length = s_chunkHeader.length; + + s_chunkOpen = true; + + return true; + } + + UInt32 ReadRecordData(void * buf, UInt32 length) + { + ASSERT(s_chunkOpen); + + if(length > s_chunkHeader.length) + length = s_chunkHeader.length; + + s_currentFile.ReadBuf(buf, length); + + s_chunkHeader.length -= length; + + return length; + } + + bool ResolveFormId(UInt32 formId, UInt32 * formIdOut) + { + UInt32 modID = formId >> 24; + + if (modID == 0xFF) + { + *formIdOut = formId; + return true; + } + + if (modID == 0xFE) + { + modID = formId >> 12; + } + + UInt32 loadedModID = ResolveModIndex(modID); + if (loadedModID < 0xFF) + { + *formIdOut = (formId & 0x00FFFFFF) | (((UInt32)loadedModID) << 24); + return true; + } + else if (loadedModID > 0xFF) + { + *formIdOut = (loadedModID << 12) | (formId & 0x00000FFF); + return true; + } + return false; + } + + bool ResolveHandle(UInt64 handle, UInt64 * handleOut) + { + UInt8 modID = handle >> 24; + + if (modID == 0xFF) + { + *handleOut = handle; + return true; + } + + if (modID == 0xFE) + { + modID = (handle >> 12) & 0xFFFFF; + } + + UInt64 loadedModID = (UInt64)ResolveModIndex(modID); + if (loadedModID < 0xFF) + { + *handleOut = (handle & 0xFFFFFFFF00FFFFFF) | (((UInt64)loadedModID) << 24); + return true; + } + else if (loadedModID > 0xFF) + { + *handleOut = (handle & 0xFFFFFFFF00000FFF) | (loadedModID << 12); + return true; + } + return false; + } + + // internal event handlers + void HandleRevertGlobalData(void) + { + for(UInt32 i = 0; i < s_pluginCallbacks.size(); i++) + if(s_pluginCallbacks[i].revert) + s_pluginCallbacks[i].revert(&g_F4SESerializationInterface); + } + + void HandleSaveGlobalData(void) + { + _MESSAGE("creating co-save"); + if(_access(s_savePath.c_str(), 0) == 0) + DeleteFile(s_savePath.c_str()); + if(!s_currentFile.Create(s_savePath.c_str())) + { + _ERROR("HandleSaveGlobalData: couldn't create save file (%s)", s_savePath.c_str()); + return; + } + + try + { + // init header + s_fileHeader.signature = Header::kSignature; + s_fileHeader.formatVersion = Header::kVersion; + s_fileHeader.f4seVersion = PACKED_F4SE_VERSION; + s_fileHeader.runtimeVersion = RUNTIME_VERSION; + s_fileHeader.numPlugins = 0; + + s_currentFile.Skip(sizeof(s_fileHeader)); + + // iterate through plugins + for(UInt32 i = 0; i < s_pluginCallbacks.size(); i++) + { + PluginCallbacks * info = &s_pluginCallbacks[i]; + + if(info->save && info->hadUID) + { + // set up header info + s_currentPlugin = i; + + s_pluginHeader.signature = info->uid; + s_pluginHeader.numChunks = 0; + s_pluginHeader.length = 0; + + s_chunkOpen = false; + + // call the plugin + try + { + info->save(&g_F4SESerializationInterface); + } + catch( ... ) + { + _ERROR("HandleSaveGlobalData: exception occurred saving %08X at %016I64X data may be corrupt.", s_pluginHeader.signature, s_currentFile.GetOffset()); + } + + // flush the remaining chunk data + FlushWriteChunk(); + + if(s_pluginHeader.numChunks) + { + UInt64 curOffset = s_currentFile.GetOffset(); + + s_currentFile.SetOffset(s_pluginHeaderOffset); + s_currentFile.WriteBuf(&s_pluginHeader, sizeof(s_pluginHeader)); + + s_currentFile.SetOffset(curOffset); + + s_fileHeader.numPlugins++; + } + } + } + + // write header + s_currentFile.SetOffset(0); + s_currentFile.WriteBuf(&s_fileHeader, sizeof(s_fileHeader)); + } + catch(...) + { + _ERROR("HandleSaveGame: exception during save"); + } + + s_currentFile.Close(); + } + + void HandleLoadGlobalData(void) + { + _MESSAGE("loading co-save"); + + if(!s_currentFile.Open(s_savePath.c_str())) + { + return; + } + + try + { + Header header; + + s_currentFile.ReadBuf(&header, sizeof(header)); + + if(header.signature != Header::kSignature) + { + _ERROR("HandleLoadGame: invalid file signature (found %08X expected %08X)", header.signature, Header::kSignature); + goto done; + } + + if(header.formatVersion <= Header::kVersion_Invalid) + { + _ERROR("HandleLoadGame: version invalid (%08X)", header.formatVersion); + goto done; + } + + if(header.formatVersion > Header::kVersion) + { + _ERROR("HandleLoadGame: version too new (found %08X current %08X)", header.formatVersion, Header::kVersion); + goto done; + } + + // reset flags + for(PluginCallbackList::iterator iter = s_pluginCallbacks.begin(); iter != s_pluginCallbacks.end(); ++iter) + iter->hadData = false; + + // iterate through plugin data chunks + while(s_currentFile.GetRemain() >= sizeof(PluginHeader)) + { + s_currentFile.ReadBuf(&s_pluginHeader, sizeof(s_pluginHeader)); + + UInt64 pluginChunkStart = s_currentFile.GetOffset(); + + UInt32 pluginIdx = kPluginHandle_Invalid; + + for(PluginCallbackList::iterator iter = s_pluginCallbacks.begin(); iter != s_pluginCallbacks.end(); ++iter) + if(iter->hadUID && (iter->uid == s_pluginHeader.signature)) + pluginIdx = iter - s_pluginCallbacks.begin(); + + try + { + if(pluginIdx != kPluginHandle_Invalid) + { + PluginCallbacks * info = &s_pluginCallbacks[pluginIdx]; + + info->hadData = true; + + if(info->load) + { + s_chunkOpen = false; + info->load(&g_F4SESerializationInterface); + } + } + else + { + _WARNING("HandleLoadGame: plugin with signature %08X not loaded", s_pluginHeader.signature); + } + } + catch( ... ) + { + _ERROR("HandleLoadGame: exception occurred loading %08X", s_pluginHeader.signature); + } + + // if plugin failed to read all its data or threw exception, jump to the next chunk + UInt64 expectedOffset = pluginChunkStart + s_pluginHeader.length; + if(s_currentFile.GetOffset() != expectedOffset) + { + _WARNING("HandleLoadGame: plugin did not read all of its data (at %016I64X expected %016I64X)", s_currentFile.GetOffset(), expectedOffset); + s_currentFile.SetOffset(expectedOffset); + } + } + + // call load on plugins that had no data + for(PluginCallbackList::iterator iter = s_pluginCallbacks.begin(); iter != s_pluginCallbacks.end(); ++iter) { + if(!iter->hadData && iter->load) { + iter->load(&g_F4SESerializationInterface); + } + } + } + catch(...) + { + _ERROR("HandleLoadGame: exception during load"); + + // ### this could be handled better, individually catch around each plugin so one plugin can't mess things up for everyone else + } + + done: + s_currentFile.Close(); + } + + void HandleDeleteSave(std::string saveName) + { + std::string savePath = MakeSavePath(saveName, NULL); + std::string coSavePath = savePath; + savePath += ".fos"; + coSavePath += ".f4se"; + + // Old save file really gone? + IFileStream saveFile; + if (!saveFile.Open(savePath.c_str())) + { + _MESSAGE("deleting co-save %s", coSavePath.c_str()); + DeleteFile(coSavePath.c_str()); + } + else + { + _MESSAGE("skipped delete of co-save %s", coSavePath.c_str()); + } + } + + void HandleDeletedForm(UInt64 handle) + { + for(UInt32 i = 0; i < s_pluginCallbacks.size(); i++) + if(s_pluginCallbacks[i].formDelete) + s_pluginCallbacks[i].formDelete(handle); + } + + template <> + bool WriteData(const F4SESerializationInterface * intfc, const BSFixedString * str) + { + return WriteData(intfc, str->c_str()); + } + + template <> + bool ReadData(const F4SESerializationInterface * intfc, BSFixedString * str) + { + UInt16 len = 0; + + if (! intfc->ReadRecordData(&len, sizeof(len))) + return false; + if(len == 0) + return true; + if (len > SHRT_MAX) + return false; + + char * buf = new char[len + 1]; + buf[0] = 0; + + if (! intfc->ReadRecordData(buf, len)) { + delete [] buf; + return false; + } + buf[len] = 0; + + *str = BSFixedString(buf); + delete [] buf; + return true; + } + + template <> + bool WriteData(const F4SESerializationInterface * intfc, const std::string * str) + { + UInt16 len = str->length(); + if (len > SHRT_MAX) + return false; + if (! intfc->WriteRecordData(&len, sizeof(len))) + return false; + if (len == 0) + return true; + if (! intfc->WriteRecordData(str->data(), len)) + return false; + return true; + } + + template <> + bool ReadData(const F4SESerializationInterface * intfc, std::string * str) + { + UInt16 len = 0; + if (! intfc->ReadRecordData(&len, sizeof(len))) + return false; + if (len == 0) + return true; + if (len > SHRT_MAX) + return false; + + char * buf = new char[len + 1]; + buf[0] = 0; + + if (! intfc->ReadRecordData(buf, len)) { + delete [] buf; + return false; + } + buf[len] = 0; + + *str = std::string(buf); + delete [] buf; + return true; + } + + template <> + bool WriteData(const F4SESerializationInterface * intfc, const char* str) + { + UInt16 len = strlen(str); + if (len > SHRT_MAX) + return false; + if (! intfc->WriteRecordData(&len, sizeof(len))) + return false; + if (len == 0) + return true; + if (! intfc->WriteRecordData(str, len)) + return false; + return true; + } +} diff --git a/f4se/f4se/Serialization.h b/f4se/f4se/Serialization.h new file mode 100644 index 0000000..22f0a82 --- /dev/null +++ b/f4se/f4se/Serialization.h @@ -0,0 +1,90 @@ +#pragma once + +#include "f4se/PluginAPI.h" +#include "f4se/GameTypes.h" + +#include + +class GFxValue; + +namespace Serialization +{ + struct PluginCallbacks + { + PluginCallbacks() + :revert(NULL) + ,save(NULL) + ,load(NULL) + ,formDelete(NULL) + ,uid(0) + ,hadData(false) + ,hadUID(false) { } + + F4SESerializationInterface::EventCallback revert; + F4SESerializationInterface::EventCallback save; + F4SESerializationInterface::EventCallback load; + F4SESerializationInterface::FormDeleteCallback formDelete; + + UInt32 uid; + + bool hadData; + bool hadUID; + }; + + // plugin API + void SetUniqueID(PluginHandle plugin, UInt32 uid); + void SetRevertCallback(PluginHandle plugin, F4SESerializationInterface::EventCallback callback); + void SetSaveCallback(PluginHandle plugin, F4SESerializationInterface::EventCallback callback); + void SetLoadCallback(PluginHandle plugin, F4SESerializationInterface::EventCallback callback); + void SetFormDeleteCallback(PluginHandle plugin, F4SESerializationInterface::FormDeleteCallback callback); + + void SetSaveName(const char * name); + bool WriteRecord(UInt32 type, UInt32 version, const void * buf, UInt32 length); + bool OpenRecord(UInt32 type, UInt32 version); + bool WriteRecordData(const void * buf, UInt32 length); + + bool GetNextRecordInfo(UInt32 * type, UInt32 * version, UInt32 * length); + UInt32 ReadRecordData(void * buf, UInt32 length); + + bool ResolveFormId(UInt32 formId, UInt32 * formIdOut); + bool ResolveHandle(UInt64 handle, UInt64 * handleOut); + + // internal event handlers + void HandleRevertGlobalData(void); + void HandleSaveGlobalData(void); + void HandleLoadGlobalData(void); + + void HandleDeleteSave(std::string saveName); + void HandleDeletedForm(UInt64 handle); + + // template helper functions + template + bool WriteData(const F4SESerializationInterface * intfc, const T * data) + { + return intfc->WriteRecordData(data, sizeof(T)); + } + + template + bool ReadData(const F4SESerializationInterface * intfc, T * data) + { + return intfc->ReadRecordData(data, sizeof(T)) > 0; + } + + template <> bool WriteData(const F4SESerializationInterface * intfc, const BSFixedString * data); + template <> bool ReadData(const F4SESerializationInterface * intfc, BSFixedString * data); + + template <> bool WriteData(const F4SESerializationInterface * intfc, const std::string * data); + template <> bool ReadData(const F4SESerializationInterface * intfc, std::string * data); + + // Note: Read would have to allocate somehow. You have to do that manually. + template <> bool WriteData(const F4SESerializationInterface * intfc, const char* data); + + template + bool SaveClassHelper(const F4SESerializationInterface* intfc, UInt32 type, T& instance) + { + if (! intfc->OpenRecord(type, T::kSaveVersion)) + return false; + + return instance.Save(intfc); + } +} diff --git a/f4se/f4se/Translation.cpp b/f4se/f4se/Translation.cpp new file mode 100644 index 0000000..4e3fe9d --- /dev/null +++ b/f4se/f4se/Translation.cpp @@ -0,0 +1,120 @@ +#include "Translation.h" + +#include "common/IFileStream.h" +#include +#include +#include "f4se/GameStreams.h" +#include "f4se/GameSettings.h" + +#include "f4se/ScaleformState.h" +#include "f4se/ScaleformTranslator.h" + +namespace Translation +{ + void ParseTranslation(BSScaleformTranslator * translator, std::string & name) + { + Setting * setting = GetINISetting("sLanguage:General"); + std::string path = "Interface\\Translations\\"; + + // Construct translation filename + path += name; + path += "_"; + path += (setting && setting->GetType() == Setting::kType_String) ? setting->data.s : "en"; + path += ".txt"; + + BSResourceNiBinaryStream fileStream(path.c_str()); + if(!fileStream.IsValid()) + return; + else + _MESSAGE("Reading translations from %s...", path.c_str()); + + // Check if file is empty, if not check if the BOM is UTF-16 + UInt16 bom = 0; + UInt32 ret = fileStream.Read(&bom, sizeof(UInt16)); + if(ret == 0) { + _MESSAGE("Empty translation file."); + return; + } + if(bom != 0xFEFF) { + _MESSAGE("BOM Error, file must be encoded in UCS-2 LE."); + return; + } + + while(true) + { + wchar_t buf[512]; + UInt32 len = fileStream.ReadLine_w(buf, sizeof(buf) / sizeof(buf[0]), '\n'); + if(len == 0) // End of file + return; + + // at least $ + wchar_t + \t + wchar_t + if(len < 4 || buf[0] != '$') + continue; + + wchar_t last = buf[len - 1]; + if(last == '\r') + len--; + + // null terminate + buf[len] = 0; + + UInt32 delimIdx = 0; + for(UInt32 i = 0; i < len; i++) + if(buf[i] == '\t') + delimIdx = i; + + // at least $ + wchar_t + if(delimIdx < 2) + continue; + + // replace \t by \0 + buf[delimIdx] = 0; + + BSFixedString key(buf); + BSFixedStringW translation(&buf[delimIdx + 1]); + + TranslationTableItem item(key, translation); + translator->translations.Add(&item); + } + } + + void ImportTranslationFiles(BSScaleformTranslator * translator) + { + char appdataPath[MAX_PATH]; + ASSERT(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdataPath))); + + std::string modlistPath = appdataPath; + modlistPath += "\\Fallout4\\plugins.txt"; + + // Parse mod list file to acquire translation filenames + IFileStream modlistFile; + if(modlistFile.Open(modlistPath.c_str())) + { + while(!modlistFile.HitEOF()) + { + char buf[512]; + modlistFile.ReadString(buf, 512, '\n', '\r'); + + // skip comments + if(buf[0] == '#' || buf[0] != '*') + continue; + + // Determine extension type + std::string line = &buf[1]; + std::string::size_type lastDelim = line.rfind('.'); + if(lastDelim != std::string::npos) + { + std::string ext = line.substr(lastDelim); + + if(_stricmp(ext.c_str(), ".ESM") == 0 || _stricmp(ext.c_str(),".ESP") == 0 || _stricmp(ext.c_str(),".ESL") == 0) + { + std::string name = line.substr(0, lastDelim); + ParseTranslation(translator, name); + } + } + } + } + + modlistFile.Close(); + } +} diff --git a/f4se/f4se/Translation.h b/f4se/f4se/Translation.h new file mode 100644 index 0000000..02a8fe7 --- /dev/null +++ b/f4se/f4se/Translation.h @@ -0,0 +1,9 @@ +#pragma once + +class BSScaleformTranslator; + +namespace Translation +{ + void ImportTranslationFiles(BSScaleformTranslator * translator); + void ParseTranslation(BSScaleformTranslator * translator, std::string & name); +} diff --git a/f4se/f4se/bhkWorld.cpp b/f4se/f4se/bhkWorld.cpp new file mode 100644 index 0000000..498b107 --- /dev/null +++ b/f4se/f4se/bhkWorld.cpp @@ -0,0 +1 @@ +#include "f4se/bhkWorld.h" diff --git a/f4se/f4se/bhkWorld.h b/f4se/f4se/bhkWorld.h new file mode 100644 index 0000000..40db93d --- /dev/null +++ b/f4se/f4se/bhkWorld.h @@ -0,0 +1,117 @@ +#pragma once + +#include "f4se/NiObjects.h" + +// 180 +class bhkWorld : public NiObject +{ +public: + UInt64 unk10[(0x180 - 0x10) >> 3]; +}; + +// 1B0 +class bhkWorldM : public bhkWorld +{ +public: + UInt64 unk180[(0x1B0 - 0x180) >> 3]; +}; + +class hkSerializableCinfo +{ + +}; + +// 18 +class bhkSerializable : public NiObject +{ + hkSerializableCinfo *pInfo; // 10 +}; + +// 08 +class hkBaseObject +{ +public: + virtual ~hkBaseObject(); +}; + +// 10 +class hkReferencedObject : public hkBaseObject +{ +public: + UInt32 m_memSizeAndRefCount; // 8 +}; + +// 20 +class bhkRefObject : public bhkSerializable +{ + hkReferencedObject *phkObject; // 18 +}; + +// 28 +class bhkWorldObject : public bhkRefObject +{ +public: + enum Flags + { + kFlags_Wind = (1 << 0), + kFlags_Dynamic = (1 << 1), + kFlags_FixedConstraints = (1 << 2), + kFlags_Unused1 = (1 << 3), + kFlags_Batch_Add = (1 << 4), + kFlags_Batch_Remove = (1 << 5), + kFlags_Disabled = (1 << 6), + kFlags_Unused2 = (1 << 7), + kFlags_Sticking_Target = (1 << 8), + kFlags_Keyframed_By_LoadedAreaBound = (1 << 9), + }; + UInt16 iFlags; // 20 + UInt16 uiUnderwaterCount; // 22 + UInt32 uiWaterProcessingFrameCount; // 24 +}; + +// 28 +class bhkEntity : public bhkWorldObject +{ +public: + +}; + +// 40 +class bhkRigidBody : public bhkEntity +{ +public: + tArray> ActionConstraint; // 28 +}; + +// 20 +class bhkConstraint : public bhkRefObject +{ +public: + +}; + +// 20 +class bhkLimitedHingeConstraint : public bhkConstraint +{ +public: +}; + +class hkpMotion : public hkReferencedObject +{ +public: + enum MotionType : UInt8 + { + kMotionType_Invalid = 0, + kMotionType_Dynamic, + kMotionType_Sphere_Inertia, + kMotionType_Box_Inertia, + kMotionType_Keyframed, + kMotionType_Fixed, + kMotionType_Thin_Box_Inertia, + kMotionType_Character, + kMotionType_Max_ID, + }; + + MotionType m_type; + // ... more +}; diff --git a/f4se/f4se/exports.def b/f4se/f4se/exports.def new file mode 100644 index 0000000..dc7b012 --- /dev/null +++ b/f4se/f4se/exports.def @@ -0,0 +1,2 @@ +EXPORTS +StartF4SE \ No newline at end of file diff --git a/f4se/f4se/f4se.cpp b/f4se/f4se/f4se.cpp new file mode 100644 index 0000000..9f14e57 --- /dev/null +++ b/f4se/f4se/f4se.cpp @@ -0,0 +1,138 @@ +#include "f4se_common/f4se_version.h" +#include "f4se_common/Utilities.h" +#include "f4se_common/Relocation.h" +#include "f4se_common/BranchTrampoline.h" +#include "f4se_common/SafeWrite.h" +#include +#include "common/IFileStream.h" +#include "Hooks_ObScript.h" +#include "Hooks_Papyrus.h" +#include "Hooks_Scaleform.h" +#include "Hooks_Gameplay.h" +#include "Hooks_Memory.h" +#include "Hooks_GameData.h" +#include "Hooks_SaveLoad.h" +#include "Hooks_Input.h" +#include "Hooks_Debug.h" +#include "Hooks_Threads.h" +#include "Hooks_Camera.h" +#include "PluginManager.h" +#include "InternalSerialization.h" + +IDebugLog gLog; +void * g_moduleHandle = nullptr; + +void WaitForDebugger(void) +{ + while(!IsDebuggerPresent()) + { + Sleep(10); + } + + Sleep(1000 * 2); +} + +static bool isInit = false; + +void F4SE_Initialize(void) +{ + if(isInit) return; + isInit = true; + + gLog.OpenRelative(CSIDL_MYDOCUMENTS, "\\My Games\\Fallout4\\F4SE\\f4se.log"); + +#ifndef _DEBUG + __try { +#endif + + FILETIME now; + GetSystemTimeAsFileTime(&now); + + _MESSAGE("F4SE runtime: initialize (version = %d.%d.%d %08X %08X%08X, os = %s)", + F4SE_VERSION_INTEGER, F4SE_VERSION_INTEGER_MINOR, F4SE_VERSION_INTEGER_BETA, RUNTIME_VERSION, + now.dwHighDateTime, now.dwLowDateTime, GetOSInfoStr().c_str()); + + _MESSAGE("imagebase = %016I64X", GetModuleHandle(NULL)); + _MESSAGE("reloc mgr imagebase = %016I64X", RelocationManager::s_baseAddr); + +#ifdef _DEBUG + SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); + + WaitForDebugger(); +#endif + + if(!g_branchTrampoline.Create(1024 * 64)) + { + _ERROR("couldn't create branch trampoline. this is fatal. skipping remainder of init process."); + return; + } + + if(!g_localTrampoline.Create(1024 * 64, g_moduleHandle)) + { + _ERROR("couldn't create codegen buffer. this is fatal. skipping remainder of init process."); + return; + } + + Hooks_Debug_Init(); + Hooks_ObScript_Init(); + Hooks_Papyrus_Init(); + Hooks_Scaleform_Init(); + Hooks_Gameplay_Init(); + Hooks_GameData_Init(); + Hooks_SaveLoad_Init(); + Hooks_Input_Init(); + Hooks_Threads_Init(); + Hooks_Camera_Init(); + + g_pluginManager.Init(); + + Hooks_Debug_Commit(); + Hooks_ObScript_Commit(); + Hooks_Papyrus_Commit(); + Hooks_Scaleform_Commit(); + Hooks_Gameplay_Commit(); + Hooks_GameData_Commit(); + Hooks_SaveLoad_Commit(); + Hooks_Input_Commit(); + Hooks_Threads_Commit(); + Hooks_Camera_Commit(); + + Init_CoreSerialization_Callbacks(); + + FlushInstructionCache(GetCurrentProcess(), NULL, 0); + +#ifndef _DEBUG + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + _ERROR("exception thrown during startup"); + } +#endif + + _MESSAGE("init complete"); +} + +extern "C" { + + void StartF4SE(void) + { + F4SE_Initialize(); + } + + BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved) + { + switch(dwReason) + { + case DLL_PROCESS_ATTACH: + g_moduleHandle = (void *)hDllHandle; + F4SE_Initialize(); + break; + + case DLL_PROCESS_DETACH: + break; + }; + + return TRUE; + } + +}; diff --git a/f4se/f4se/f4se.vcxproj b/f4se/f4se/f4se.vcxproj new file mode 100644 index 0000000..0d41874 --- /dev/null +++ b/f4se/f4se/f4se.vcxproj @@ -0,0 +1,353 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + {f1447a44-f26a-4680-8e20-2d9186766e51} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {A236F69D-8FF9-4491-AC5F-45BF49448BBE} + Win32Proj + f4se + + + + + + + + + 10.0.18362.0 + + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + true + $(ProjectName)_1_10_163 + + + false + $(ProjectName)_1_10_163 + $(SolutionDir)f4se;$(IncludePath) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;F4SE_EXPORTS;RUNTIME;RUNTIME_VERSION=0x010A0A30;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\.. + common/IPrefix.h + MultiThreadedDebug + + + Windows + true + exports.def + + + copy "$(TargetPath)" "$(Fallout4Path)\$(TargetFileName)" + + + Installing DLL... + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;F4SE_EXPORTS;RUNTIME;RUNTIME_VERSION=0x010A0A30;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\.. + common/IPrefix.h + MultiThreaded + true + + + Windows + true + true + true + exports.def + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f4se/f4se/f4se.vcxproj.filters b/f4se/f4se/f4se.vcxproj.filters new file mode 100644 index 0000000..7704ebf --- /dev/null +++ b/f4se/f4se/f4se.vcxproj.filters @@ -0,0 +1,706 @@ + + + + + + + + + api + + + api + + + hooks + + + obscript + + + api + + + api + + + api + + + api + + + netimmerse + + + netimmerse + + + api + + + netimmerse + + + netimmerse + + + netimmerse + + + netimmerse + + + api + + + internal + + + api + + + api + + + scaleform + + + hooks + + + hooks + + + hooks + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + hooks + + + api + + + netimmerse + + + api + + + netimmerse + + + api + + + api + + + api + + + api + + + api + + + netimmerse + + + api + + + hooks + + + papyrus\vm + + + papyrus\vm + + + api + + + netimmerse + + + hooks + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + hooks + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + netimmerse + + + internal + + + internal + + + hooks + + + hooks + + + netimmerse + + + papyrus\vm + + + internal + + + papyrus\vm + + + papyrus\functions + + + papyrus\vm + + + papyrus\vm + + + papyrus\functions + + + papyrus\functions + + + scaleform + + + papyrus\vm + + + papyrus\vm + + + papyrus\functions + + + internal + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + api + + + hooks + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + netimmerse + + + api + + + papyrus\functions + + + netimmerse + + + netimmerse + + + netimmerse + + + netimmerse + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + internal + + + papyrus\functions + + + papyrus\functions + + + + + + api + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + + + {9450fc4c-75b6-4488-bbcf-eb85ab09adc6} + + + {4d6b0bc6-fcd2-4a6a-8d71-66b2661e6676} + + + {69b04711-4ce5-4233-a001-6239f33bac37} + + + {0d737e95-d0ef-4c7c-8ba0-1dd5bab3aa26} + + + {c674ab06-93c6-4c9e-be2c-93ce2ca09274} + + + {bb1e4e23-e1ba-4913-a905-60ff75e60297} + + + {bf19db67-c2c6-4319-9992-f13bbe743fdf} + + + {618e2d88-ea94-40ab-a796-2f7d4ab270ef} + + + {53266a9e-d65a-4ef4-b936-685b8012d3c0} + + + + + api + + + api + + + hooks + + + obscript + + + api + + + api + + + api + + + api + + + netimmerse + + + netimmerse + + + api + + + netimmerse + + + netimmerse + + + netimmerse + + + netimmerse + + + api + + + internal + + + internal + + + api + + + api + + + scaleform + + + hooks + + + hooks + + + hooks + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + scaleform + + + hooks + + + api + + + netimmerse + + + api + + + netimmerse + + + api + + + api + + + api + + + api + + + api + + + netimmerse + + + api + + + hooks + + + papyrus\vm + + + papyrus\vm + + + api + + + netimmerse + + + hooks + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + papyrus\vm + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + hooks + + + netimmerse + + + internal + + + internal + + + hooks + + + hooks + + + netimmerse + + + papyrus\vm + + + internal + + + papyrus\vm + + + papyrus\functions + + + papyrus\vm + + + papyrus\vm + + + papyrus\functions + + + papyrus\functions + + + scaleform + + + papyrus\vm + + + papyrus\vm + + + papyrus\functions + + + internal + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + api + + + hooks + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + netimmerse + + + api + + + netimmerse + + + papyrus\functions + + + netimmerse + + + netimmerse + + + netimmerse + + + netimmerse + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + papyrus\functions + + + internal + + + papyrus\functions + + + papyrus\functions + + + \ No newline at end of file diff --git a/f4se/f4se_common/BranchTrampoline.cpp b/f4se/f4se_common/BranchTrampoline.cpp new file mode 100644 index 0000000..fd39b55 --- /dev/null +++ b/f4se/f4se_common/BranchTrampoline.cpp @@ -0,0 +1,254 @@ +#include "BranchTrampoline.h" +#include "SafeWrite.h" +#include + +BranchTrampoline g_branchTrampoline; +BranchTrampoline g_localTrampoline; + +BranchTrampoline::BranchTrampoline() + :m_base(nullptr) + ,m_len(0) + ,m_allocated(0) + ,m_curAlloc(nullptr) +{ + // +} + +BranchTrampoline::~BranchTrampoline() +{ + Destroy(); +} + +bool BranchTrampoline::Create(size_t len, void * module) +{ + if(!module) module = GetModuleHandle(NULL); + + // search backwards from module base + uintptr_t moduleBase = uintptr_t(module); + uintptr_t addr = moduleBase; + uintptr_t maxDisplacement = 0x80000000 - (1024 * 1024 * 128); // largest 32-bit displacement with 128MB scratch space + uintptr_t lowestOKAddress = (moduleBase >= maxDisplacement) ? moduleBase - maxDisplacement : 0; + addr--; + + while(!m_base) + { + MEMORY_BASIC_INFORMATION info; + + if(!VirtualQuery((void *)addr, &info, sizeof(info))) + { + _ERROR("VirtualQuery failed: %08X", GetLastError()); + break; + } + + if(info.State == MEM_FREE) + { + // free block, big enough? + if(info.RegionSize >= len) + { + // try to allocate it + addr = ((uintptr_t)info.BaseAddress) + info.RegionSize - len; + + m_base = (void *)VirtualAlloc((void *)addr, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if(m_base) + { + m_len = len; + m_allocated = 0; + } + else + { + _WARNING("trampoline alloc %016I64Xx%016I64X failed (%08X)", addr, len, GetLastError()); + } + } + } + + // move back and try again + if(!m_base) + { + addr = ((uintptr_t)info.BaseAddress) - 1; + } + + if(addr < lowestOKAddress) + { + _ERROR("couldn't allocate trampoline, no free space before image"); + break; + } + } + + return m_base != nullptr; +} + +void BranchTrampoline::Destroy() +{ + if(m_base) + { + VirtualFree(m_base, 0, MEM_RELEASE); + m_base = nullptr; + } +} + +void * BranchTrampoline::StartAlloc() +{ + ASSERT(m_base); + ASSERT(!m_curAlloc); + + m_curAlloc = ((UInt8 *)m_base) + m_allocated; + + return m_curAlloc; +} + +void BranchTrampoline::EndAlloc(const void * end) +{ + ASSERT(m_base); + ASSERT(m_curAlloc); + + size_t len = uintptr_t(end) - uintptr_t(m_curAlloc); + ASSERT(len <= Remain()); + + m_allocated += len; + m_curAlloc = nullptr; +} + +void * BranchTrampoline::Allocate(size_t size) +{ + ASSERT(m_base); + + void * result = nullptr; + + if(size <= Remain()) + { + result = ((UInt8 *)m_base) + m_allocated; + m_allocated += size; + } + + return result; +} + +bool BranchTrampoline::Write6Branch(uintptr_t src, uintptr_t dst) +{ + return Write6Branch_Internal(src, dst, 0x25); +} + +bool BranchTrampoline::Write6Call(uintptr_t src, uintptr_t dst) +{ + return Write6Branch_Internal(src, dst, 0x15); +} + +bool BranchTrampoline::Write5Branch(uintptr_t src, uintptr_t dst) +{ + return Write5Branch_Internal(src, dst, 0xE9); +} + +bool BranchTrampoline::Write5Call(uintptr_t src, uintptr_t dst) +{ + return Write5Branch_Internal(src, dst, 0xE8); +} + +bool BranchTrampoline::Write6Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op) +{ + bool result = false; + + uintptr_t * trampoline = (uintptr_t *)Allocate(); + if(trampoline) + { + uintptr_t trampolineAddr = (uintptr_t)trampoline; + uintptr_t nextInstr = src + 6; + ptrdiff_t trampolineDispl = trampolineAddr - nextInstr; + + if((trampolineDispl >= _I32_MIN) && (trampolineDispl <= _I32_MAX)) + { + UInt8 code[6]; + + // jmp [rip+imm32] + code[0] = 0xFF; + code[1] = op; + *((SInt32 *)&code[2]) = (SInt32)trampolineDispl; + + SafeWriteBuf(src, code, sizeof(code)); + + *trampoline = dst; + + result = true; + +#if defined(_DEBUG) + _MESSAGE("Write6Branch: %016I64X %016I64X %016I64X", src, dst, trampoline); +#endif + } + } + + // do this for now so it's obvious when something goes wrong + ASSERT(result); + + return result; +} + +bool BranchTrampoline::Write5Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op) +{ + bool result = false; + +#pragma pack(push, 1) + // code placed in trampoline + struct TrampolineCode + { + // jmp [rip] + UInt8 escape; // FF + UInt8 modrm; // 25 + UInt32 displ; // 00000000 + // rip points here + UInt64 dst; // target + + void Init(uintptr_t _dst) + { + escape = 0xFF; + modrm = 0x25; + displ = 0; + dst = _dst; + } + }; + + struct HookCode + { + // jmp disp32 + UInt8 op; // E9 for jmp, E8 for call + SInt32 displ; // + + void Init(SInt32 _displ, UInt8 _op) + { + op = _op; + displ = _displ; + } + }; +#pragma pack(pop) + + STATIC_ASSERT(sizeof(TrampolineCode) == 14); + STATIC_ASSERT(sizeof(HookCode) == 5); + + TrampolineCode * trampolineCode = (TrampolineCode *)Allocate(sizeof(TrampolineCode)); + if(trampolineCode) + { + trampolineCode->Init(dst); + + HookCode hookCode; + + uintptr_t trampolineAddr = uintptr_t(trampolineCode); + uintptr_t nextInstr = src + sizeof(hookCode); + ptrdiff_t trampolineDispl = trampolineAddr - nextInstr; + + // should never fail because we're branching in to the trampoline + ASSERT((trampolineDispl >= _I32_MIN) && (trampolineDispl <= _I32_MAX)); + + hookCode.Init(trampolineDispl, op); + + SafeWriteBuf(src, &hookCode, sizeof(hookCode)); + +#if defined(_DEBUG) + _MESSAGE("Write5Branch: %016I64X %016I64X %016I64X", src, dst, trampolineCode); +#endif + + result = true; + } + + // do this for now so it's obvious when something goes wrong + ASSERT(result); + + return result; +} diff --git a/f4se/f4se_common/BranchTrampoline.h b/f4se/f4se_common/BranchTrampoline.h new file mode 100644 index 0000000..d1393a7 --- /dev/null +++ b/f4se/f4se_common/BranchTrampoline.h @@ -0,0 +1,43 @@ +#pragma once + +class BranchTrampoline +{ +public: + BranchTrampoline(); + ~BranchTrampoline(); + + bool Create(size_t len, void * module = NULL); + void Destroy(); + + // allocate unsized + void * StartAlloc(); + void EndAlloc(const void * end); + + void * Allocate(size_t size = sizeof(void *)); + + size_t Remain() { return m_len - m_allocated; } + + // takes 6 bytes of space at src, 8 bytes in trampoline + bool Write6Branch(uintptr_t src, uintptr_t dst); + bool Write6Call(uintptr_t src, uintptr_t dst); + + // takes 5 bytes of space at src, 14 bytes in trampoline + bool Write5Branch(uintptr_t src, uintptr_t dst); + bool Write5Call(uintptr_t src, uintptr_t dst); + +private: + // takes 6 bytes of space at src, 8 bytes in trampoline + bool Write6Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op); + + // takes 5 bytes of space at src, 14 bytes in trampoline + bool Write5Branch_Internal(uintptr_t src, uintptr_t dst, UInt8 op); + + void * m_base; + size_t m_len; // bytes + size_t m_allocated; // bytes + + void * m_curAlloc; // currently active StartAlloc base +}; + +extern BranchTrampoline g_branchTrampoline; +extern BranchTrampoline g_localTrampoline; diff --git a/f4se/f4se_common/Relocation.cpp b/f4se/f4se_common/Relocation.cpp new file mode 100644 index 0000000..baca1a8 --- /dev/null +++ b/f4se/f4se_common/Relocation.cpp @@ -0,0 +1,30 @@ +#include "Relocation.h" + +// the goal of this file is to support pointers in to a relocated binary with as little runtime overhead, code bloat, and hassle as possible +// +// since the main executable will always be loaded before the dll, the easiest solution is to perform the relocation in the constructor of +// a pointer class that supports conversion to T*. however, since we can't control anything about initialization order, each constructor +// must call GetModuleHandle(NULL) locally, which sucks. each pointer will need an entry in the static init table, and nothing can be done +// with the pointers in any other static constructors. +// +// one solution to this problem is init_seg(lib). any objects constructed in a file containing this pragma will be constructed before standard +// 'user' level code. this means we can use the constructor of that object to call GetModuleHandle once and initialize a global with the load +// address, which the pointer class constructor then references to fix up the addresses. this still creates an entry in the static init table +// for each pointer, but only calls GetModuleHandle once. pointers are not fixed up until all static init has finished, so other static ctors +// can't safely use pointers. +// +// the problem can't be solved further without moving the RelocPtr constructors in to init_seg(lib), which doesn't appear to be possible +// without forcing all pointers to be defined in a file with init_seg(lib). that is really ugly and doesn't seem like a good idea. + +// anything in this file will initialized after the crt but before any user code +#pragma warning(disable: 4073) // yes this is intentional +#pragma init_seg(lib) + +static RelocationManager s_relocMgr; + +uintptr_t RelocationManager::s_baseAddr = 0; + +RelocationManager::RelocationManager() +{ + s_baseAddr = reinterpret_cast(GetModuleHandle(NULL)); +} diff --git a/f4se/f4se_common/Relocation.h b/f4se/f4se_common/Relocation.h new file mode 100644 index 0000000..1d17aac --- /dev/null +++ b/f4se/f4se_common/Relocation.h @@ -0,0 +1,89 @@ +#pragma once + +class RelocationManager +{ +public: + RelocationManager(); + + static uintptr_t s_baseAddr; +}; + +// use this for addresses that represent pointers to a type T +template +class RelocPtr +{ +public: + RelocPtr(uintptr_t offset) + :m_offset(offset + RelocationManager::s_baseAddr) + { + // + } + + operator T *() const + { + return GetPtr(); + } + + T * operator->() const + { + return GetPtr(); + } + + T * GetPtr() const + { + return reinterpret_cast (m_offset); + } + + const T * GetConst() const + { + return reinterpret_cast (m_offset); + } + + uintptr_t GetUIntPtr() const + { + return m_offset; + } + +private: + uintptr_t m_offset; + + // hide + RelocPtr(); + RelocPtr(RelocPtr & rhs); + RelocPtr & operator=(RelocPtr & rhs); +}; + +// use this for direct addresses to types T. needed to avoid ambiguity +template +class RelocAddr +{ +public: + RelocAddr(uintptr_t offset) + :m_offset(reinterpret_cast (offset + RelocationManager::s_baseAddr)) + { + // + } + + operator T() + { + return reinterpret_cast (m_offset); + } + + uintptr_t GetUIntPtr() const + { + return reinterpret_cast (m_offset); + } + +private: + // apparently you can't reinterpret_cast from a type to the same type + // that's kind of stupid and makes it impossible to use this for uintptr_ts if I use the same type + // so we make a new type by storing the data in a pointer to this useless struct + // at least this is hidden by a wrapper + struct BlockConversionType { }; + BlockConversionType * m_offset; + + // hide + RelocAddr(); + RelocAddr(RelocAddr & rhs); + RelocAddr & operator=(RelocAddr & rhs); +}; diff --git a/f4se/f4se_common/SafeWrite.cpp b/f4se/f4se_common/SafeWrite.cpp new file mode 100644 index 0000000..2118c32 --- /dev/null +++ b/f4se/f4se_common/SafeWrite.cpp @@ -0,0 +1,66 @@ +#include "SafeWrite.h" + +void SafeWriteBuf(uintptr_t addr, void * data, size_t len) +{ + UInt32 oldProtect; + + VirtualProtect((void *)addr, len, PAGE_EXECUTE_READWRITE, &oldProtect); + memcpy((void *)addr, data, len); + VirtualProtect((void *)addr, len, oldProtect, &oldProtect); +} + +void SafeWrite8(uintptr_t addr, UInt8 data) +{ + SafeWriteBuf(addr, &data, sizeof(data)); +} + +void SafeWrite16(uintptr_t addr, UInt16 data) +{ + SafeWriteBuf(addr, &data, sizeof(data)); +} + +void SafeWrite32(uintptr_t addr, UInt32 data) +{ + SafeWriteBuf(addr, &data, sizeof(data)); +} + +void SafeWrite64(uintptr_t addr, UInt64 data) +{ + SafeWriteBuf(addr, &data, sizeof(data)); +} + +static bool SafeWriteJump_Internal(uintptr_t src, uintptr_t dst, UInt8 op) +{ +#pragma pack(push, 1) + struct Code + { + UInt8 op; + SInt32 displ; + }; +#pragma pack(pop) + + STATIC_ASSERT(sizeof(Code) == 5); + + ptrdiff_t delta = dst - (src + sizeof(Code)); + if((delta < INT_MIN) || (delta > INT_MAX)) + return false; + + Code code; + + code.op = op; + code.displ = delta; + + SafeWriteBuf(src, &code, sizeof(code)); + + return true; +} + +bool SafeWriteJump(uintptr_t src, uintptr_t dst) +{ + return SafeWriteJump_Internal(src, dst, 0xE9); +} + +bool SafeWriteCall(uintptr_t src, uintptr_t dst) +{ + return SafeWriteJump_Internal(src, dst, 0xE8); +} diff --git a/f4se/f4se_common/SafeWrite.h b/f4se/f4se_common/SafeWrite.h new file mode 100644 index 0000000..709e38b --- /dev/null +++ b/f4se/f4se_common/SafeWrite.h @@ -0,0 +1,13 @@ +#pragma once + +void SafeWriteBuf(uintptr_t addr, void * data, size_t len); +void SafeWrite8(uintptr_t addr, UInt8 data); +void SafeWrite16(uintptr_t addr, UInt16 data); +void SafeWrite32(uintptr_t addr, UInt32 data); +void SafeWrite64(uintptr_t addr, UInt64 data); + +// ### warning: if you try to branch more than +/- 2GB with these, they will fail and return false +// ### this is a limitation of the 'jmp' instruction and more generally the x64 ISA +// 5 bytes written to src +bool SafeWriteJump(uintptr_t src, uintptr_t dst); +bool SafeWriteCall(uintptr_t src, uintptr_t dst); diff --git a/f4se/f4se_common/Utilities.cpp b/f4se/f4se_common/Utilities.cpp new file mode 100644 index 0000000..93cebcf --- /dev/null +++ b/f4se/f4se_common/Utilities.cpp @@ -0,0 +1,255 @@ +#include "Utilities.h" +#include + +std::string GetRuntimePath() +{ + static char appPath[4096] = { 0 }; + + if(appPath[0]) + return appPath; + + ASSERT(GetModuleFileName(GetModuleHandle(NULL), appPath, sizeof(appPath))); + + return appPath; +} + +std::string GetRuntimeName() +{ + std::string appPath = GetRuntimePath(); + + std::string::size_type slashOffset = appPath.rfind('\\'); + if(slashOffset == std::string::npos) + return appPath; + + return appPath.substr(slashOffset + 1); +} + +const std::string & GetRuntimeDirectory() +{ + static std::string s_runtimeDirectory; + + if(s_runtimeDirectory.empty()) + { + std::string runtimePath = GetRuntimePath(); + + // truncate at last slash + std::string::size_type lastSlash = runtimePath.rfind('\\'); + if(lastSlash != std::string::npos) // if we don't find a slash something is VERY WRONG + { + s_runtimeDirectory = runtimePath.substr(0, lastSlash + 1); + } + else + { + _WARNING("no slash in runtime path? (%s)", runtimePath.c_str()); + } + } + + return s_runtimeDirectory; +} + +const std::string & GetConfigPath() +{ + static std::string s_configPath; + + if(s_configPath.empty()) + { + std::string runtimePath = GetRuntimeDirectory(); + if(!runtimePath.empty()) + { + s_configPath = runtimePath + "Data\\F4SE\\f4se.ini"; + + _MESSAGE("config path = %s", s_configPath.c_str()); + } + } + + return s_configPath; +} + +std::string GetConfigOption(const char * section, const char * key) +{ + std::string result; + + const std::string & configPath = GetConfigPath(); + if(!configPath.empty()) + { + char resultBuf[256]; + resultBuf[0] = 0; + + UInt32 resultLen = GetPrivateProfileString(section, key, NULL, resultBuf, sizeof(resultBuf), configPath.c_str()); + + result = resultBuf; + } + + return result; +} + +bool GetConfigOption_UInt32(const char * section, const char * key, UInt32 * dataOut) +{ + std::string data = GetConfigOption(section, key); + if(data.empty()) + return false; + + return (sscanf_s(data.c_str(), "%u", dataOut) == 1); +} + +const std::string & GetOSInfoStr() +{ + static std::string result; + + if(result.empty()) + { + OSVERSIONINFO info; + + info.dwOSVersionInfoSize = sizeof(info); + + if(GetVersionEx(&info)) + { + char buf[256]; + + sprintf_s(buf, sizeof(buf), "%d.%d (%d)", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber); + + result = buf; + } + else + { + result = "unknown"; + } + } + + return result; +} + +void * GetIATAddr(void * module, const char * searchDllName, const char * searchImportName) +{ + UInt8 * base = (UInt8 *)module; + IMAGE_DOS_HEADER * dosHeader = (IMAGE_DOS_HEADER *)base; + IMAGE_NT_HEADERS * ntHeader = (IMAGE_NT_HEADERS *)(base + dosHeader->e_lfanew); + IMAGE_IMPORT_DESCRIPTOR * importTable = + (IMAGE_IMPORT_DESCRIPTOR *)(base + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); + + for(; importTable->Characteristics; ++importTable) + { + const char * dllName = (const char *)(base + importTable->Name); + + if(!_stricmp(dllName, searchDllName)) + { + // found the dll + + IMAGE_THUNK_DATA * thunkData = (IMAGE_THUNK_DATA *)(base + importTable->OriginalFirstThunk); + uintptr_t * iat = (uintptr_t *)(base + importTable->FirstThunk); + + for(; thunkData->u1.Ordinal; ++thunkData, ++iat) + { + if(!IMAGE_SNAP_BY_ORDINAL(thunkData->u1.Ordinal)) + { + IMAGE_IMPORT_BY_NAME * importInfo = (IMAGE_IMPORT_BY_NAME *)(base + thunkData->u1.AddressOfData); + + if(!_stricmp((char *)importInfo->Name, searchImportName)) + { + // found the import + return iat; + } + } + } + + return NULL; + } + } + + return NULL; +} + +#pragma warning (push) +#pragma warning (disable : 4200) +struct RTTIType +{ + void * typeInfo; + UInt64 data; + char name[0]; +}; + +struct RTTILocator +{ + UInt32 sig, offset, cdOffset; + UInt32 typeDesc; + UInt32 classDesc; +}; +#pragma warning (pop) + +// use the RTTI information to return an object's class name +const char * GetObjectClassName(void * objBase) +{ + const char * result = ""; + __try + { + void ** obj = (void **)objBase; + RTTILocator ** vtbl = (RTTILocator **)obj[0]; + RTTILocator * rtti = vtbl[-1]; + UInt64 typeDesc = rtti->typeDesc; + RelocPtr type(typeDesc); + + // starts with ,? + if((type->name[0] == '.') && (type->name[1] == '?')) + { + // is at most 100 chars long + for(UInt32 i = 0; i < 100; i++) + { + if(type->name[i] == 0) + { + // remove the .?AV + result = type->name + 4; + break; + } + } + } + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + // return the default + } + + return result; +} + +void DumpClass(void * theClassPtr, UInt64 nIntsToDump) +{ + UInt64* basePtr = (UInt64*)theClassPtr; + + _MESSAGE("DumpClass: %016I64X", basePtr); + + gLog.Indent(); + + if (!theClassPtr) return; + for (UInt64 ix = 0; ix < nIntsToDump; ix++ ) { + UInt64* curPtr = basePtr+ix; + const char* curPtrName = NULL; + UInt64 otherPtr = 0; + float otherFloat1 = 0.0; + float otherFloat2 = 0.0; + const char* otherPtrName = NULL; + if (curPtr) { + curPtrName = GetObjectClassName((void*)curPtr); + + __try + { + otherPtr = *curPtr; + UInt32 lowerFloat = otherPtr & 0xFFFFFFFF; + UInt32 upperFloat = (otherPtr >> 32) & 0xFFFFFFFF; + otherFloat1 = *(float*)&lowerFloat; + otherFloat2 = *(float*)&upperFloat; + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + // + } + + if (otherPtr) { + otherPtrName = GetObjectClassName((void*)otherPtr); + } + } + + _MESSAGE("%3d +%03X ptr: 0x%016I64X: %32s *ptr: 0x%016I64x | %f, %f: %32s", ix, ix*8, curPtr, curPtrName, otherPtr, otherFloat2, otherFloat1, otherPtrName); + } + + gLog.Outdent(); +} \ No newline at end of file diff --git a/f4se/f4se_common/Utilities.h b/f4se/f4se_common/Utilities.h new file mode 100644 index 0000000..16b5c70 --- /dev/null +++ b/f4se/f4se_common/Utilities.h @@ -0,0 +1,192 @@ +#pragma once + +#include "f4se_common/Relocation.h" + +// this has been tested to work for non-varargs functions +// varargs functions end up with 'this' passed as the last parameter (ie. probably broken) +// do NOT use with classes that have multiple inheritance + +// if many member functions are to be declared, use MEMBER_FN_PREFIX to create a type with a known name +// so it doesn't need to be restated throughout the member list + +// all of the weirdness with the _GetType function is because you can't declare a static const pointer +// inside the class definition. we sadly can't inline anymore because of relocation. + +// RelocPtr only works at a global scope, which we can't handle or we'd be bypassing the function route altogether + +#define MEMBER_FN_PREFIX(className) \ + typedef className _MEMBER_FN_BASE_TYPE + +#define DEFINE_MEMBER_FN_LONG(className, functionName, retnType, address, ...) \ + typedef retnType (className::* _##functionName##_type)(__VA_ARGS__); \ + \ + inline _##functionName##_type * _##functionName##_GetPtr(void) \ + { \ + static uintptr_t _address; \ + _address = address + RelocationManager::s_baseAddr; \ + return (_##functionName##_type *)&_address; \ + } + +#define DEFINE_MEMBER_FN(functionName, retnType, address, ...) \ + DEFINE_MEMBER_FN_LONG(_MEMBER_FN_BASE_TYPE, functionName, retnType, address, __VA_ARGS__) + +#define DEFINE_STATIC_HEAP(staticAllocate, staticFree) \ + static void * operator new(std::size_t size) \ + { \ + return staticAllocate(size); \ + } \ + static void * operator new(std::size_t size, const std::nothrow_t &) \ + { \ + return staticAllocate(size); \ + } \ + static void * operator new(std::size_t size, void * ptr) \ + { \ + return ptr; \ + } \ + static void operator delete(void * ptr) \ + { \ + staticFree(ptr); \ + } \ + static void operator delete(void * ptr, const std::nothrow_t &) \ + { \ + staticFree(ptr); \ + } \ + static void operator delete(void *, void *) \ + { \ + } + +#define CALL_MEMBER_FN(obj, fn) \ + ((*(obj)).*(*((obj)->_##fn##_GetPtr()))) + + +// Using the original implementation does very broken things in a Release build +// For classes like BSGFxShaderFXTarget and GameMenuBase +#define FORCE_INLINE __forceinline +#define DEFINE_MEMBER_FN_0(fnName, retnType, addr) \ + FORCE_INLINE retnType fnName() { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(); \ + } +#define DEFINE_MEMBER_FN_1(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1); \ + } +#define DEFINE_MEMBER_FN_2(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2); \ + } +#define DEFINE_MEMBER_FN_3(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3); \ + } +#define DEFINE_MEMBER_FN_4(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3, t4); \ + } +#define DEFINE_MEMBER_FN_5(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3, t4, t5); \ + } +#define DEFINE_MEMBER_FN_6(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3, t4, t5, t6); \ + } +#define DEFINE_MEMBER_FN_7(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3, t4, t5, t6, t7); \ + } +#define DEFINE_MEMBER_FN_8(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7, T8 && t8) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3, t4, t5, t6, t7, t8); \ + } +#define DEFINE_MEMBER_FN_9(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7, T8 && t8, T9 && t9) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3, t4, t5, t6, t7, t8, t9); \ + } +#define DEFINE_MEMBER_FN_10(fnName, retnType, addr, ...) \ + template \ + FORCE_INLINE retnType fnName(T1 && t1, T2 && t2, T3 && t3, T4 && t4, T5 && t5, T6 && t6, T7 && t7, T8 && t8, T9 && t9, T10 && t10) { \ + struct empty_struct {}; \ + typedef retnType(empty_struct::*_##fnName##_type)(__VA_ARGS__); \ + const static uintptr_t address = addr + RelocationManager::s_baseAddr; \ + _##fnName##_type fn = *(_##fnName##_type*)&address; \ + return (reinterpret_cast(this)->*fn)(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10); \ + } + +// this is the solution to getting a pointer-to-member-function pointer +template +uintptr_t GetFnAddr(T src) +{ + union + { + uintptr_t u; + T t; + } data; + + data.t = src; + + return data.u; +} + +std::string GetRuntimePath(); +std::string GetRuntimeName(); +const std::string & GetRuntimeDirectory(); + +const std::string & GetConfigPath(); +std::string GetConfigOption(const char * section, const char * key); +bool GetConfigOption_UInt32(const char * section, const char * key, UInt32 * dataOut); + +const std::string & GetOSInfoStr(); + +void * GetIATAddr(void * module, const char * searchDllName, const char * searchImportName); + +const char * GetObjectClassName(void * objBase); +void DumpClass(void * theClassPtr, UInt64 nIntsToDump); diff --git a/f4se/f4se_common/f4se_common.vcxproj b/f4se/f4se_common/f4se_common.vcxproj new file mode 100644 index 0000000..867e08a --- /dev/null +++ b/f4se/f4se_common/f4se_common.vcxproj @@ -0,0 +1,114 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + {472e19ab-def0-42df-819b-18722e8dc822} + + + + {20C6411C-596F-4B85-BE4E-8BC91F59D8A6} + Win32Proj + f4se_common + + + + + + + + + 10.0.18362.0 + + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + $(SolutionDir)f4se;$(IncludePath) + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\.. + MultiThreadedDebug + common/IPrefix.h + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + $(SolutionDir);$(SolutionDir)\.. + MultiThreaded + common/IPrefix.h + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/f4se/f4se_common/f4se_common.vcxproj.filters b/f4se/f4se_common/f4se_common.vcxproj.filters new file mode 100644 index 0000000..b7b3f89 --- /dev/null +++ b/f4se/f4se_common/f4se_common.vcxproj.filters @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/f4se/f4se_common/f4se_version.h b/f4se/f4se_common/f4se_version.h new file mode 100644 index 0000000..f6b60e6 --- /dev/null +++ b/f4se/f4se_common/f4se_version.h @@ -0,0 +1,78 @@ +#ifndef __F4SE_VERSION_H__ +#define __F4SE_VERSION_H__ + +// these have to be macros so they can be used in the .rc +#define F4SE_VERSION_INTEGER 0 +#define F4SE_VERSION_INTEGER_MINOR 6 +#define F4SE_VERSION_INTEGER_BETA 20 +#define F4SE_VERSION_VERSTRING "0, 0, 6, 20" +#define F4SE_VERSION_PADDEDSTRING "0022" +#define F4SE_VERSION_RELEASEIDX 22 + +#define MAKE_EXE_VERSION_EX(major, minor, build, sub) ((((major) & 0xFF) << 24) | (((minor) & 0xFF) << 16) | (((build) & 0xFFF) << 4) | ((sub) & 0xF)) +#define MAKE_EXE_VERSION(major, minor, build) MAKE_EXE_VERSION_EX(major, minor, build, 0) + +#define GET_EXE_VERSION_MAJOR(a) (((a) & 0xFF000000) >> 24) +#define GET_EXE_VERSION_MINOR(a) (((a) & 0x00FF0000) >> 16) +#define GET_EXE_VERSION_BUILD(a) (((a) & 0x0000FFF0) >> 4) +#define GET_EXE_VERSION_SUB(a) (((a) & 0x0000000F) >> 0) + +#define RUNTIME_VERSION_1_1_29 MAKE_EXE_VERSION(1, 1, 29) // 0x010101D0 initial version released on steam +#define RUNTIME_VERSION_1_1_30 MAKE_EXE_VERSION(1, 1, 30) // 0x010101E0 day1 patch to fix xaudio problem +#define RUNTIME_VERSION_1_2 MAKE_EXE_VERSION(1, 2, 33) // 0x01020210 beta, removed mini-mod-manager from the launcher +#define RUNTIME_VERSION_1_2_37 MAKE_EXE_VERSION(1, 2, 37) // 0x01020250 beta - unclear +#define RUNTIME_VERSION_1_3_45 MAKE_EXE_VERSION(1, 3, 45) // 0x010302D0 beta - compiled with optimization enabled (finally), added hbao+ and cuda-based impact particles (shame) +#define RUNTIME_VERSION_1_3_47 MAKE_EXE_VERSION(1, 3, 47) // 0x010302F0 release, originally pushed as beta +#define RUNTIME_VERSION_1_4_124 MAKE_EXE_VERSION(1, 4, 124) // 0x010407C0 beta - preliminary internal mod manager +#define RUNTIME_VERSION_1_4_125 MAKE_EXE_VERSION(1, 4, 125) // 0x010407D0 beta - released same day as previous version. about 300K smaller +#define RUNTIME_VERSION_1_4_131 MAKE_EXE_VERSION(1, 4, 131) // 0x01040830 beta - +#define RUNTIME_VERSION_1_4_132 MAKE_EXE_VERSION(1, 4, 132) // 0x01040840 released without a beta version +#define RUNTIME_VERSION_1_5_141 MAKE_EXE_VERSION(1, 5, 141) // 0x010508D0 beta - survival mode improvements +#define RUNTIME_VERSION_1_5_147 MAKE_EXE_VERSION(1, 5, 147) // 0x01050930 beta - survival mode improvements +#define RUNTIME_VERSION_1_5_151 MAKE_EXE_VERSION(1, 5, 151) // 0x01050970 beta - survival mode improvements +#define RUNTIME_VERSION_1_5_154 MAKE_EXE_VERSION(1, 5, 154) // 0x010509A0 beta - public editor beta +#define RUNTIME_VERSION_1_5_157 MAKE_EXE_VERSION(1, 5, 157) // 0x010509D0 beta - released the same day +#define RUNTIME_VERSION_1_5_205 MAKE_EXE_VERSION(1, 5, 205) // 0x01050CD0 beta - released to main +#define RUNTIME_VERSION_1_5_210 MAKE_EXE_VERSION(1, 5, 210) // 0x01050D20 beta - no whatsnew +#define RUNTIME_VERSION_1_5_307 MAKE_EXE_VERSION(1, 5, 307) // 0x01051330 release - exe almost identical +#define RUNTIME_VERSION_1_5_412 MAKE_EXE_VERSION(1, 5, 412) // 0x010519C0 beta - that's a big number. added GetOrbisModInfo console cmd (does nothing) +#define RUNTIME_VERSION_1_5_414 MAKE_EXE_VERSION(1, 5, 414) // 0x010519E0 beta - why so many releases all at once +#define RUNTIME_VERSION_1_5_416 MAKE_EXE_VERSION(1, 5, 416) // 0x01051A00 release - why so many releases all at once +#define RUNTIME_VERSION_1_6_0 MAKE_EXE_VERSION(1, 6, 0) // 0x01060000 beta +#define RUNTIME_VERSION_1_6_3 MAKE_EXE_VERSION(1, 6, 3) // 0x01060030 beta - promoted to release +#define RUNTIME_VERSION_1_6_9 MAKE_EXE_VERSION(1, 6, 9) // 0x01060090 release - no interim beta +#define RUNTIME_VERSION_1_7_7 MAKE_EXE_VERSION(1, 7, 7) // 0x01070070 beta +#define RUNTIME_VERSION_1_7_9 MAKE_EXE_VERSION(1, 7, 9) // 0x01070090 beta - promoted to release +#define RUNTIME_VERSION_1_7_10 MAKE_EXE_VERSION(1, 7, 10) // 0x010700A0 release - no interim beta +#define RUNTIME_VERSION_1_7_12 MAKE_EXE_VERSION(1, 7, 12) // 0x010700C0 release - no interim beta, wtf bethesda +#define RUNTIME_VERSION_1_7_15 MAKE_EXE_VERSION(1, 7, 15) // 0x010700F0 release - no interim beta, released on a holiday weekend, wtf bethesda +#define RUNTIME_VERSION_1_7_19 MAKE_EXE_VERSION(1, 7, 19) // 0x01070130 release - rolled back +#define RUNTIME_VERSION_1_7_22 MAKE_EXE_VERSION(1, 7, 22) // 0x01070160 release - bugfix for 1.7.19 +#define RUNTIME_VERSION_1_8_7 MAKE_EXE_VERSION(1, 8, 7) // 0x01080070 release - poking at the built-in mod manager +#define RUNTIME_VERSION_1_9_4 MAKE_EXE_VERSION(1, 9, 4) // 0x01090040 release - high-resolution texture pack +#define RUNTIME_VERSION_1_10_20 MAKE_EXE_VERSION(1, 10, 20) // 0x010A0140 beta/release - creation club +#define RUNTIME_VERSION_1_10_26 MAKE_EXE_VERSION(1, 10, 26) // 0x010A01A0 creation club update 2 +#define RUNTIME_VERSION_1_10_40 MAKE_EXE_VERSION(1, 10, 40) // 0x010A0280 creation club update 3 (thanks for cleaning up plugin identification) +#define RUNTIME_VERSION_1_10_50 MAKE_EXE_VERSION(1, 10, 50) // 0x010A0320 creation club update 4 +#define RUNTIME_VERSION_1_10_64 MAKE_EXE_VERSION(1, 10, 64) // 0x010A0400 creation club update 5 +#define RUNTIME_VERSION_1_10_75 MAKE_EXE_VERSION(1, 10, 75) // 0x010A04B0 creation club update 6 +#define RUNTIME_VERSION_1_10_82 MAKE_EXE_VERSION(1, 10, 82) // 0x010A0520 creation club update 7 (startup speed?) +#define RUNTIME_VERSION_1_10_89 MAKE_EXE_VERSION(1, 10, 89) // 0x010A0590 creation club update 8 +#define RUNTIME_VERSION_1_10_98 MAKE_EXE_VERSION(1, 10, 98) // 0x010A0620 creation club update 9 +#define RUNTIME_VERSION_1_10_106 MAKE_EXE_VERSION(1, 10, 106) // 0x010A06A0 creation club update 10 (no addresses changed) +#define RUNTIME_VERSION_1_10_111 MAKE_EXE_VERSION(1, 10, 111) // 0x010A06F0 creation club update 11 (no addresses changed) +#define RUNTIME_VERSION_1_10_114 MAKE_EXE_VERSION(1, 10, 114) // 0x010A0720 creation club update 12 (no addresses changed) +#define RUNTIME_VERSION_1_10_120 MAKE_EXE_VERSION(1, 10, 120) // 0x010A0780 creation club update 13 (no addresses changed) +#define RUNTIME_VERSION_1_10_130 MAKE_EXE_VERSION(1, 10, 130) // 0x010A0820 creation club update 14 +#define RUNTIME_VERSION_1_10_138 MAKE_EXE_VERSION(1, 10, 138) // 0x010A08A0 creation club update 15 +#define RUNTIME_VERSION_1_10_162 MAKE_EXE_VERSION(1, 10, 162) // 0x010A0A20 creation club update 16 +#define RUNTIME_VERSION_1_10_163 MAKE_EXE_VERSION(1, 10, 163) // 0x010A0A30 creation club update 17 + +#define PACKED_F4SE_VERSION MAKE_EXE_VERSION(F4SE_VERSION_INTEGER, F4SE_VERSION_INTEGER_MINOR, F4SE_VERSION_INTEGER_BETA) + +// information about the state of the game at the time of release +#define F4SE_TARGETING_BETA_VERSION 0 +#define CURRENT_RELEASE_RUNTIME RUNTIME_VERSION_1_10_163 +#define CURRENT_RELEASE_F4SE_STR "0.6.20" + +#endif /* __F4SE_VERSION_H__ */ diff --git a/f4se/f4se_common/f4se_version.rc b/f4se/f4se_common/f4se_version.rc new file mode 100644 index 0000000..481f9d2 --- /dev/null +++ b/f4se/f4se_common/f4se_version.rc @@ -0,0 +1,32 @@ +#include "f4se_version.h" + +1 VERSIONINFO + FILEVERSION 0,F4SE_VERSION_INTEGER,F4SE_VERSION_INTEGER_MINOR,F4SE_VERSION_INTEGER_BETA + PRODUCTVERSION 0,F4SE_VERSION_INTEGER,F4SE_VERSION_INTEGER_MINOR,F4SE_VERSION_INTEGER_BETA + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "A component of the Fallout 4 Script Extender" + VALUE "FileVersion", F4SE_VERSION_VERSTRING + VALUE "InternalName", "F4SE" + VALUE "LegalCopyright", "Copyright (C) 2006-2019" + VALUE "ProductName", "F4SE" + VALUE "ProductVersion", F4SE_VERSION_VERSTRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/f4se/f4se_readme.txt b/f4se/f4se_readme.txt new file mode 100644 index 0000000..d3cc759 --- /dev/null +++ b/f4se/f4se_readme.txt @@ -0,0 +1,55 @@ +Fallout 4 Script Extender v0.6.20 +by Ian Patterson, Stephen Abel, and Brendan Borthwick (ianpatt, behippo, and plb) + +The Fallout 4 Script Extender, or F4SE for short, is a modder's resource that expands the scripting capabilities of Fallout 4. It does so without modifying the executable files on disk, so there are no permanent side effects. + +Compatibility: + +F4SE will support the latest version of Fallout available on Steam, and _only_ this version. When a new version is released, we'll update as soon as possible: please be patient. Don't email asking when the update will be ready; we already know the new version is out. The editor does not currently need modification, however when available a custom set of .pex/psc files must be installed. + +This version is compatible with runtime 1.10.163. You can ignore any part of the version number after those components; they're not relevant for compatibility purposes. + +[ Installation ] + +1. Copy f4se_1_10_163.dll, f4se_loader.exe, and f4se_steam_loader.dll to your Fallout installation folder. This is usually C:\Program Files (x86)\Steam\SteamApps\common\Fallout 4\, but if you have installed to a custom Steam library then you will need to find this folder yourself. Copy the Data folder over as well. + +2. Launch the game by running the copy of f4se_loader.exe that you placed in the Fallout installation folder. + +3. If you are looking for information about a specific feature, check f4se_whatsnew.txt for more details. + +[ FAQ ] + +* Console version? + - No. Not possible due to policy restrictions put in place by the console manufacturers. + +* My virus scanner complains about f4se_loader. Is it a virus? + - No. DLL injection sets off badly-written virus scanners. Nothing we can do about it. + +* Can I redistribute or make modified versions of F4SE? + - No. The suggested method for extending F4SE is to write a plugin. If this does not meet your needs, please email the contact addresses listed below. + +* How do I write a plugin for F4SE? + - There is no example plugin yet, so you'll need to look at PluginAPI.h for details on the high-level interface. The API is not currently locked down due to the early state of the project, so anything may change in later versions. Note that plugins must have their source code publicly available. + +* How do I uninstall F4SE? + - Delete the three files you copied to your Fallout installation folder. + +* How much does this cost? + - F4SE is free. + +[ Contact ] + +If you are having trouble with the installation instructions or are running in to other problems, post on /r/f4se. We cannot help you solve load order problems. + +Entire Team +Send email to team [at] f4se [dot] silverlock [dot] org + +Ian (ianpatt) +Send email to ianpatt+f4se [at] gmail [dot] com + +Stephen (behippo) +Send email to gamer [at] silverlock [dot] org + +[ Standard Disclaimer ] + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/f4se/f4se_whatsnew.txt b/f4se/f4se_whatsnew.txt new file mode 100644 index 0000000..1f82c1e --- /dev/null +++ b/f4se/f4se_whatsnew.txt @@ -0,0 +1,389 @@ +0.6.20 +- support for runtime 1.10.163 + +0.6.19 +- fix serialization of light mod list + +0.6.18 +- support for runtime 1.10.162 +- send kMessage_NewGame on new game (thanks to HLP for porting from skse64) + +0.6.17 +- support for runtime 1.10.138 + +0.6.16 +- support for runtime 1.10.130 +- Game.UpdateThirdPerson, Game.GetCameraState (based on code from Neanka, thanks) +- fix Actor.GetWornItemMods + +0.6.15 +- fix crash when loading cosaves with light mod list entries + +0.6.14 +- support for runtime 1.10.120 + +0.6.13 +- support for runtime 1.10.114 + +0.6.12 +- support for runtime 1.10.111 + +0.6.11 +- support for runtime 1.10.106 +- added InstanceData.Get/SetAddAmmoList +- fixed nullptr crash with material swaps with no material name + +0.6.10 +- support for runtime 1.10.98 +- added GetExternalEventRegistrations to Papyrus Plugin API + - Returns all scripts and callbackName registered with F4SE's RegisterForExternalEvent +- added Form functions + - Get/SetRaceForm + - GetSlotMask + - AddSlotToMask + - RemoveSlotFromMask +- added Armor.GetArmorAddons +- added ArmorAddon class + - GetAdditionalRaces + +0.6.9 +- fixes UI.Load deadlock + +0.6.8 +- support for runtime 1.10.89 +- changed locking implementations to BSReadWriteLock +- added Scaleform functions to load dds images + - MountImage(menuName, pathToMount, mountName) + - UnmountImage(menuName, pathToUnmount) + - Image is loaded through URLRequest("img://mountName") +- added Scaleform receiving function + - function onF4SEObjCreated(codeObject:Object):void + - This function is called on the menu root document as well as first-level children + +0.6.7 +- support for runtime 1.10.82 +- added ObjectReference.Get/SetMaterialSwap +- fixed ObjectReference.ApplyMaterialSwap crash + +0.6.6 +- support for runtime 1.10.75 +- fixed SimpleLock implementation (again) + +0.6.5 +- support for runtime 1.10.64 +- fixed bug with SimpleLock implementation +- added MatSwap class and MatSwap.Get/SetRemapData +- added ObjectReference.ApplyMaterialSwap +- added UI functions + - IsMenuRegistered + - RegisterCustomMenu + - Open/CloseMenu + +0.6.4 +- support for runtime 1.10.50 + +0.6.3 +- fixed compiling against outdated script files (ObjectReference was missing ClearFromOldLocations) + +0.6.2 +- fixed crash in ObjectReference::AttachWireLatent +- fixed input handler + +0.6.1 +- support for runtime 1.10.40 + +0.6.0 +- support for runtime 1.10.26 + +0.5.1 +- Fixed bug with F4SE based inventory-safe functions +- Fixed crash when passing None structs to native functions taking structs as args +- Fixed incorrect definitions to ActorValueInfo +- Added UI.IsMenuOpen +- InstanceData functions will now create InstanceData where applicable (e.g. target equipped item has no mods at all) +- Added InstanceData.Get/SetKeywords +- Fixed Scaleform function AllowTextInput +- Added missing two control mappings to CustomControlMap.txt +- ObjectReference.GetDisplayName is now inventory safe + +0.5.0 +- Added support for Light plugins +- Added Game.GetInstalledLightPlugins +- Update for new game version + +0.4.2 +- Added DefaultObject class and functions: + - Get/Set + - GetDefaultObject +- Added ObjectReference functions: + - GetInventoryWeight +- Fixed Input binding +- Fixed Perk.IsEligible +- Added Math functions: + - Exp +- Added Location functions: + - SetParent +- Added Actor functions: + - GetFurnitureReference +- Added ScriptObject functions: + - Un/registerForFurnitureEvent + - OnFurnitureEvent(Actor akActor, ObjectReference akFurniture, bool isGettingUp) +- Added ConstructibleObject functions: + - Get/SetConstructibleComponents + - Get/SetCreatedObject + - Get/SetCreatedCount + - Get/SetPriority + - Get/SetWorkbenchKeyword +- Added Component functions: + - Get/SetScrapItem + - Get/SetScrapScalar +- Added MiscObject functions: + - Get/SetMiscComponents + +0.4.1 +- Fixed bug when passing Script objects to Scaleform +- Added ObjectReference Functions: + - TransmitConnectedPower + - GetConnectPoints + +0.4.0 +- InstanceData Get/SetDamageTypes now supports both Armor and Weapon instances +- Added InstanceData functions: + - Get/SetWeight - supports Weapon and Armor instances + - Get/SetGoldValue - supports Weapon and Armor instances +- Added ObjectReference Functions: + - GetInventoryItems + +0.3.2 +- Fixed Scaleform SendExternalEvent and CallFunctionNoWait +- Fixed UI.Invoke when Args > 0 +- Added UI.Load (see UI.psc for example usage) +- Added Scaleform GetDirectoryListing + - e.g. root.f4se.GetDirectoryListing("Data/Somewhere", "*.ini", recursive=false) + result: Array=[{"nativePath": "Data/Somewhere/File.ini", "name": "File.ini", "lastModified": Date(), "creationDate": Date(), "isDirectory" : false, "isHidden": false0}] +- Added Location Functions: + -GetParent + -Get/SetEncounterZone +- Added EncounterZone Functions: + -Get/SetLocation + -Get/SetRank + -Get/SetMinLevel + -Get/SetMaxLevel + -Set/IsNeverResetable + -Set/IsWorkshop +- InstanceData functions can now accept ObjectReferences as the Form +- ObjectReference.GetAllMods is now inventory safe +- Added Game Functions: + -SetGameSettingInt/Float/Bool/String + +0.3.1 +- Fixed script unique event registration +- Added template serialization of VM for Latent functions +- Added object serialization interface +- Added Scaleform to Papyrus Adapters +- Added Scaleform functions: + -AllowTextInput + -SendExternalEvent + -CallFunctionNoWait +- Added UI Functions: + -Set/Get/Invoke +- Added ScriptObject Functions: + -Un/RegisterForExternalEvent + -Un/RegisterForCameraState +- Added ObjectReference Functions: + -GetConnectedObjects + -AttachWire (Creates a disabled wire) + -CreateWire (non-native to Create and enable) + -GetDisplayName +- Added Utility Functions: + -VarToVarArray + -VarArrayToVar +- Added Actor Functions: + -QueueUpdate + -GetInstanceOwner +- Added ActorBase Functions: + -Get/SetBodyWeight +- Added Form functions: + -Set/GetName + -Set/GetWeight + -SetGoldValue + -GetKeywords + -HasWorldModel + -Set/GetWorldModelPath + -Set/GetIconPath + -Set/GetMessageIconPath + -Set/GetEnchantment + -Set/GetEnchantmentValue + -Set/GetEquipType + -GetDescription +- Added Weapon functions: + -Set/GetEmbeddedMod + -GetInstanceOwner +- Added new InstanceData global class functions: + -Set/GetAttackDamage + -Set/GetDamageTypes + -Set/GetAmmoCapacity + -Set/GetAccuracyBonus + -Set/GetActionPointCost + -Set/GetAttackDelay + -Set/GetOutOfRangeMultiplier + -Set/GetReloadSpeed + -Set/GetReach + -Set/GetMinRange + -Set/GetMaxRange + -Set/GetSpeed + -Set/GetStagger + -Set/GetMeleeSpeed + -Set/GetSkill + -Set/GetResist + -Set/GetCritMultiplier + -Set/GetCritChargeBonus + -Set/GetProjectileOverride + -Set/GetNumProjectiles + -Set/GetSightedTransition +- Added ObjectMod functions: + -GetPropertyModifiers + -Set/GetMaxRank + -Set/GetPriority + -GetLooseMod +- Added Cell functions: + -GetWaterType +- Added WaterType functions: + -Set/GetConsumeSpell + -Set/GetContactSpell +- Added Perk functions: + -IsPlayable + -IsHidden + -GetLevel + -GetNumRanks + -GetNextPerk + -GetSWFPath + -IsEligible +- Added Scaleform Translation Injection via Interface/Translations/ModName_en.txt + +0.3.0 +- support for runtime 1.9.4 +- input event hook +- event sending +- ScriptObject.Register/UnregisterForKey/Control +- enabled papyrus API for plugins + +0.2.8 +- support for runtime 1.8.7 + +0.2.7 +- support for runtime 1.7.22 + +0.2.6 +- support for runtime 1.7.19 + +0.2.5 +- support for runtime 1.7.15 +- fixed papyrus complex parameter type setup +- internal papyrus function calls + +0.2.4 +- support for runtime 1.7.12 + +0.2.3 +- support for runtime 1.7.10 + +0.2.2 +- fixed a crash on savegame load + +0.2.1 +- support for runtime 1.7.9 +- plugin serialization interface +- plugin delete message + +0.2.0 +- support for runtime 1.6.9 +- alpha papyrus support (see scripts for details) + +0.1.19 +- support for beta runtime 1.6.3 +- papyrus mostly done, plugin support currently disabled until it's more tested + +0.1.18 +- support for runtime 1.5.416 + +0.1.17 +- support for beta runtime 1.5.412 +- improved version check messages (hopefully) + +0.1.16 +- support for runtime 1.5.307 + +0.1.15 +- support for beta runtime 1.5.210 +- more internal decoding +- start of papyrus, 1.5.210 came out at a bad time + +0.1.14 +- support for beta runtime 1.5.205 + +0.1.13 +- support for beta runtime 1.5.157 + +0.1.12 +- support for beta runtime 1.5.154 + +0.1.11: +- support for beta runtime 1.5.151 + +0.1.10: +- support for beta runtime 1.5.147 +- DataLoaded, PreSaveGame/PostSaveGame/PreLoadGame/PostLoadGame plugin messages +- more internal class decoding + +0.1.9: +- support for beta runtime 1.5.141 +- more internal class/global/etc decoding +- exposed scaleform registration interface + +0.1.8: +- support for runtime 1.4.132 +- more internal class decoding + +0.1.7: +- support for beta runtime 1.4.131 +- more internal class decoding + +0.1.6: +- support for beta runtime 1.4.125 +- internal tools updates +- more internal class decoding (inventory, input, netimmerse skin) + +0.1.5: +- support for beta runtime 1.3.47 +- initial game menu support and utilities +- more internal class decoding + +0.1.4: +- support for beta runtime 1.3.45 +- enable logging from scaleform. add bEnableGFXLog=1 to the [Interface] section of your f4se.ini. create it if it doesn't exist. +- hook global scaleform tint functions to allow interface mods to selectively modify tint colors/intensities +- initial scaleform native plugin API +- more internal class decoding + +0.1.3: +- support for beta runtime 1.2.37 + +0.1.2: +- support for beta runtime 1.2.33 +- more internal class decoding + +0.1.1: +- fixed crash when resetting keybindings from ui + +0.1.0: +- everything +- plugin manager +- simple hooks for papyrus, scaleform, and the classic script system, but nothing useful for modders yet +- customization of internal keymappings + Copy CustomControlMap.txt to Data\F4SE\CustomControlMap.txt. + Edit that file to set your bindings. The format is the same as Skyrim, it's a space-separated file. + The first column is the name of the internal keybind. The second column is the keyboard scan code that should activate the bind in hex, or 0xFF if unbound. Use google to find a table of scan codes. The third column is for mice, the fourth for controllers. The next three columns control whether or not a control should be written to the user's config file. The final column specifies which input layer the bind is associated with - you will probably not want to change that. +- currently-installed version of F4SE is shown in the Settings menu next to the game version +- running GetF4SEVersion from the console will print the current version as well +- log spam is written to My Documents\My Games\Fallout4\F4SE\ diff --git a/f4se/xbyak/COPYRIGHT b/f4se/xbyak/COPYRIGHT new file mode 100644 index 0000000..66b6ea5 --- /dev/null +++ b/f4se/xbyak/COPYRIGHT @@ -0,0 +1,47 @@ + +Copyright (c) 2007 MITSUNARI Shigeo +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +Neither the name of the copyright owner nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満た +す場合に限り、再頒布および使用が許可されます。 + +ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項 +を含めること。 +バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作 +権表示、本条件一覧、および下記免責条項を含めること。 +書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進 +に、著作権者の名前またはコントリビューターの名前を使用してはならない。 +本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供さ +れており、明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性 +に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。 +著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを +問わず、かつ責任の根拠が契約であるか厳格責任であるか(過失その他の)不法行為で +あるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、 +本ソフトウェアの使用によって発生した(代替品または代用サービスの調達、使用の +喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない)直接 +損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、 +一切責任を負わないものとします。 diff --git a/f4se/xbyak/xbyak.h b/f4se/xbyak/xbyak.h new file mode 100644 index 0000000..f8835e6 --- /dev/null +++ b/f4se/xbyak/xbyak.h @@ -0,0 +1,2234 @@ +#pragma once +#ifndef XBYAK_XBYAK_H_ +#define XBYAK_XBYAK_H_ +/*! + @file xbyak.h + @brief Xbyak ; JIT assembler for x86(IA32)/x64 by C++ + @author herumi + @url https://github.com/herumi/xbyak, http://homepage1.nifty.com/herumi/soft/xbyak_e.html + @note modified new BSD license + http://opensource.org/licenses/BSD-3-Clause +*/ +#ifndef XBYAK_NO_OP_NAMES + #if not +0 // trick to detect whether 'not' is operator or not + #error "use -fno-operator-names option if you want to use and(), or(), xor(), not() as function names, Or define XBYAK_NO_OP_NAMES and use and_(), or_(), xor_(), not_()." + #endif +#endif + +#include // for debug print +#include +#include +#include +#include +#ifndef NDEBUG +#include +#endif + +//#define XBYAK_USE_MMAP_ALLOCATOR +#if !defined(__GNUC__) || defined(__MINGW32__) + #undef XBYAK_USE_MMAP_ALLOCATOR +#endif + +// This covers -std=(gnu|c)++(0x|11|1y), -stdlib=libc++, and modern Microsoft. +#if ((defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(_LIBCPP_VERSION) ||\ + ((__cplusplus >= 201103) || defined(__GXX_EXPERIMENTAL_CXX0X__))) + #include + #define XBYAK_STD_UNORDERED_MAP std::unordered_map + #define XBYAK_STD_UNORDERED_MULTIMAP std::unordered_multimap + +// Clang/llvm-gcc and ICC-EDG in 'GCC-mode' always claim to be GCC 4.2, using +// libstdcxx 20070719 (from GCC 4.2.1, the last GPL 2 version). +// These headers have been expanded/fixed in various forks. +// In F.S.F. 'real' GCC, issues with the tr headers were resolved in GCC 4.5. +#elif defined(__GNUC__) && (__GNUC__ >= 4) && ((__GNUC_MINOR__ >= 5) || \ + ((__GLIBCXX__ >= 20070719) && (__GNUC_MINOR__ >= 2) && \ + (defined(__INTEL_COMPILER) || defined(__llvm__)))) + #include + #define XBYAK_STD_UNORDERED_MAP std::tr1::unordered_map + #define XBYAK_STD_UNORDERED_MULTIMAP std::tr1::unordered_multimap + +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) && (_MSC_VER < 1600) + #include + #define XBYAK_STD_UNORDERED_MAP std::tr1::unordered_map + #define XBYAK_STD_UNORDERED_MULTIMAP std::tr1::unordered_multimap + +#else + #include + #define XBYAK_STD_UNORDERED_MAP std::map + #define XBYAK_STD_UNORDERED_MULTIMAP std::multimap +#endif +#ifdef _WIN32 + #include + #include + #include +#elif defined(__GNUC__) + #include + #include + #include +#endif +#if !defined(_MSC_VER) || (_MSC_VER >= 1600) + #include +#endif + +#if defined(_WIN64) || defined(__MINGW64__) || (defined(__CYGWIN__) && defined(__x86_64__)) + #define XBYAK64_WIN +#elif defined(__x86_64__) + #define XBYAK64_GCC +#endif +#if !defined(XBYAK64) && !defined(XBYAK32) + #if defined(XBYAK64_GCC) || defined(XBYAK64_WIN) + #define XBYAK64 + #else + #define XBYAK32 + #endif +#endif + +#if (__cplusplus >= 201103) || (_MSC_VER >= 1800) + #define XBYAK_VARIADIC_TEMPLATE +#endif + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4514) /* remove inline function */ + #pragma warning(disable : 4786) /* identifier is too long */ + #pragma warning(disable : 4503) /* name is too long */ + #pragma warning(disable : 4127) /* constant expresison */ +#endif + +namespace Xbyak { + +#include "xbyak_bin2hex.h" + +enum { + DEFAULT_MAX_CODE_SIZE = 4096, + VERSION = 0x4870 /* 0xABCD = A.BC(D) */ +}; + +#ifndef MIE_INTEGER_TYPE_DEFINED +#define MIE_INTEGER_TYPE_DEFINED +#ifdef _MSC_VER + typedef unsigned __int64 uint64; + typedef __int64 sint64; +#else + typedef uint64_t uint64; + typedef int64_t sint64; +#endif +typedef unsigned int uint32; +typedef unsigned short uint16; +typedef unsigned char uint8; +#endif + +#ifndef MIE_ALIGN + #ifdef _MSC_VER + #define MIE_ALIGN(x) __declspec(align(x)) + #else + #define MIE_ALIGN(x) __attribute__((aligned(x))) + #endif +#endif +#ifndef MIE_PACK // for shufps + #define MIE_PACK(x, y, z, w) ((x) * 64 + (y) * 16 + (z) * 4 + (w)) +#endif + +enum { + ERR_NONE = 0, + ERR_BAD_ADDRESSING, + ERR_CODE_IS_TOO_BIG, + ERR_BAD_SCALE, + ERR_ESP_CANT_BE_INDEX, + ERR_BAD_COMBINATION, + ERR_BAD_SIZE_OF_REGISTER, + ERR_IMM_IS_TOO_BIG, + ERR_BAD_ALIGN, + ERR_LABEL_IS_REDEFINED, + ERR_LABEL_IS_TOO_FAR, + ERR_LABEL_IS_NOT_FOUND, + ERR_CODE_ISNOT_COPYABLE, + ERR_BAD_PARAMETER, + ERR_CANT_PROTECT, + ERR_CANT_USE_64BIT_DISP, + ERR_OFFSET_IS_TOO_BIG, + ERR_MEM_SIZE_IS_NOT_SPECIFIED, + ERR_BAD_MEM_SIZE, + ERR_BAD_ST_COMBINATION, + ERR_OVER_LOCAL_LABEL, // not used + ERR_UNDER_LOCAL_LABEL, + ERR_CANT_ALLOC, + ERR_ONLY_T_NEAR_IS_SUPPORTED_IN_AUTO_GROW, + ERR_BAD_PROTECT_MODE, + ERR_BAD_PNUM, + ERR_BAD_TNUM, + ERR_BAD_VSIB_ADDRESSING, + ERR_CANT_CONVERT, + ERR_LABEL_ISNOT_SET_BY_L, + ERR_LABEL_IS_ALREADY_SET_BY_L, + ERR_BAD_LABEL_STR, + ERR_MUNMAP, + ERR_INTERNAL +}; + +class Error : public std::exception { + int err_; +public: + explicit Error(int err) : err_(err) + { + if (err_ < 0 || err_ > ERR_INTERNAL) { + fprintf(stderr, "bad err=%d in Xbyak::Error\n", err_); + exit(1); + } + } + operator int() const { return err_; } + const char *what() const throw() + { + static const char *errTbl[] = { + "none", + "bad addressing", + "code is too big", + "bad scale", + "esp can't be index", + "bad combination", + "bad size of register", + "imm is too big", + "bad align", + "label is redefined", + "label is too far", + "label is not found", + "code is not copyable", + "bad parameter", + "can't protect", + "can't use 64bit disp(use (void*))", + "offset is too big", + "MEM size is not specified", + "bad mem size", + "bad st combination", + "over local label", + "under local label", + "can't alloc", + "T_SHORT is not supported in AutoGrow", + "bad protect mode", + "bad pNum", + "bad tNum", + "bad vsib addressing", + "can't convert", + "label is not set by L()", + "label is already set by L()", + "bad label string", + "err munmap", + "internal error", + }; + assert((size_t)err_ < sizeof(errTbl) / sizeof(*errTbl)); + return errTbl[err_]; + } +}; + +inline const char *ConvertErrorToString(Error err) +{ + return err.what(); +} + +inline void *AlignedMalloc(size_t size, size_t alignment) +{ +#ifdef __MINGW32__ + return __mingw_aligned_malloc(size, alignment); +#elif defined(_WIN32) + return _aligned_malloc(size, alignment); +#else + void *p; + int ret = posix_memalign(&p, alignment, size); + return (ret == 0) ? p : 0; +#endif +} + +inline void AlignedFree(void *p) +{ +#ifdef __MINGW32__ + __mingw_aligned_free(p); +#elif defined(_MSC_VER) + _aligned_free(p); +#else + free(p); +#endif +} + +template +inline const To CastTo(From p) throw() +{ + return (const To)(size_t)(p); +} +namespace inner { + +static const size_t ALIGN_PAGE_SIZE = 4096; + +inline bool IsInDisp8(uint32 x) { return 0xFFFFFF80 <= x || x <= 0x7F; } +inline bool IsInInt32(uint64 x) { return ~uint64(0x7fffffffu) <= x || x <= 0x7FFFFFFFU; } + +inline uint32 VerifyInInt32(uint64 x) +{ +#ifdef XBYAK64 + if (!IsInInt32(x)) throw Error(ERR_OFFSET_IS_TOO_BIG); +#endif + return static_cast(x); +} + +enum LabelMode { + LasIs, // as is + Labs, // absolute + LaddTop // (addr + top) for mov(reg, label) with AutoGrow +}; + +} // inner + +/* + custom allocator +*/ +struct Allocator { + virtual uint8 *alloc(size_t size) { return reinterpret_cast(AlignedMalloc(size, inner::ALIGN_PAGE_SIZE)); } + virtual void free(uint8 *p) { AlignedFree(p); } + virtual ~Allocator() {} + /* override to return false if you call protect() manually */ + virtual bool useProtect() const { return true; } +}; + +#ifdef XBYAK_USE_MMAP_ALLOCATOR +class MmapAllocator : Allocator { + typedef XBYAK_STD_UNORDERED_MAP SizeList; + SizeList sizeList_; +public: + uint8 *alloc(size_t size) + { + const size_t alignedSizeM1 = inner::ALIGN_PAGE_SIZE - 1; + size = (size + alignedSizeM1) & ~alignedSizeM1; +#ifdef MAP_ANONYMOUS + const int mode = MAP_PRIVATE | MAP_ANONYMOUS; +#elif defined(MAP_ANON) + const int mode = MAP_PRIVATE | MAP_ANON; +#else + #error "not supported" +#endif + void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, mode, -1, 0); + if (p == MAP_FAILED) throw Error(ERR_CANT_ALLOC); + assert(p); + sizeList_[(uintptr_t)p] = size; + return (uint8*)p; + } + void free(uint8 *p) + { + if (p == 0) return; + SizeList::iterator i = sizeList_.find((uintptr_t)p); + if (i == sizeList_.end()) throw Error(ERR_BAD_PARAMETER); + if (munmap((void*)i->first, i->second) < 0) throw Error(ERR_MUNMAP); + sizeList_.erase(i); + } +}; +#endif + +class Operand { +private: + uint8 idx_; // 0..15, MSB = 1 if spl/bpl/sil/dil + uint8 kind_; + uint16 bit_; +public: + enum Kind { + NONE = 0, + MEM = 1 << 1, + IMM = 1 << 2, + REG = 1 << 3, + MMX = 1 << 4, + XMM = 1 << 5, + FPU = 1 << 6, + YMM = 1 << 7 + }; + enum Code { +#ifdef XBYAK64 + RAX = 0, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15, + R8D = 8, R9D, R10D, R11D, R12D, R13D, R14D, R15D, + R8W = 8, R9W, R10W, R11W, R12W, R13W, R14W, R15W, + R8B = 8, R9B, R10B, R11B, R12B, R13B, R14B, R15B, + SPL = 4, BPL, SIL, DIL, +#endif + EAX = 0, ECX, EDX, EBX, ESP, EBP, ESI, EDI, + AX = 0, CX, DX, BX, SP, BP, SI, DI, + AL = 0, CL, DL, BL, AH, CH, DH, BH + }; + Operand() : idx_(0), kind_(0), bit_(0) { } + Operand(int idx, Kind kind, int bit, bool ext8bit = 0) + : idx_(static_cast(idx | (ext8bit ? 0x80 : 0))) + , kind_(static_cast(kind)) + , bit_(static_cast(bit)) + { + assert((bit_ & (bit_ - 1)) == 0); // bit must be power of two + } + Kind getKind() const { return static_cast(kind_); } + int getIdx() const { return idx_ & 15; } + bool isNone() const { return kind_ == 0; } + bool isMMX() const { return is(MMX); } + bool isXMM() const { return is(XMM); } + bool isYMM() const { return is(YMM); } + bool isREG(int bit = 0) const { return is(REG, bit); } + bool isMEM(int bit = 0) const { return is(MEM, bit); } + bool isFPU() const { return is(FPU); } + bool isExt8bit() const { return (idx_ & 0x80) != 0; } + // ah, ch, dh, bh? + bool isHigh8bit() const + { + if (!isBit(8)) return false; + if (isExt8bit()) return false; + const int idx = getIdx(); + return AH <= idx && idx <= BH; + } + // any bit is accetable if bit == 0 + bool is(int kind, uint32 bit = 0) const + { + return (kind_ & kind) && (bit == 0 || (bit_ & bit)); // cf. you can set (8|16) + } + bool isBit(uint32 bit) const { return (bit_ & bit) != 0; } + uint32 getBit() const { return bit_; } + const char *toString() const + { + const int idx = getIdx(); + if (kind_ == REG) { + if (isExt8bit()) { + static const char *tbl[4] = { "spl", "bpl", "sil", "dil" }; + return tbl[idx - 4]; + } + static const char *tbl[4][16] = { + { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, + { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }, + { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }, + { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }, + }; + return tbl[bit_ == 8 ? 0 : bit_ == 16 ? 1 : bit_ == 32 ? 2 : 3][idx]; + } else if (isYMM()) { + static const char *tbl[16] = { "ym0", "ym1", "ym2", "ym3", "ym4", "ym5", "ym6", "ym7", "ym8", "ym9", "ym10", "ym11", "ym12", "ym13", "ym14", "ym15" }; + return tbl[idx]; + } else if (isXMM()) { + static const char *tbl[16] = { "xm0", "xm1", "xm2", "xm3", "xm4", "xm5", "xm6", "xm7", "xm8", "xm9", "xm10", "xm11", "xm12", "xm13", "xm14", "xm15" }; + return tbl[idx]; + } else if (isMMX()) { + static const char *tbl[8] = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }; + return tbl[idx]; + } else if (isFPU()) { + static const char *tbl[8] = { "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7" }; + return tbl[idx]; + } + throw Error(ERR_INTERNAL); + } + bool isSameNotInherited(const Operand& rhs) const { return idx_ == rhs.idx_ && kind_ == rhs.kind_ && bit_ == rhs.bit_; } + bool operator==(const Operand& rhs) const; + bool operator!=(const Operand& rhs) const { return !operator==(rhs); } +}; + +class Label; + +struct Reg8; +struct Reg16; +struct Reg32; +#ifdef XBYAK64 +struct Reg64; +#endif +class Reg : public Operand { + bool hasRex() const { return isExt8bit() | isREG(64) | isExtIdx(); } +public: + Reg() { } + Reg(int idx, Kind kind, int bit = 0, bool ext8bit = false) : Operand(idx, kind, bit, ext8bit) { } + Reg changeBit(int bit) const { return Reg(getIdx(), getKind(), bit, isExt8bit()); } + bool isExtIdx() const { return getIdx() > 7; } + uint8 getRex(const Reg& base = Reg()) const + { + return (hasRex() || base.hasRex()) ? uint8(0x40 | ((isREG(64) | base.isREG(64)) ? 8 : 0) | (isExtIdx() ? 4 : 0)| (base.isExtIdx() ? 1 : 0)) : 0; + } + Reg8 cvt8() const; + Reg16 cvt16() const; + Reg32 cvt32() const; +#ifdef XBYAK64 + Reg64 cvt64() const; +#endif +}; + +struct Reg8 : public Reg { + explicit Reg8(int idx = 0, bool ext8bit = false) : Reg(idx, Operand::REG, 8, ext8bit) { } +}; + +struct Reg16 : public Reg { + explicit Reg16(int idx = 0) : Reg(idx, Operand::REG, 16) { } +}; + +struct Mmx : public Reg { + explicit Mmx(int idx = 0, Kind kind = Operand::MMX, int bit = 64) : Reg(idx, kind, bit) { } +}; + +struct Xmm : public Mmx { + explicit Xmm(int idx = 0, Kind kind = Operand::XMM, int bit = 128) : Mmx(idx, kind, bit) { } +}; + +struct Ymm : public Xmm { + explicit Ymm(int idx = 0) : Xmm(idx, Operand::YMM, 256) { } +}; + +struct Fpu : public Reg { + explicit Fpu(int idx = 0) : Reg(idx, Operand::FPU, 32) { } +}; + +struct Reg32e : public Reg { + explicit Reg32e(int idx, int bit) : Reg(idx, Operand::REG, bit) {} +}; +struct Reg32 : public Reg32e { + explicit Reg32(int idx = 0) : Reg32e(idx, 32) {} +}; +#ifdef XBYAK64 +struct Reg64 : public Reg32e { + explicit Reg64(int idx = 0) : Reg32e(idx, 64) {} +}; +struct RegRip { + sint64 disp_; + Label* label_; + explicit RegRip(sint64 disp = 0, Label* label = 0) : disp_(disp), label_(label) {} + friend const RegRip operator+(const RegRip& r, sint64 disp) { + return RegRip(r.disp_ + disp, r.label_); + } + friend const RegRip operator-(const RegRip& r, sint64 disp) { + return RegRip(r.disp_ - disp, r.label_); + } + friend const RegRip operator+(const RegRip& r, Label& label) { + if (r.label_) throw Error(ERR_BAD_ADDRESSING); + return RegRip(r.disp_, &label); + } +}; +#endif + +inline Reg8 Reg::cvt8() const +{ + const int idx = getIdx(); + if (isBit(8)) return Reg8(idx, isExt8bit()); +#ifdef XBYAK32 + if (idx >= 4) throw Error(ERR_CANT_CONVERT); +#endif + return Reg8(idx, 4 <= idx && idx < 8); +} + +inline Reg16 Reg::cvt16() const +{ + const int idx = getIdx(); + if (isBit(8) && (4 <= idx && idx < 8) && !isExt8bit()) throw Error(ERR_CANT_CONVERT); + return Reg16(idx); +} + +inline Reg32 Reg::cvt32() const +{ + const int idx = getIdx(); + if (isBit(8) && (4 <= idx && idx < 8) && !isExt8bit()) throw Error(ERR_CANT_CONVERT); + return Reg32(idx); +} + +#ifdef XBYAK64 +inline Reg64 Reg::cvt64() const +{ + const int idx = getIdx(); + if (isBit(8) && (4 <= idx && idx < 8) && !isExt8bit()) throw Error(ERR_CANT_CONVERT); + return Reg64(idx); +} +#endif + +#ifndef XBYAK_DISABLE_SEGMENT +// not derived from Reg +class Segment { + int idx_; +public: + enum { + es, cs, ss, ds, fs, gs + }; + Segment(int idx) : idx_(idx) { assert(0 <= idx_ && idx_ < 6); } + int getIdx() const { return idx_; } + const char *toString() const + { + static const char tbl[][3] = { + "es", "cs", "ss", "ds", "fs", "gs" + }; + return tbl[idx_]; + } +}; +#endif + +class RegExp { +public: + struct SReg { + uint16 bit:9; // 32/64/128/256 none if 0 + uint16 idx:7; + SReg() : bit(0), idx(0) { } + void set(const Reg& r) { this->bit = uint16(r.getBit()); this->idx = uint16(r.getIdx()); } + bool operator==(const SReg& rhs) const { return bit == rhs.bit && idx == rhs.idx; } + }; + RegExp(size_t disp = 0) : disp_(disp), scale_(0) { } + RegExp(const Reg& r, int scale = 1) + : disp_(0) + , scale_(scale) + { + if (!r.is(Reg::REG, 32|64) && !r.is(Reg::XMM|Reg::YMM)) throw Error(ERR_BAD_SIZE_OF_REGISTER); + if (scale != 1 && scale != 2 && scale != 4 && scale != 8) throw Error(ERR_BAD_SCALE); + if (r.getBit() >= 128 || scale != 1) { // xmm/ymm is always index + index_.set(r); + } else { + base_.set(r); + } + } + bool isVsib() const { return index_.bit >= 128; } + bool isYMM() const { return index_.bit >= 256; } + RegExp optimize() const // select smaller size + { + // [reg * 2] => [reg + reg] + if (!isVsib() && !base_.bit && index_.bit && scale_ == 2) { + RegExp ret = *this; + ret.base_ = index_; + ret.scale_ = 1; + return ret; + } + return *this; + } + bool operator==(const RegExp& rhs) const + { + return base_ == rhs.base_ && index_ == rhs.index_ && disp_ == rhs.disp_; + } + const SReg& getBase() const { return base_; } + const SReg& getIndex() const { return index_; } + int getScale() const { return scale_; } + uint32 getDisp() const { return uint32(disp_); } + void verify() const + { + if (base_.bit >= 128) throw Error(ERR_BAD_SIZE_OF_REGISTER); + if (index_.bit && index_.bit <= 64) { + if (index_.idx == Operand::ESP) throw Error(ERR_ESP_CANT_BE_INDEX); + if (base_.bit && base_.bit != index_.bit) throw Error(ERR_BAD_SIZE_OF_REGISTER); + } + } +private: + friend RegExp operator+(const RegExp& a, const RegExp& b); + friend RegExp operator-(const RegExp& e, size_t disp); + /* + [base_ + index_ * scale_ + disp_] + base : Reg32e, index : Reg32e(w/o esp), Xmm, Ymm + */ + size_t disp_; + int scale_; + SReg base_; + SReg index_; +}; + +inline RegExp operator+(const RegExp& a, const RegExp& b) +{ + if (a.index_.bit && b.index_.bit) throw Error(ERR_BAD_ADDRESSING); + RegExp ret = a; + if (!ret.index_.bit) { ret.index_ = b.index_; ret.scale_ = b.scale_; } + if (b.base_.bit) { + if (ret.base_.bit) { + if (ret.index_.bit) throw Error(ERR_BAD_ADDRESSING); + // base + base => base + index * 1 + ret.index_ = b.base_; + // [reg + esp] => [esp + reg] + if (ret.index_.idx == Operand::ESP) std::swap(ret.base_, ret.index_); + ret.scale_ = 1; + } else { + ret.base_ = b.base_; + } + } + ret.disp_ += b.disp_; + return ret; +} +inline RegExp operator*(const Reg& r, int scale) +{ + return RegExp(r, scale); +} +inline RegExp operator-(const RegExp& e, size_t disp) +{ + RegExp ret = e; + ret.disp_ -= disp; + return ret; +} + +// 2nd parameter for constructor of CodeArray(maxSize, userPtr, alloc) +void *const AutoGrow = (void*)1; + +class CodeArray { + enum Type { + USER_BUF = 1, // use userPtr(non alignment, non protect) + ALLOC_BUF, // use new(alignment, protect) + AUTO_GROW // automatically move and grow memory if necessary + }; + CodeArray(const CodeArray& rhs); + void operator=(const CodeArray&); + bool isAllocType() const { return type_ == ALLOC_BUF || type_ == AUTO_GROW; } + struct AddrInfo { + size_t codeOffset; // position to write + size_t jmpAddr; // value to write + int jmpSize; // size of jmpAddr + inner::LabelMode mode; + AddrInfo(size_t _codeOffset, size_t _jmpAddr, int _jmpSize, inner::LabelMode _mode) + : codeOffset(_codeOffset), jmpAddr(_jmpAddr), jmpSize(_jmpSize), mode(_mode) {} + uint64 getVal(const uint8 *top) const + { + uint64 disp = (mode == inner::LaddTop) ? jmpAddr + size_t(top) : (mode == inner::LasIs) ? jmpAddr : jmpAddr - size_t(top); + if (jmpSize == 4) disp = inner::VerifyInInt32(disp); + return disp; + } + }; + typedef std::list AddrInfoList; + AddrInfoList addrInfoList_; + const Type type_; +#ifdef XBYAK_USE_MMAP_ALLOCATOR + MmapAllocator defaultAllocator_; +#else + Allocator defaultAllocator_; +#endif + Allocator *alloc_; +protected: + size_t maxSize_; + uint8 *top_; + size_t size_; + + /* + allocate new memory and copy old data to the new area + */ + void growMemory() + { + const size_t newSize = (std::max)(DEFAULT_MAX_CODE_SIZE, maxSize_ * 2); + uint8 *newTop = alloc_->alloc(newSize); + if (newTop == 0) throw Error(ERR_CANT_ALLOC); + for (size_t i = 0; i < size_; i++) newTop[i] = top_[i]; + alloc_->free(top_); + top_ = newTop; + maxSize_ = newSize; + } + /* + calc jmp address for AutoGrow mode + */ + void calcJmpAddress() + { + for (AddrInfoList::const_iterator i = addrInfoList_.begin(), ie = addrInfoList_.end(); i != ie; ++i) { + uint64 disp = i->getVal(top_); + rewrite(i->codeOffset, disp, i->jmpSize); + } + if (alloc_->useProtect() && !protect(top_, size_, true)) throw Error(ERR_CANT_PROTECT); + } +public: + explicit CodeArray(size_t maxSize, void *userPtr = 0, Allocator *allocator = 0) + : type_(userPtr == AutoGrow ? AUTO_GROW : userPtr ? USER_BUF : ALLOC_BUF) + , alloc_(allocator ? allocator : (Allocator*)&defaultAllocator_) + , maxSize_(maxSize) + , top_(type_ == USER_BUF ? reinterpret_cast(userPtr) : alloc_->alloc((std::max)(maxSize, 1))) + , size_(0) + { + if (maxSize_ > 0 && top_ == 0) throw Error(ERR_CANT_ALLOC); + if ((type_ == ALLOC_BUF && alloc_->useProtect()) && !protect(top_, maxSize, true)) { + alloc_->free(top_); + throw Error(ERR_CANT_PROTECT); + } + } + virtual ~CodeArray() + { + if (isAllocType()) { + if (alloc_->useProtect()) protect(top_, maxSize_, false); + alloc_->free(top_); + } + } + void resetSize() + { + size_ = 0; + addrInfoList_.clear(); + } + void db(int code) + { + if (size_ >= maxSize_) { + if (type_ == AUTO_GROW) { + growMemory(); + } else { + throw Error(ERR_CODE_IS_TOO_BIG); + } + } + top_[size_++] = static_cast(code); + } + void db(const uint8 *code, int codeSize) + { + for (int i = 0; i < codeSize; i++) db(code[i]); + } + void db(uint64 code, int codeSize) + { + if (codeSize > 8) throw Error(ERR_BAD_PARAMETER); + for (int i = 0; i < codeSize; i++) db(static_cast(code >> (i * 8))); + } + void dw(uint32 code) { db(code, 2); } + void dd(uint32 code) { db(code, 4); } + void dq(uint64 code) { db(code, 8); } + const uint8 *getCode() const { return top_; } + template + const F getCode() const { return CastTo(top_); } + const uint8 *getCurr() const { return &top_[size_]; } + template + const F getCurr() const { return CastTo(&top_[size_]); } + size_t getSize() const { return size_; } + void setSize(size_t size) + { + if (size > maxSize_) throw Error(ERR_OFFSET_IS_TOO_BIG); + size_ = size; + } + void dump() const + { + const uint8 *p = getCode(); + size_t bufSize = getSize(); + size_t remain = bufSize; + for (int i = 0; i < 4; i++) { + size_t disp = 16; + if (remain < 16) { + disp = remain; + } + for (size_t j = 0; j < 16; j++) { + if (j < disp) { + printf("%02X", p[i * 16 + j]); + } + } + putchar('\n'); + remain -= disp; + if (remain <= 0) { + break; + } + } + } + /* + @param offset [in] offset from top + @param disp [in] offset from the next of jmp + @param size [in] write size(1, 2, 4, 8) + */ + void rewrite(size_t offset, uint64 disp, size_t size) + { + assert(offset < maxSize_); + if (size != 1 && size != 2 && size != 4 && size != 8) throw Error(ERR_BAD_PARAMETER); + uint8 *const data = top_ + offset; + for (size_t i = 0; i < size; i++) { + data[i] = static_cast(disp >> (i * 8)); + } + } + void save(size_t offset, size_t val, int size, inner::LabelMode mode) + { + addrInfoList_.push_back(AddrInfo(offset, val, size, mode)); + } + bool isAutoGrow() const { return type_ == AUTO_GROW; } + /** + change exec permission of memory + @param addr [in] buffer address + @param size [in] buffer size + @param canExec [in] true(enable to exec), false(disable to exec) + @return true(success), false(failure) + */ + static inline bool protect(const void *addr, size_t size, bool canExec) + { +#if defined(_WIN32) + DWORD oldProtect; + return VirtualProtect(const_cast(addr), size, canExec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &oldProtect) != 0; +#elif defined(__GNUC__) + size_t pageSize = sysconf(_SC_PAGESIZE); + size_t iaddr = reinterpret_cast(addr); + size_t roundAddr = iaddr & ~(pageSize - static_cast(1)); + int mode = PROT_READ | PROT_WRITE | (canExec ? PROT_EXEC : 0); + return mprotect(reinterpret_cast(roundAddr), size + (iaddr - roundAddr), mode) == 0; +#else + return true; +#endif + } + /** + get aligned memory pointer + @param addr [in] address + @param alingedSize [in] power of two + @return aligned addr by alingedSize + */ + static inline uint8 *getAlignedAddress(uint8 *addr, size_t alignedSize = 16) + { + return reinterpret_cast((reinterpret_cast(addr) + alignedSize - 1) & ~(alignedSize - static_cast(1))); + } +}; + +class Address : public Operand { + mutable uint8 top_[6]; // 6 = 1(ModRM) + 1(SIB) + 4(disp) + uint8 size_; + uint8 rex_; + size_t disp_; + const Label* label_; + bool isOnlyDisp_; + bool is64bitDisp_; + bool is32bit_; + mutable bool isVsib_; + bool isYMM_; + void verify() const { if (isVsib_) throw Error(ERR_BAD_VSIB_ADDRESSING); } +public: + Address(uint32 sizeBit, bool isOnlyDisp, size_t disp, bool is32bit, bool is64bitDisp = false, bool isVsib = false, bool isYMM = false) + : Operand(0, MEM, sizeBit) + , size_(0) + , rex_(0) + , disp_(disp) + , label_(0) + , isOnlyDisp_(isOnlyDisp) + , is64bitDisp_(is64bitDisp) + , is32bit_(is32bit) + , isVsib_(isVsib) + , isYMM_(isYMM) + { + } + void db(int code) + { + if (size_ >= sizeof(top_)) throw Error(ERR_CODE_IS_TOO_BIG); + top_[size_++] = static_cast(code); + } + void dd(uint32 code) { for (int i = 0; i < 4; i++) db(code >> (i * 8)); } + const uint8 *getCode() const { return top_; } + size_t getSize() const { return size_; } + void updateRegField(uint8 regIdx) const + { + *top_ = (*top_ & B11000111) | ((regIdx << 3) & B00111000); + } + void setVsib(bool isVsib) const { isVsib_ = isVsib; } + bool isVsib() const { return isVsib_; } + bool isYMM() const { return isYMM_; } + bool is32bit() const { verify(); return is32bit_; } + bool isOnlyDisp() const { verify(); return isOnlyDisp_; } // for mov eax + size_t getDisp() const { verify(); return disp_; } + uint8 getRex() const { verify(); return rex_; } + bool is64bitDisp() const { verify(); return is64bitDisp_; } // for moffset + void setRex(uint8 rex) { rex_ = rex; } + void setLabel(const Label* label) { label_ = label; } + const Label* getLabel() const { return label_; } + bool operator==(const Address& rhs) const + { + return getBit() == rhs.getBit() && size_ == rhs.size_ && rex_ == rhs.rex_ && disp_ == rhs.disp_ && label_ == rhs.label_ && isOnlyDisp_ == rhs.isOnlyDisp_ + && is64bitDisp_ == rhs.is64bitDisp_ && is32bit_ == rhs.is32bit_ && isVsib_ == rhs.isVsib_ && isYMM_ == rhs.isYMM_; + } + bool operator!=(const Address& rhs) const { return !operator==(rhs); } +}; + +inline bool Operand::operator==(const Operand& rhs) const +{ + if (isMEM() && rhs.isMEM()) return static_cast(*this) == static_cast(rhs); + return isSameNotInherited(rhs); +} + +class AddressFrame { +private: + void operator=(const AddressFrame&); + Address makeAddress(const RegExp& e) const + { + e.verify(); + const bool isVsib = e.isVsib(); + const bool isYMM = e.isYMM(); + const RegExp::SReg& base = e.getBase(); + const RegExp::SReg& index = e.getIndex(); + const uint32 disp = e.getDisp(); + Address frame(bit_, (!base.bit && !index.bit), disp, base.bit == 32 || index.bit == 32, false, isVsib, isYMM); + enum { + mod00 = 0, mod01 = 1, mod10 = 2 + }; + int mod; + if (!base.bit || ((base.idx & 7) != Operand::EBP && disp == 0)) { + mod = mod00; + } else if (inner::IsInDisp8(disp)) { + mod = mod01; + } else { + mod = mod10; + } + const int baseIdx = base.bit ? (base.idx & 7) : Operand::EBP; + /* ModR/M = [2:3:3] = [Mod:reg/code:R/M] */ + bool hasSIB = index.bit || (base.idx & 7) == Operand::ESP; +#ifdef XBYAK64 + if (!base.bit && !index.bit) hasSIB = true; +#endif + if (hasSIB) { + frame.db((mod << 6) | Operand::ESP); + /* SIB = [2:3:3] = [SS:index:base(=rm)] */ + const int indexIdx = index.bit ? (index.idx & 7) : Operand::ESP; + const int scale = e.getScale(); + const int ss = (scale == 8) ? 3 : (scale == 4) ? 2 : (scale == 2) ? 1 : 0; + frame.db((ss << 6) | (indexIdx << 3) | baseIdx); + } else { + frame.db((mod << 6) | baseIdx); + } + if (mod == mod01) { + frame.db(disp); + } else if (mod == mod10 || (mod == mod00 && !base.bit)) { + frame.dd(disp); + } + int rex = ((index.idx >> 3) << 1) | (base.idx >> 3); + if (rex) rex |= 0x40; + frame.setRex(uint8(rex)); + return frame; + } +public: + const uint32 bit_; + explicit AddressFrame(uint32 bit) : bit_(bit) { } + Address operator[](const void *disp) const + { + size_t adr = reinterpret_cast(disp); +#ifdef XBYAK64 + if (adr > 0xFFFFFFFFU) throw Error(ERR_OFFSET_IS_TOO_BIG); +#endif + RegExp e(static_cast(adr)); + return operator[](e); + } +#ifdef XBYAK64 + Address operator[](uint64 disp) const + { + return Address(64, true, disp, false, true); + } + Address operator[](const RegRip& addr) const + { + Address frame(bit_, true, addr.disp_, false); + frame.db(0x05); + if (addr.label_) { + frame.setLabel(addr.label_); + } else { + frame.dd(inner::VerifyInInt32(addr.disp_)); + } + return frame; + } +#endif + Address operator[](const RegExp& e) const + { + return makeAddress(e.optimize()); + } +}; + +struct JmpLabel { + size_t endOfJmp; /* offset from top to the end address of jmp */ + int jmpSize; + inner::LabelMode mode; + size_t disp; // disp for [rip + disp] + explicit JmpLabel(size_t endOfJmp = 0, int jmpSize = 0, inner::LabelMode mode = inner::LasIs, size_t disp = 0) + : endOfJmp(endOfJmp), jmpSize(jmpSize), mode(mode), disp(disp) + { + } +}; + +class LabelManager; + +class Label { + mutable LabelManager *mgr; + mutable int id; + friend class LabelManager; +public: + Label() : mgr(0), id(0) {} + Label(const Label& rhs); + Label& operator=(const Label& rhs); + ~Label(); + int getId() const { return id; } + + // backward compatibility + static std::string toStr(int num) + { + char buf[16]; +#ifdef _MSC_VER + _snprintf_s +#else + snprintf +#endif + (buf, sizeof(buf), ".%08x", num); + return buf; + } +}; + +class LabelManager { + // for string label + struct SlabelVal { + size_t offset; + SlabelVal(size_t offset) : offset(offset) {} + }; + typedef XBYAK_STD_UNORDERED_MAP SlabelDefList; + typedef XBYAK_STD_UNORDERED_MULTIMAP SlabelUndefList; + struct SlabelState { + SlabelDefList defList; + SlabelUndefList undefList; + }; + typedef std::list StateList; + // for Label class + struct ClabelVal { + ClabelVal(size_t offset = 0) : offset(offset), refCount(1) {} + size_t offset; + int refCount; + }; + typedef XBYAK_STD_UNORDERED_MAP ClabelDefList; + typedef XBYAK_STD_UNORDERED_MULTIMAP ClabelUndefList; + + CodeArray *base_; + // global : stateList_.front(), local : stateList_.back() + StateList stateList_; + mutable int labelId_; + ClabelDefList clabelDefList_; + ClabelUndefList clabelUndefList_; + + int getId(const Label& label) const + { + if (label.id == 0) label.id = labelId_++; + return label.id; + } + template + void define_inner(DefList& defList, UndefList& undefList, const T& labelId, size_t addrOffset) + { + // add label + typename DefList::value_type item(labelId, addrOffset); + std::pair ret = defList.insert(item); + if (!ret.second) throw Error(ERR_LABEL_IS_REDEFINED); + // search undefined label + for (;;) { + typename UndefList::iterator itr = undefList.find(labelId); + if (itr == undefList.end()) break; + const JmpLabel *jmp = &itr->second; + const size_t offset = jmp->endOfJmp - jmp->jmpSize; + size_t disp; + if (jmp->mode == inner::LaddTop) { + disp = addrOffset; + } else if (jmp->mode == inner::Labs) { + disp = size_t(base_->getCurr()); + } else { + disp = addrOffset - jmp->endOfJmp + jmp->disp; +#ifdef XBYAK64 + if (jmp->jmpSize <= 4 && !inner::IsInInt32(disp)) throw Error(ERR_OFFSET_IS_TOO_BIG); +#endif + if (jmp->jmpSize == 1 && !inner::IsInDisp8((uint32)disp)) throw Error(ERR_LABEL_IS_TOO_FAR); + } + if (base_->isAutoGrow()) { + base_->save(offset, disp, jmp->jmpSize, jmp->mode); + } else { + base_->rewrite(offset, disp, jmp->jmpSize); + } + undefList.erase(itr); + } + } + template + bool getOffset_inner(const DefList& defList, size_t *offset, const T& label) const + { + typename DefList::const_iterator i = defList.find(label); + if (i == defList.end()) return false; + *offset = i->second.offset; + return true; + } + friend class Label; + void incRefCount(int id) { clabelDefList_[id].refCount++; } + void decRefCount(int id) + { + ClabelDefList::iterator i = clabelDefList_.find(id); + if (i == clabelDefList_.end()) return; + if (i->second.refCount == 1) { + clabelDefList_.erase(id); + } else { + --i->second.refCount; + } + } + template + bool hasUndefinedLabel_inner(const T& list) const + { +#ifndef NDEBUG + for (typename T::const_iterator i = list.begin(); i != list.end(); ++i) { + std::cerr << "undefined label:" << i->first << std::endl; + } +#endif + return !list.empty(); + } +public: + LabelManager() + { + reset(); + } + void reset() + { + base_ = 0; + labelId_ = 1; + stateList_.clear(); + stateList_.push_back(SlabelState()); + stateList_.push_back(SlabelState()); + clabelDefList_.clear(); + clabelUndefList_.clear(); + } + void enterLocal() + { + stateList_.push_back(SlabelState()); + } + void leaveLocal() + { + if (stateList_.size() <= 2) throw Error(ERR_UNDER_LOCAL_LABEL); + if (hasUndefinedLabel_inner(stateList_.back().undefList)) throw Error(ERR_LABEL_IS_NOT_FOUND); + stateList_.pop_back(); + } + void set(CodeArray *base) { base_ = base; } + void defineSlabel(std::string label) + { + if (label == "@b" || label == "@f") throw Error(ERR_BAD_LABEL_STR); + if (label == "@@") { + SlabelDefList& defList = stateList_.front().defList; + SlabelDefList::iterator i = defList.find("@f"); + if (i != defList.end()) { + defList.erase(i); + label = "@b"; + } else { + i = defList.find("@b"); + if (i != defList.end()) { + defList.erase(i); + } + label = "@f"; + } + } + SlabelState& st = *label.c_str() == '.' ? stateList_.back() : stateList_.front(); + define_inner(st.defList, st.undefList, label, base_->getSize()); + } + void defineClabel(const Label& label) + { + define_inner(clabelDefList_, clabelUndefList_, getId(label), base_->getSize()); + label.mgr = this; + } + void assign(Label& dst, const Label& src) + { + ClabelDefList::const_iterator i = clabelDefList_.find(src.id); + if (i == clabelDefList_.end()) throw Error(ERR_LABEL_ISNOT_SET_BY_L); + define_inner(clabelDefList_, clabelUndefList_, dst.id, i->second.offset); + dst.mgr = this; + } + bool getOffset(size_t *offset, std::string& label) const + { + const SlabelDefList& defList = stateList_.front().defList; + if (label == "@b") { + if (defList.find("@f") != defList.end()) { + label = "@f"; + } else if (defList.find("@b") == defList.end()) { + throw Error(ERR_LABEL_IS_NOT_FOUND); + } + } else if (label == "@f") { + if (defList.find("@f") != defList.end()) { + label = "@b"; + } + } + const SlabelState& st = *label.c_str() == '.' ? stateList_.back() : stateList_.front(); + return getOffset_inner(st.defList, offset, label); + } + bool getOffset(size_t *offset, const Label& label) const + { + return getOffset_inner(clabelDefList_, offset, getId(label)); + } + void addUndefinedLabel(const std::string& label, const JmpLabel& jmp) + { + SlabelState& st = *label.c_str() == '.' ? stateList_.back() : stateList_.front(); + st.undefList.insert(SlabelUndefList::value_type(label, jmp)); + } + void addUndefinedLabel(const Label& label, const JmpLabel& jmp) + { + clabelUndefList_.insert(ClabelUndefList::value_type(label.id, jmp)); + } + bool hasUndefSlabel() const + { + for (StateList::const_iterator i = stateList_.begin(), ie = stateList_.end(); i != ie; ++i) { + if (hasUndefinedLabel_inner(i->undefList)) return true; + } + return false; + } + bool hasUndefClabel() const { return hasUndefinedLabel_inner(clabelUndefList_); } +}; + +inline Label::Label(const Label& rhs) +{ + id = rhs.id; + mgr = rhs.mgr; + if (mgr) mgr->incRefCount(id); +} +inline Label& Label::operator=(const Label& rhs) +{ + if (id) throw Error(ERR_LABEL_IS_ALREADY_SET_BY_L); + id = rhs.id; + mgr = rhs.mgr; + if (mgr) mgr->incRefCount(id); + return *this; +} +inline Label::~Label() +{ + if (id && mgr) mgr->decRefCount(id); +} + +class CodeGenerator : public CodeArray { +public: + enum LabelType { + T_SHORT, + T_NEAR, + T_AUTO // T_SHORT if possible + }; +private: + CodeGenerator operator=(const CodeGenerator&); // don't call +#ifdef XBYAK64 + enum { i32e = 32 | 64, BIT = 64 }; + static const size_t dummyAddr = (size_t(0x11223344) << 32) | 55667788; + typedef Reg64 NativeReg; +#else + enum { i32e = 32, BIT = 32 }; + static const size_t dummyAddr = 0x12345678; + typedef Reg32 NativeReg; +#endif + // (XMM, XMM|MEM) + static inline bool isXMM_XMMorMEM(const Operand& op1, const Operand& op2) + { + return op1.isXMM() && (op2.isXMM() || op2.isMEM()); + } + // (MMX, MMX|MEM) or (XMM, XMM|MEM) + static inline bool isXMMorMMX_MEM(const Operand& op1, const Operand& op2) + { + return (op1.isMMX() && (op2.isMMX() || op2.isMEM())) || isXMM_XMMorMEM(op1, op2); + } + // (XMM, MMX|MEM) + static inline bool isXMM_MMXorMEM(const Operand& op1, const Operand& op2) + { + return op1.isXMM() && (op2.isMMX() || op2.isMEM()); + } + // (MMX, XMM|MEM) + static inline bool isMMX_XMMorMEM(const Operand& op1, const Operand& op2) + { + return op1.isMMX() && (op2.isXMM() || op2.isMEM()); + } + // (XMM, REG32|MEM) + static inline bool isXMM_REG32orMEM(const Operand& op1, const Operand& op2) + { + return op1.isXMM() && (op2.isREG(i32e) || op2.isMEM()); + } + // (REG32, XMM|MEM) + static inline bool isREG32_XMMorMEM(const Operand& op1, const Operand& op2) + { + return op1.isREG(i32e) && (op2.isXMM() || op2.isMEM()); + } + // (REG32, REG32|MEM) + static inline bool isREG32_REG32orMEM(const Operand& op1, const Operand& op2) + { + return op1.isREG(i32e) && ((op2.isREG(i32e) && op1.getBit() == op2.getBit()) || op2.isMEM()); + } + void rex(const Operand& op1, const Operand& op2 = Operand()) + { + uint8 rex = 0; + const Operand *p1 = &op1, *p2 = &op2; + if (p1->isMEM()) std::swap(p1, p2); + if (p1->isMEM()) throw Error(ERR_BAD_COMBINATION); + if (p2->isMEM()) { + const Address& addr = static_cast(*p2); + if (BIT == 64 && addr.is32bit()) db(0x67); + rex = addr.getRex() | static_cast(*p1).getRex(); + } else { + // ModRM(reg, base); + rex = static_cast(op2).getRex(static_cast(op1)); + } + // except movsx(16bit, 32/64bit) + if ((op1.isBit(16) && !op2.isBit(i32e)) || (op2.isBit(16) && !op1.isBit(i32e))) db(0x66); + if (rex) db(rex); + } + enum AVXtype { + PP_NONE = 1 << 0, + PP_66 = 1 << 1, + PP_F3 = 1 << 2, + PP_F2 = 1 << 3, + MM_RESERVED = 1 << 4, + MM_0F = 1 << 5, + MM_0F38 = 1 << 6, + MM_0F3A = 1 << 7 + }; + void vex(bool r, int idx, bool is256, int type, bool x = false, bool b = false, int w = 1) + { + uint32 pp = (type & PP_66) ? 1 : (type & PP_F3) ? 2 : (type & PP_F2) ? 3 : 0; + uint32 vvvv = (((~idx) & 15) << 3) | (is256 ? 4 : 0) | pp; + if (!b && !x && !w && (type & MM_0F)) { + db(0xC5); db((r ? 0 : 0x80) | vvvv); + } else { + uint32 mmmm = (type & MM_0F) ? 1 : (type & MM_0F38) ? 2 : (type & MM_0F3A) ? 3 : 0; + db(0xC4); db((r ? 0 : 0x80) | (x ? 0 : 0x40) | (b ? 0 : 0x20) | mmmm); db((w << 7) | vvvv); + } + } + LabelManager labelMgr_; + bool isInDisp16(uint32 x) const { return 0xFFFF8000 <= x || x <= 0x7FFF; } + uint8 getModRM(int mod, int r1, int r2) const { return static_cast((mod << 6) | ((r1 & 7) << 3) | (r2 & 7)); } + void opModR(const Reg& reg1, const Reg& reg2, int code0, int code1 = NONE, int code2 = NONE) + { + rex(reg2, reg1); + db(code0 | (reg1.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); + db(getModRM(3, reg1.getIdx(), reg2.getIdx())); + } + void opModM(const Address& addr, const Reg& reg, int code0, int code1 = NONE, int code2 = NONE, int immSize = 0) + { + if (addr.is64bitDisp()) throw Error(ERR_CANT_USE_64BIT_DISP); + rex(addr, reg); + db(code0 | (reg.isBit(8) ? 0 : 1)); if (code1 != NONE) db(code1); if (code2 != NONE) db(code2); + addr.updateRegField(static_cast(reg.getIdx())); + opAddr(addr, immSize); + } + void makeJmp(uint32 disp, LabelType type, uint8 shortCode, uint8 longCode, uint8 longPref) + { + const int shortJmpSize = 2; + const int longHeaderSize = longPref ? 2 : 1; + const int longJmpSize = longHeaderSize + 4; + if (type != T_NEAR && inner::IsInDisp8(disp - shortJmpSize)) { + db(shortCode); db(disp - shortJmpSize); + } else { + if (type == T_SHORT) throw Error(ERR_LABEL_IS_TOO_FAR); + if (longPref) db(longPref); + db(longCode); dd(disp - longJmpSize); + } + } + template + void opJmp(T& label, LabelType type, uint8 shortCode, uint8 longCode, uint8 longPref) + { + if (isAutoGrow() && size_ + 16 >= maxSize_) growMemory(); /* avoid splitting code of jmp */ + size_t offset = 0; + if (labelMgr_.getOffset(&offset, label)) { /* label exists */ + makeJmp(inner::VerifyInInt32(offset - size_), type, shortCode, longCode, longPref); + } else { + int jmpSize = 0; + if (type == T_NEAR) { + jmpSize = 4; + if (longPref) db(longPref); + db(longCode); dd(0); + } else { + jmpSize = 1; + db(shortCode); db(0); + } + JmpLabel jmp(size_, jmpSize, inner::LasIs); + labelMgr_.addUndefinedLabel(label, jmp); + } + } + void opJmpAbs(const void *addr, LabelType type, uint8 shortCode, uint8 longCode) + { + if (isAutoGrow()) { + if (type != T_NEAR) throw Error(ERR_ONLY_T_NEAR_IS_SUPPORTED_IN_AUTO_GROW); + if (size_ + 16 >= maxSize_) growMemory(); + db(longCode); + dd(0); + save(size_ - 4, size_t(addr) - size_, 4, inner::Labs); + } else { + makeJmp(inner::VerifyInInt32(reinterpret_cast(addr) - getCurr()), type, shortCode, longCode, 0); + } + + } + // immSize is the size for immediate value + void opAddr(const Address &addr, int immSize = 0) + { + db(addr.getCode(), static_cast(addr.getSize())); + if (addr.getLabel()) { // [rip + Label] + putL_inner(*addr.getLabel(), true, addr.getDisp() - immSize); + } + } + /* preCode is for SSSE3/SSE4 */ + void opGen(const Operand& reg, const Operand& op, int code, int pref, bool isValid(const Operand&, const Operand&), int imm8 = NONE, int preCode = NONE) + { + if (isValid && !isValid(reg, op)) throw Error(ERR_BAD_COMBINATION); + if (pref != NONE) db(pref); + if (op.isMEM()) { + opModM(static_cast(op), static_cast(reg), 0x0F, preCode, code, (imm8 != NONE) ? 1 : 0); + } else { + opModR(static_cast(reg), static_cast(op), 0x0F, preCode, code); + } + if (imm8 != NONE) db(imm8); + } + void opMMX_IMM(const Mmx& mmx, int imm8, int code, int ext) + { + if (mmx.isXMM()) db(0x66); + opModR(Reg32(ext), mmx, 0x0F, code); + db(imm8); + } + void opMMX(const Mmx& mmx, const Operand& op, int code, int pref = 0x66, int imm8 = NONE, int preCode = NONE) + { + opGen(mmx, op, code, mmx.isXMM() ? pref : NONE, isXMMorMMX_MEM, imm8, preCode); + } + void opMovXMM(const Operand& op1, const Operand& op2, int code, int pref) + { + if (pref != NONE) db(pref); + if (op1.isXMM() && op2.isMEM()) { + opModM(static_cast(op2), static_cast(op1), 0x0F, code); + } else if (op1.isMEM() && op2.isXMM()) { + opModM(static_cast(op1), static_cast(op2), 0x0F, code | 1); + } else { + throw Error(ERR_BAD_COMBINATION); + } + } + void opExt(const Operand& op, const Mmx& mmx, int code, int imm, bool hasMMX2 = false) + { + if (hasMMX2 && op.isREG(i32e)) { /* pextrw is special */ + if (mmx.isXMM()) db(0x66); + opModR(static_cast(op), mmx, 0x0F, B11000101); db(imm); + } else { + opGen(mmx, op, code, 0x66, isXMM_REG32orMEM, imm, B00111010); + } + } + void opR_ModM(const Operand& op, int bit, int ext, int code0, int code1 = NONE, int code2 = NONE, bool disableRex = false, int immSize = 0) + { + int opBit = op.getBit(); + if (disableRex && opBit == 64) opBit = 32; + if (op.isREG(bit)) { + opModR(Reg(ext, Operand::REG, opBit), static_cast(op).changeBit(opBit), code0, code1, code2); + } else if (op.isMEM()) { + opModM(static_cast(op), Reg(ext, Operand::REG, opBit), code0, code1, code2, immSize); + } else { + throw Error(ERR_BAD_COMBINATION); + } + } + void opShift(const Operand& op, int imm, int ext) + { + verifyMemHasSize(op); + opR_ModM(op, 0, ext, (B11000000 | ((imm == 1 ? 1 : 0) << 4)), NONE, NONE, false, (imm != 1) ? 1 : 0); + if (imm != 1) db(imm); + } + void opShift(const Operand& op, const Reg8& _cl, int ext) + { + if (_cl.getIdx() != Operand::CL) throw Error(ERR_BAD_COMBINATION); + opR_ModM(op, 0, ext, B11010010); + } + void opModRM(const Operand& op1, const Operand& op2, bool condR, bool condM, int code0, int code1 = NONE, int code2 = NONE, int immSize = 0) + { + if (condR) { + opModR(static_cast(op1), static_cast(op2), code0, code1, code2); + } else if (condM) { + opModM(static_cast(op2), static_cast(op1), code0, code1, code2, immSize); + } else { + throw Error(ERR_BAD_COMBINATION); + } + } + void opShxd(const Operand& op, const Reg& reg, uint8 imm, int code, const Reg8 *_cl = 0) + { + if (_cl && _cl->getIdx() != Operand::CL) throw Error(ERR_BAD_COMBINATION); + opModRM(reg, op, (op.isREG(16 | i32e) && op.getBit() == reg.getBit()), op.isMEM() && (reg.isREG(16 | i32e)), 0x0F, code | (_cl ? 1 : 0), NONE, _cl ? 0 : 1); + if (!_cl) db(imm); + } + // (REG, REG|MEM), (MEM, REG) + void opRM_RM(const Operand& op1, const Operand& op2, int code) + { + if (op1.isREG() && op2.isMEM()) { + opModM(static_cast(op2), static_cast(op1), code | 2); + } else { + opModRM(op2, op1, op1.isREG() && op1.getKind() == op2.getKind(), op1.isMEM() && op2.isREG(), code); + } + } + // (REG|MEM, IMM) + void opRM_I(const Operand& op, uint32 imm, int code, int ext) + { + verifyMemHasSize(op); + uint32 immBit = inner::IsInDisp8(imm) ? 8 : isInDisp16(imm) ? 16 : 32; + if (op.isBit(8)) immBit = 8; + if (op.getBit() < immBit) throw Error(ERR_IMM_IS_TOO_BIG); + if (op.isBit(32|64) && immBit == 16) immBit = 32; /* don't use MEM16 if 32/64bit mode */ + if (op.isREG() && op.getIdx() == 0 && (op.getBit() == immBit || (op.isBit(64) && immBit == 32))) { // rax, eax, ax, al + rex(op); + db(code | 4 | (immBit == 8 ? 0 : 1)); + } else { + int tmp = immBit < (std::min)(op.getBit(), 32U) ? 2 : 0; + opR_ModM(op, 0, ext, B10000000 | tmp, NONE, NONE, false, immBit / 8); + } + db(imm, immBit / 8); + } + void opIncDec(const Operand& op, int code, int ext) + { + verifyMemHasSize(op); +#ifndef XBYAK64 + if (op.isREG() && !op.isBit(8)) { + rex(op); db(code | op.getIdx()); + return; + } +#endif + code = B11111110; + if (op.isREG()) { + opModR(Reg(ext, Operand::REG, op.getBit()), static_cast(op), code); + } else { + opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code); + } + } + void opPushPop(const Operand& op, int code, int ext, int alt) + { + if (op.isREG()) { + if (op.isBit(16)) db(0x66); + if (static_cast(op).getIdx() >= 8) db(0x41); + db(alt | (op.getIdx() & 7)); + } else if (op.isMEM()) { + opModM(static_cast(op), Reg(ext, Operand::REG, op.getBit()), code); + } else { + throw Error(ERR_BAD_COMBINATION); + } + } + void verifyMemHasSize(const Operand& op) const + { + if (op.isMEM() && op.getBit() == 0) throw Error(ERR_MEM_SIZE_IS_NOT_SPECIFIED); + } + void opMovxx(const Reg& reg, const Operand& op, uint8 code) + { + if (op.isBit(32)) throw Error(ERR_BAD_COMBINATION); + int w = op.isBit(16); +#ifdef XBYAK64 + if (op.isHigh8bit()) throw Error(ERR_BAD_COMBINATION); +#endif + bool cond = reg.isREG() && (reg.getBit() > op.getBit()); + opModRM(reg, op, cond && op.isREG(), cond && op.isMEM(), 0x0F, code | w); + } + void opFpuMem(const Address& addr, uint8 m16, uint8 m32, uint8 m64, uint8 ext, uint8 m64ext) + { + if (addr.is64bitDisp()) throw Error(ERR_CANT_USE_64BIT_DISP); + uint8 code = addr.isBit(16) ? m16 : addr.isBit(32) ? m32 : addr.isBit(64) ? m64 : 0; + if (!code) throw Error(ERR_BAD_MEM_SIZE); + if (m64ext && addr.isBit(64)) ext = m64ext; + + rex(addr, st0); + db(code); + addr.updateRegField(ext); + opAddr(addr); + } + // use code1 if reg1 == st0 + // use code2 if reg1 != st0 && reg2 == st0 + void opFpuFpu(const Fpu& reg1, const Fpu& reg2, uint32 code1, uint32 code2) + { + uint32 code = reg1.getIdx() == 0 ? code1 : reg2.getIdx() == 0 ? code2 : 0; + if (!code) throw Error(ERR_BAD_ST_COMBINATION); + db(uint8(code >> 8)); + db(uint8(code | (reg1.getIdx() | reg2.getIdx()))); + } + void opFpu(const Fpu& reg, uint8 code1, uint8 code2) + { + db(code1); db(code2 | reg.getIdx()); + } + void opVex(const Reg& r, const Operand *p1, const Operand *p2, int type, int code, int w, int imm8 = NONE) + { + bool x, b; + if (p2->isMEM()) { + const Address& addr = static_cast(*p2); + uint8 rex = addr.getRex(); + x = (rex & 2) != 0; + b = (rex & 1) != 0; + if (BIT == 64 && addr.is32bit()) db(0x67); + if (BIT == 64 && w == -1) w = (rex & 4) ? 1 : 0; + } else { + x = false; + b = static_cast(*p2).isExtIdx(); + } + if (w == -1) w = 0; + vex(r.isExtIdx(), p1 ? p1->getIdx() : 0, r.isYMM(), type, x, b, w); + db(code); + if (p2->isMEM()) { + const Address& addr = static_cast(*p2); + addr.updateRegField(static_cast(r.getIdx())); + opAddr(addr, (imm8 != NONE) ? 1 : 0); + } else { + db(getModRM(3, r.getIdx(), p2->getIdx())); + } + if (imm8 != NONE) db(imm8); + } + // (r, r, r/m) if isR_R_RM + // (r, r/m, r) + void opGpr(const Reg32e& r, const Operand& op1, const Operand& op2, int type, uint8 code, bool isR_R_RM, int imm8 = NONE) + { + const Operand *p1 = &op1; + const Operand *p2 = &op2; + if (!isR_R_RM) std::swap(p1, p2); + const unsigned int bit = r.getBit(); + if (p1->getBit() != bit || (p2->isREG() && p2->getBit() != bit)) throw Error(ERR_BAD_COMBINATION); + int w = bit == 64; + opVex(r, p1, p2, type, code, w, imm8); + } + void opAVX_X_X_XM(const Xmm& x1, const Operand& op1, const Operand& op2, int type, int code0, bool supportYMM, int w = -1, int imm8 = NONE) + { + const Xmm *x2; + const Operand *op; + if (op2.isNone()) { + x2 = &x1; + op = &op1; + } else { + if (!(op1.isXMM() || (supportYMM && op1.isYMM()))) throw Error(ERR_BAD_COMBINATION); + x2 = static_cast(&op1); + op = &op2; + } + // (x1, x2, op) + if (!((x1.isXMM() && x2->isXMM()) || (supportYMM && x1.isYMM() && x2->isYMM()))) throw Error(ERR_BAD_COMBINATION); + opVex(x1, x2, op, type, code0, w, imm8); + } + // if cvt then return pointer to Xmm(idx) (or Ymm(idx)), otherwise return op + void opAVX_X_X_XMcvt(const Xmm& x1, const Operand& op1, const Operand& op2, bool cvt, Operand::Kind kind, int type, int code0, bool supportYMM, int w = -1, int imm8 = NONE) + { + // use static_cast to avoid calling unintentional copy constructor on gcc + opAVX_X_X_XM(x1, op1, cvt ? kind == Operand::XMM ? static_cast(Xmm(op2.getIdx())) : static_cast(Ymm(op2.getIdx())) : op2, type, code0, supportYMM, w, imm8); + } + // support (x, x/m, imm), (y, y/m, imm) + void opAVX_X_XM_IMM(const Xmm& x, const Operand& op, int type, int code, bool supportYMM, int w = -1, int imm8 = NONE) + { + opAVX_X_X_XM(x, x.isXMM() ? xm0 : ym0, op, type, code, supportYMM, w, imm8); + } + // QQQ:need to refactor + void opSp1(const Reg& reg, const Operand& op, uint8 pref, uint8 code0, uint8 code1) + { + if (reg.isBit(8)) throw Error(ERR_BAD_SIZE_OF_REGISTER); + bool is16bit = reg.isREG(16) && (op.isREG(16) || op.isMEM()); + if (!is16bit && !(reg.isREG(i32e) && (op.isREG(reg.getBit()) || op.isMEM()))) throw Error(ERR_BAD_COMBINATION); + if (is16bit) db(0x66); + db(pref); opModRM(reg.changeBit(i32e == 32 ? 32 : reg.getBit()), op, op.isREG(), true, code0, code1); + } + void opGather(const Xmm& x1, const Address& addr, const Xmm& x2, int type, uint8 code, int w, int mode) + { + if (!addr.isVsib()) throw Error(ERR_BAD_VSIB_ADDRESSING); + const int y_vx_y = 0; + const int y_vy_y = 1; +// const int x_vy_x = 2; + const bool isAddrYMM = addr.isYMM(); + if (!x1.isXMM() || isAddrYMM || !x2.isXMM()) { + bool isOK = false; + if (mode == y_vx_y) { + isOK = x1.isYMM() && !isAddrYMM && x2.isYMM(); + } else if (mode == y_vy_y) { + isOK = x1.isYMM() && isAddrYMM && x2.isYMM(); + } else { // x_vy_x + isOK = !x1.isYMM() && isAddrYMM && !x2.isYMM(); + } + if (!isOK) throw Error(ERR_BAD_VSIB_ADDRESSING); + } + addr.setVsib(false); + opAVX_X_X_XM(isAddrYMM ? Ymm(x1.getIdx()) : x1, isAddrYMM ? Ymm(x2.getIdx()) : x2, addr, type, code, true, w); + addr.setVsib(true); + } +public: + unsigned int getVersion() const { return VERSION; } + using CodeArray::db; + const Mmx mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7; + const Xmm xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7; + const Ymm ymm0, ymm1, ymm2, ymm3, ymm4, ymm5, ymm6, ymm7; + const Xmm &xm0, &xm1, &xm2, &xm3, &xm4, &xm5, &xm6, &xm7; + const Ymm &ym0, &ym1, &ym2, &ym3, &ym4, &ym5, &ym6, &ym7; + const Reg32 eax, ecx, edx, ebx, esp, ebp, esi, edi; + const Reg16 ax, cx, dx, bx, sp, bp, si, di; + const Reg8 al, cl, dl, bl, ah, ch, dh, bh; + const AddressFrame ptr, byte, word, dword, qword; + const Fpu st0, st1, st2, st3, st4, st5, st6, st7; +#ifdef XBYAK64 + const Reg64 rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, r8, r9, r10, r11, r12, r13, r14, r15; + const Reg32 r8d, r9d, r10d, r11d, r12d, r13d, r14d, r15d; + const Reg16 r8w, r9w, r10w, r11w, r12w, r13w, r14w, r15w; + const Reg8 r8b, r9b, r10b, r11b, r12b, r13b, r14b, r15b; + const Reg8 spl, bpl, sil, dil; + const Xmm xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15; + const Ymm ymm8, ymm9, ymm10, ymm11, ymm12, ymm13, ymm14, ymm15; + const Xmm &xm8, &xm9, &xm10, &xm11, &xm12, &xm13, &xm14, &xm15; // for my convenience + const Ymm &ym8, &ym9, &ym10, &ym11, &ym12, &ym13, &ym14, &ym15; + const RegRip rip; +#endif +#ifndef XBYAK_DISABLE_SEGMENT + const Segment es, cs, ss, ds, fs, gs; +#endif + void L(const std::string& label) { labelMgr_.defineSlabel(label); } + void L(const Label& label) { labelMgr_.defineClabel(label); } + /* + assign src to dst + require + dst : does not used by L() + src : used by L() + */ + void assignL(Label& dst, const Label& src) { labelMgr_.assign(dst, src); } + void inLocalLabel() { labelMgr_.enterLocal(); } + void outLocalLabel() { labelMgr_.leaveLocal(); } + void jmp(std::string label, LabelType type = T_AUTO) + { + opJmp(label, type, B11101011, B11101001, 0); + } + void jmp(const Label& label, LabelType type = T_AUTO) + { + opJmp(label, type, B11101011, B11101001, 0); + } + void jmp(const char *label, LabelType type = T_AUTO) { jmp(std::string(label), type); } + void jmp(const void *addr, LabelType type = T_AUTO) + { + opJmpAbs(addr, type, B11101011, B11101001); + } + void jmp(const Operand& op) + { + opR_ModM(op, BIT, 4, 0xFF, NONE, NONE, true); + } + void call(const Operand& op) + { + opR_ModM(op, 16 | i32e, 2, 0xFF, NONE, NONE, true); + } + // (REG|MEM, REG) + void test(const Operand& op, const Reg& reg) + { + opModRM(reg, op, op.isREG() && (op.getKind() == reg.getKind()), op.isMEM(), B10000100); + } + // (REG|MEM, IMM) + void test(const Operand& op, uint32 imm) + { + verifyMemHasSize(op); + int immSize = (std::min)(op.getBit() / 8, 4U); + if (op.isREG() && op.getIdx() == 0) { // al, ax, eax + rex(op); + db(B10101000 | (op.isBit(8) ? 0 : 1)); + } else { + opR_ModM(op, 0, 0, B11110110, NONE, NONE, false, immSize); + } + db(imm, immSize); + } + void ret(int imm = 0) + { + if (imm) { + db(B11000010); dw(imm); + } else { + db(B11000011); + } + } + // (REG16|REG32, REG16|REG32|MEM) + void imul(const Reg& reg, const Operand& op) + { + opModRM(reg, op, op.isREG() && (reg.getKind() == op.getKind()), op.isMEM(), 0x0F, B10101111); + } + void imul(const Reg& reg, const Operand& op, int imm) + { + int s = inner::IsInDisp8(imm) ? 1 : 0; + int immSize = s ? 1 : reg.isREG(16) ? 2 : 4; + opModRM(reg, op, op.isREG() && (reg.getKind() == op.getKind()), op.isMEM(), B01101001 | (s << 1), NONE, NONE, immSize); + db(imm, immSize); + } + void pop(const Operand& op) + { + opPushPop(op, B10001111, 0, B01011000); + } + void push(const Operand& op) + { + opPushPop(op, B11111111, 6, B01010000); + } + void push(const AddressFrame& af, uint32 imm) + { + if (af.bit_ == 8 && inner::IsInDisp8(imm)) { + db(B01101010); db(imm); + } else if (af.bit_ == 16 && isInDisp16(imm)) { + db(0x66); db(B01101000); dw(imm); + } else { + db(B01101000); dd(imm); + } + } + /* use "push(word, 4)" if you want "push word 4" */ + void push(uint32 imm) + { + if (inner::IsInDisp8(imm)) { + push(byte, imm); + } else { + push(dword, imm); + } + } +#ifndef XBYAK_DISABLE_SEGMENT + void push(const Segment& seg) + { + switch (seg.getIdx()) { + case Segment::es: db(0x06); break; + case Segment::cs: db(0x0e); break; + case Segment::ss: db(0x16); break; + case Segment::ds: db(0x1e); break; + case Segment::fs: db(0x0f); db(0xa0); break; + case Segment::gs: db(0x0f); db(0xa8); break; + default: + assert(0); + } + } + void pop(const Segment& seg) + { + switch (seg.getIdx()) { + case Segment::es: db(0x07); break; + case Segment::cs: throw Error(ERR_BAD_COMBINATION); + case Segment::ss: db(0x17); break; + case Segment::ds: db(0x1f); break; + case Segment::fs: db(0x0f); db(0xa1); break; + case Segment::gs: db(0x0f); db(0xa9); break; + default: + assert(0); + } + } +#endif + void bswap(const Reg32e& reg) + { + opModR(Reg32(1), reg, 0x0F); + } + void mov(const Operand& reg1, const Operand& reg2) + { + const Reg *reg = 0; + const Address *addr = 0; + uint8 code = 0; + if (reg1.isREG() && reg1.getIdx() == 0 && reg2.isMEM()) { // mov eax|ax|al, [disp] + reg = &static_cast(reg1); + addr= &static_cast(reg2); + code = B10100000; + } else + if (reg1.isMEM() && reg2.isREG() && reg2.getIdx() == 0) { // mov [disp], eax|ax|al + reg = &static_cast(reg2); + addr= &static_cast(reg1); + code = B10100010; + } +#ifdef XBYAK64 + if (addr && addr->is64bitDisp()) { + if (code) { + rex(*reg); + db(reg1.isREG(8) ? 0xA0 : reg1.isREG() ? 0xA1 : reg2.isREG(8) ? 0xA2 : 0xA3); + db(addr->getDisp(), 8); + } else { + throw Error(ERR_BAD_COMBINATION); + } + } else +#else + if (code && addr->isOnlyDisp()) { + rex(*reg, *addr); + db(code | (reg->isBit(8) ? 0 : 1)); + dd(static_cast(addr->getDisp())); + } else +#endif + { + opRM_RM(reg1, reg2, B10001000); + } + } +private: + /* + mov(r, imm) = db(imm, mov_imm(r, imm)) + */ + int mov_imm(const Reg& reg, size_t imm) + { + int bit = reg.getBit(); + const int idx = reg.getIdx(); + int code = B10110000 | ((bit == 8 ? 0 : 1) << 3); + if (bit == 64 && (imm & ~size_t(0xffffffffu)) == 0) { + rex(Reg32(idx)); + bit = 32; + } else { + rex(reg); + if (bit == 64 && inner::IsInInt32(imm)) { + db(B11000111); + code = B11000000; + bit = 32; + } + } + db(code | (idx & 7)); + return bit / 8; + } + template + void putL_inner(T& label, bool relative = false, size_t disp = 0) + { + const int jmpSize = relative ? 4 : (int)sizeof(size_t); + if (isAutoGrow() && size_ + 16 >= maxSize_) growMemory(); + size_t offset = 0; + if (labelMgr_.getOffset(&offset, label)) { + if (relative) { + db(inner::VerifyInInt32(offset + disp - size_ - jmpSize), jmpSize); + } else if (isAutoGrow()) { + db(uint64(0), jmpSize); + save(size_ - jmpSize, offset, jmpSize, inner::LaddTop); + } else { + db(size_t(top_) + offset, jmpSize); + } + return; + } + db(uint64(0), jmpSize); + JmpLabel jmp(size_, jmpSize, (relative ? inner::LasIs : isAutoGrow() ? inner::LaddTop : inner::Labs), disp); + labelMgr_.addUndefinedLabel(label, jmp); + } +public: + void mov(const Operand& op, size_t imm) + { + if (op.isREG()) { + const int size = mov_imm(static_cast(op), imm); + db(imm, size); + } else if (op.isMEM()) { + verifyMemHasSize(op); + int immSize = op.getBit() / 8; + if (immSize <= 4) { + sint64 s = sint64(imm) >> (immSize * 8); + if (s != 0 && s != -1) throw Error(ERR_IMM_IS_TOO_BIG); + } else { + if (!inner::IsInInt32(imm)) throw Error(ERR_IMM_IS_TOO_BIG); + immSize = 4; + } + opModM(static_cast(op), Reg(0, Operand::REG, op.getBit()), B11000110, NONE, NONE, immSize); + db(static_cast(imm), immSize); + } else { + throw Error(ERR_BAD_COMBINATION); + } + } + void mov(const NativeReg& reg, const char *label) // can't use std::string + { + if (label == 0) { + mov(static_cast(reg), 0); // call imm + return; + } + mov_imm(reg, dummyAddr); + putL(label); + } + void mov(const NativeReg& reg, const Label& label) + { + mov_imm(reg, dummyAddr); + putL(label); + } +#ifndef XBYAK_DISABLE_SEGMENT + void putSeg(const Segment& seg) + { + switch (seg.getIdx()) { + case Segment::es: db(0x2e); break; + case Segment::cs: db(0x36); break; + case Segment::ss: db(0x3e); break; + case Segment::ds: db(0x26); break; + case Segment::fs: db(0x64); break; + case Segment::gs: db(0x65); break; + default: + assert(0); + } + } + void mov(const Operand& op, const Segment& seg) + { + opModRM(Reg8(seg.getIdx()), op, op.isREG(16|i32e), op.isMEM(), 0x8C); + } + void mov(const Segment& seg, const Operand& op) + { + opModRM(Reg8(seg.getIdx()), op.isREG(16|i32e) ? static_cast(static_cast(op).cvt32()) : op, op.isREG(16|i32e), op.isMEM(), 0x8E); + } +#endif + void movbe(const Reg& reg, const Address& addr) { opModM(addr, reg, 0x0F, 0x38, 0xF0); } + void movbe(const Address& addr, const Reg& reg) { opModM(addr, reg, 0x0F, 0x38, 0xF1); } + /* + put address of label to buffer + @note the put size is 4(32-bit), 8(64-bit) + */ + void putL(std::string label) { putL_inner(label); } + void putL(const Label& label) { putL_inner(label); } + void adcx(const Reg32e& reg, const Operand& op) { opGen(reg, op, 0xF6, 0x66, isREG32_REG32orMEM, NONE, 0x38); } + void adox(const Reg32e& reg, const Operand& op) { opGen(reg, op, 0xF6, 0xF3, isREG32_REG32orMEM, NONE, 0x38); } + void cmpxchg8b(const Address& addr) { opModM(addr, Reg32(1), 0x0F, B11000111); } +#ifdef XBYAK64 + void cmpxchg16b(const Address& addr) { opModM(addr, Reg64(1), 0x0F, B11000111); } +#endif + void xadd(const Operand& op, const Reg& reg) + { + opModRM(reg, op, (op.isREG() && reg.isREG() && op.getBit() == reg.getBit()), op.isMEM(), 0x0F, B11000000 | (reg.isBit(8) ? 0 : 1)); + } + void cmpxchg(const Operand& op, const Reg& reg) + { + opModRM(reg, op, (op.isREG() && reg.isREG() && op.getBit() == reg.getBit()), op.isMEM(), 0x0F, 0xb0 | (reg.isBit(8) ? 0 : 1)); + } + void xchg(const Operand& op1, const Operand& op2) + { + const Operand *p1 = &op1, *p2 = &op2; + if (p1->isMEM() || (p2->isREG(16 | i32e) && p2->getIdx() == 0)) { + p1 = &op2; p2 = &op1; + } + if (p1->isMEM()) throw Error(ERR_BAD_COMBINATION); + if (p2->isREG() && (p1->isREG(16 | i32e) && p1->getIdx() == 0) +#ifdef XBYAK64 + && (p2->getIdx() != 0 || !p1->isREG(32)) +#endif + ) { + rex(*p2, *p1); db(0x90 | (p2->getIdx() & 7)); + return; + } + opModRM(*p1, *p2, (p1->isREG() && p2->isREG() && (p1->getBit() == p2->getBit())), p2->isMEM(), B10000110 | (p1->isBit(8) ? 0 : 1)); + } + void call(std::string label) { opJmp(label, T_NEAR, 0, B11101000, 0); } + // call(string label) + void call(const char *label) { call(std::string(label)); } + void call(const Label& label) { opJmp(label, T_NEAR, 0, B11101000, 0); } + // call(function pointer) +#ifdef XBYAK_VARIADIC_TEMPLATE + template + void call(Ret(*func)(Params...)) { call(CastTo(func)); } +#endif + void call(const void *addr) { opJmpAbs(addr, T_NEAR, 0, B11101000); } + // special case + void movd(const Address& addr, const Mmx& mmx) + { + if (mmx.isXMM()) db(0x66); + opModM(addr, mmx, 0x0F, B01111110); + } + void movd(const Reg32& reg, const Mmx& mmx) + { + if (mmx.isXMM()) db(0x66); + opModR(mmx, reg, 0x0F, B01111110); + } + void movd(const Mmx& mmx, const Address& addr) + { + if (mmx.isXMM()) db(0x66); + opModM(addr, mmx, 0x0F, B01101110); + } + void movd(const Mmx& mmx, const Reg32& reg) + { + if (mmx.isXMM()) db(0x66); + opModR(mmx, reg, 0x0F, B01101110); + } + void movq2dq(const Xmm& xmm, const Mmx& mmx) + { + db(0xF3); opModR(xmm, mmx, 0x0F, B11010110); + } + void movdq2q(const Mmx& mmx, const Xmm& xmm) + { + db(0xF2); opModR(mmx, xmm, 0x0F, B11010110); + } + void movq(const Mmx& mmx, const Operand& op) + { + if (mmx.isXMM()) db(0xF3); + opModRM(mmx, op, (mmx.getKind() == op.getKind()), op.isMEM(), 0x0F, mmx.isXMM() ? B01111110 : B01101111); + } + void movq(const Address& addr, const Mmx& mmx) + { + if (mmx.isXMM()) db(0x66); + opModM(addr, mmx, 0x0F, mmx.isXMM() ? B11010110 : B01111111); + } +#ifdef XBYAK64 + void movq(const Reg64& reg, const Mmx& mmx) + { + if (mmx.isXMM()) db(0x66); + opModR(mmx, reg, 0x0F, B01111110); + } + void movq(const Mmx& mmx, const Reg64& reg) + { + if (mmx.isXMM()) db(0x66); + opModR(mmx, reg, 0x0F, B01101110); + } + void pextrq(const Operand& op, const Xmm& xmm, uint8 imm) + { + if (!op.isREG(64) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); + opGen(Reg64(xmm.getIdx()), op, 0x16, 0x66, 0, imm, B00111010); // force to 64bit + } + void pinsrq(const Xmm& xmm, const Operand& op, uint8 imm) + { + if (!op.isREG(64) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); + opGen(Reg64(xmm.getIdx()), op, 0x22, 0x66, 0, imm, B00111010); // force to 64bit + } + void movsxd(const Reg64& reg, const Operand& op) + { + if (!op.isBit(32)) throw Error(ERR_BAD_COMBINATION); + opModRM(reg, op, op.isREG(), op.isMEM(), 0x63); + } +#endif + // MMX2 : pextrw : reg, mmx/xmm, imm + // SSE4 : pextrw, pextrb, pextrd, extractps : reg/mem, mmx/xmm, imm + void pextrw(const Operand& op, const Mmx& xmm, uint8 imm) { opExt(op, xmm, 0x15, imm, true); } + void pextrb(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x14, imm); } + void pextrd(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x16, imm); } + void extractps(const Operand& op, const Xmm& xmm, uint8 imm) { opExt(op, xmm, 0x17, imm); } + void pinsrw(const Mmx& mmx, const Operand& op, int imm) + { + if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); + opGen(mmx, op, B11000100, mmx.isXMM() ? 0x66 : NONE, 0, imm); + } + void insertps(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, imm, B00111010); } + void pinsrb(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x20, 0x66, isXMM_REG32orMEM, imm, B00111010); } + void pinsrd(const Xmm& xmm, const Operand& op, uint8 imm) { opGen(xmm, op, 0x22, 0x66, isXMM_REG32orMEM, imm, B00111010); } + + void pmovmskb(const Reg32e& reg, const Mmx& mmx) + { + if (mmx.isXMM()) db(0x66); + opModR(reg, mmx, 0x0F, B11010111); + } + void maskmovq(const Mmx& reg1, const Mmx& reg2) + { + if (!reg1.isMMX() || !reg2.isMMX()) throw Error(ERR_BAD_COMBINATION); + opModR(reg1, reg2, 0x0F, B11110111); + } + void lea(const Reg32e& reg, const Address& addr) { opModM(addr, reg, B10001101); } + + void movmskps(const Reg32e& reg, const Xmm& xmm) { opModR(reg, xmm, 0x0F, B01010000); } + void movmskpd(const Reg32e& reg, const Xmm& xmm) { db(0x66); movmskps(reg, xmm); } + void movntps(const Address& addr, const Xmm& xmm) { opModM(addr, Mmx(xmm.getIdx()), 0x0F, B00101011); } + void movntdqa(const Xmm& xmm, const Address& addr) { db(0x66); opModM(addr, xmm, 0x0F, 0x38, 0x2A); } + void lddqu(const Xmm& xmm, const Address& addr) { db(0xF2); opModM(addr, xmm, 0x0F, B11110000); } + void movnti(const Address& addr, const Reg32e& reg) { opModM(addr, reg, 0x0F, B11000011); } + void movntq(const Address& addr, const Mmx& mmx) + { + if (!mmx.isMMX()) throw Error(ERR_BAD_COMBINATION); + opModM(addr, mmx, 0x0F, B11100111); + } + void crc32(const Reg32e& reg, const Operand& op) + { + if (reg.isBit(32) && op.isBit(16)) db(0x66); + db(0xF2); + opModRM(reg, op, op.isREG(), op.isMEM(), 0x0F, 0x38, 0xF0 | (op.isBit(8) ? 0 : 1)); + } + void rdrand(const Reg& r) { if (r.isBit(8)) throw Error(ERR_BAD_SIZE_OF_REGISTER); opModR(Reg(6, Operand::REG, r.getBit()), r, 0x0f, 0xc7); } + void rdseed(const Reg& r) { if (r.isBit(8)) throw Error(ERR_BAD_SIZE_OF_REGISTER); opModR(Reg(7, Operand::REG, r.getBit()), r, 0x0f, 0xc7); } + void rorx(const Reg32e& r, const Operand& op, uint8 imm) { opGpr(r, op, Reg32e(0, r.getBit()), MM_0F3A | PP_F2, 0xF0, false, imm); } + enum { NONE = 256 }; + CodeGenerator(size_t maxSize = DEFAULT_MAX_CODE_SIZE, void *userPtr = 0, Allocator *allocator = 0) + : CodeArray(maxSize, userPtr, allocator) + , mm0(0), mm1(1), mm2(2), mm3(3), mm4(4), mm5(5), mm6(6), mm7(7) + , xmm0(0), xmm1(1), xmm2(2), xmm3(3), xmm4(4), xmm5(5), xmm6(6), xmm7(7) + , ymm0(0), ymm1(1), ymm2(2), ymm3(3), ymm4(4), ymm5(5), ymm6(6), ymm7(7) + , xm0(xmm0), xm1(xmm1), xm2(xmm2), xm3(xmm3), xm4(xmm4), xm5(xmm5), xm6(xmm6), xm7(xmm7) // for my convenience + , ym0(ymm0), ym1(ymm1), ym2(ymm2), ym3(ymm3), ym4(ymm4), ym5(ymm5), ym6(ymm6), ym7(ymm7) // for my convenience + , eax(Operand::EAX), ecx(Operand::ECX), edx(Operand::EDX), ebx(Operand::EBX), esp(Operand::ESP), ebp(Operand::EBP), esi(Operand::ESI), edi(Operand::EDI) + , ax(Operand::AX), cx(Operand::CX), dx(Operand::DX), bx(Operand::BX), sp(Operand::SP), bp(Operand::BP), si(Operand::SI), di(Operand::DI) + , al(Operand::AL), cl(Operand::CL), dl(Operand::DL), bl(Operand::BL), ah(Operand::AH), ch(Operand::CH), dh(Operand::DH), bh(Operand::BH) + , ptr(0), byte(8), word(16), dword(32), qword(64) + , st0(0), st1(1), st2(2), st3(3), st4(4), st5(5), st6(6), st7(7) +#ifdef XBYAK64 + , rax(Operand::RAX), rcx(Operand::RCX), rdx(Operand::RDX), rbx(Operand::RBX), rsp(Operand::RSP), rbp(Operand::RBP), rsi(Operand::RSI), rdi(Operand::RDI), r8(Operand::R8), r9(Operand::R9), r10(Operand::R10), r11(Operand::R11), r12(Operand::R12), r13(Operand::R13), r14(Operand::R14), r15(Operand::R15) + , r8d(Operand::R8D), r9d(Operand::R9D), r10d(Operand::R10D), r11d(Operand::R11D), r12d(Operand::R12D), r13d(Operand::R13D), r14d(Operand::R14D), r15d(Operand::R15D) + , r8w(Operand::R8W), r9w(Operand::R9W), r10w(Operand::R10W), r11w(Operand::R11W), r12w(Operand::R12W), r13w(Operand::R13W), r14w(Operand::R14W), r15w(Operand::R15W) + , r8b(Operand::R8B), r9b(Operand::R9B), r10b(Operand::R10B), r11b(Operand::R11B), r12b(Operand::R12B), r13b(Operand::R13B), r14b(Operand::R14B), r15b(Operand::R15B) + , spl(Operand::SPL, true), bpl(Operand::BPL, true), sil(Operand::SIL, true), dil(Operand::DIL, true) + , xmm8(8), xmm9(9), xmm10(10), xmm11(11), xmm12(12), xmm13(13), xmm14(14), xmm15(15) + , ymm8(8), ymm9(9), ymm10(10), ymm11(11), ymm12(12), ymm13(13), ymm14(14), ymm15(15) + , xm8(xmm8), xm9(xmm9), xm10(xmm10), xm11(xmm11), xm12(xmm12), xm13(xmm13), xm14(xmm14), xm15(xmm15) // for my convenience + , ym8(ymm8), ym9(ymm9), ym10(ymm10), ym11(ymm11), ym12(ymm12), ym13(ymm13), ym14(ymm14), ym15(ymm15) // for my convenience + , rip() +#endif +#ifndef XBYAK_DISABLE_SEGMENT + , es(Segment::es), cs(Segment::cs), ss(Segment::ss), ds(Segment::ds), fs(Segment::fs), gs(Segment::gs) +#endif + { + labelMgr_.set(this); + } + void reset() + { + resetSize(); + labelMgr_.reset(); + labelMgr_.set(this); + } + bool hasUndefinedLabel() const { return labelMgr_.hasUndefSlabel() || labelMgr_.hasUndefClabel(); } + /* + call ready() to complete generating code on AutoGrow + */ + void ready() + { + if (hasUndefinedLabel()) throw Error(ERR_LABEL_IS_NOT_FOUND); + calcJmpAddress(); + } +#ifdef XBYAK_TEST + void dump(bool doClear = true) + { + CodeArray::dump(); + if (doClear) size_ = 0; + } +#endif + +#ifndef XBYAK_DONT_READ_LIST +#include "xbyak_mnemonic.h" + void align(int x = 16) + { + if (x == 1) return; + if (x < 1 || (x & (x - 1))) throw Error(ERR_BAD_ALIGN); + if (isAutoGrow() && x > (int)inner::ALIGN_PAGE_SIZE) fprintf(stderr, "warning:autoGrow mode does not support %d align\n", x); + while (size_t(getCurr()) % x) { + nop(); + } + } +#endif +}; + +namespace util { +static const Mmx mm0(0), mm1(1), mm2(2), mm3(3), mm4(4), mm5(5), mm6(6), mm7(7); +static const Xmm xmm0(0), xmm1(1), xmm2(2), xmm3(3), xmm4(4), xmm5(5), xmm6(6), xmm7(7); +static const Ymm ymm0(0), ymm1(1), ymm2(2), ymm3(3), ymm4(4), ymm5(5), ymm6(6), ymm7(7); +static const Reg32 eax(Operand::EAX), ecx(Operand::ECX), edx(Operand::EDX), ebx(Operand::EBX), esp(Operand::ESP), ebp(Operand::EBP), esi(Operand::ESI), edi(Operand::EDI); +static const Reg16 ax(Operand::AX), cx(Operand::CX), dx(Operand::DX), bx(Operand::BX), sp(Operand::SP), bp(Operand::BP), si(Operand::SI), di(Operand::DI); +static const Reg8 al(Operand::AL), cl(Operand::CL), dl(Operand::DL), bl(Operand::BL), ah(Operand::AH), ch(Operand::CH), dh(Operand::DH), bh(Operand::BH); +static const AddressFrame ptr(0), byte(8), word(16), dword(32), qword(64); +static const Fpu st0(0), st1(1), st2(2), st3(3), st4(4), st5(5), st6(6), st7(7); +#ifdef XBYAK64 +static const Reg64 rax(Operand::RAX), rcx(Operand::RCX), rdx(Operand::RDX), rbx(Operand::RBX), rsp(Operand::RSP), rbp(Operand::RBP), rsi(Operand::RSI), rdi(Operand::RDI), r8(Operand::R8), r9(Operand::R9), r10(Operand::R10), r11(Operand::R11), r12(Operand::R12), r13(Operand::R13), r14(Operand::R14), r15(Operand::R15); +static const Reg32 r8d(Operand::R8D), r9d(Operand::R9D), r10d(Operand::R10D), r11d(Operand::R11D), r12d(Operand::R12D), r13d(Operand::R13D), r14d(Operand::R14D), r15d(Operand::R15D); +static const Reg16 r8w(Operand::R8W), r9w(Operand::R9W), r10w(Operand::R10W), r11w(Operand::R11W), r12w(Operand::R12W), r13w(Operand::R13W), r14w(Operand::R14W), r15w(Operand::R15W); +static const Reg8 r8b(Operand::R8B), r9b(Operand::R9B), r10b(Operand::R10B), r11b(Operand::R11B), r12b(Operand::R12B), r13b(Operand::R13B), r14b(Operand::R14B), r15b(Operand::R15B), spl(Operand::SPL, 1), bpl(Operand::BPL, 1), sil(Operand::SIL, 1), dil(Operand::DIL, 1); +static const Xmm xmm8(8), xmm9(9), xmm10(10), xmm11(11), xmm12(12), xmm13(13), xmm14(14), xmm15(15); +static const Ymm ymm8(8), ymm9(9), ymm10(10), ymm11(11), ymm12(12), ymm13(13), ymm14(14), ymm15(15); +static const RegRip rip; +#endif +#ifndef XBYAK_DISABLE_SEGMENT +static const Segment es(Segment::es), cs(Segment::cs), ss(Segment::ss), ds(Segment::ds), fs(Segment::fs), gs(Segment::gs); +#endif +} // util + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +} // end of namespace + +#endif // XBYAK_XBYAK_H_ diff --git a/f4se/xbyak/xbyak_bin2hex.h b/f4se/xbyak/xbyak_bin2hex.h new file mode 100644 index 0000000..69ecdbf --- /dev/null +++ b/f4se/xbyak/xbyak_bin2hex.h @@ -0,0 +1,258 @@ +enum { + B00000000= 0, + B00000001= 1, + B00000010= 2, + B00000011= 3, + B00000100= 4, + B00000101= 5, + B00000110= 6, + B00000111= 7, + B00001000= 8, + B00001001= 9, + B00001010= 10, + B00001011= 11, + B00001100= 12, + B00001101= 13, + B00001110= 14, + B00001111= 15, + B00010000= 16, + B00010001= 17, + B00010010= 18, + B00010011= 19, + B00010100= 20, + B00010101= 21, + B00010110= 22, + B00010111= 23, + B00011000= 24, + B00011001= 25, + B00011010= 26, + B00011011= 27, + B00011100= 28, + B00011101= 29, + B00011110= 30, + B00011111= 31, + B00100000= 32, + B00100001= 33, + B00100010= 34, + B00100011= 35, + B00100100= 36, + B00100101= 37, + B00100110= 38, + B00100111= 39, + B00101000= 40, + B00101001= 41, + B00101010= 42, + B00101011= 43, + B00101100= 44, + B00101101= 45, + B00101110= 46, + B00101111= 47, + B00110000= 48, + B00110001= 49, + B00110010= 50, + B00110011= 51, + B00110100= 52, + B00110101= 53, + B00110110= 54, + B00110111= 55, + B00111000= 56, + B00111001= 57, + B00111010= 58, + B00111011= 59, + B00111100= 60, + B00111101= 61, + B00111110= 62, + B00111111= 63, + B01000000= 64, + B01000001= 65, + B01000010= 66, + B01000011= 67, + B01000100= 68, + B01000101= 69, + B01000110= 70, + B01000111= 71, + B01001000= 72, + B01001001= 73, + B01001010= 74, + B01001011= 75, + B01001100= 76, + B01001101= 77, + B01001110= 78, + B01001111= 79, + B01010000= 80, + B01010001= 81, + B01010010= 82, + B01010011= 83, + B01010100= 84, + B01010101= 85, + B01010110= 86, + B01010111= 87, + B01011000= 88, + B01011001= 89, + B01011010= 90, + B01011011= 91, + B01011100= 92, + B01011101= 93, + B01011110= 94, + B01011111= 95, + B01100000= 96, + B01100001= 97, + B01100010= 98, + B01100011= 99, + B01100100= 100, + B01100101= 101, + B01100110= 102, + B01100111= 103, + B01101000= 104, + B01101001= 105, + B01101010= 106, + B01101011= 107, + B01101100= 108, + B01101101= 109, + B01101110= 110, + B01101111= 111, + B01110000= 112, + B01110001= 113, + B01110010= 114, + B01110011= 115, + B01110100= 116, + B01110101= 117, + B01110110= 118, + B01110111= 119, + B01111000= 120, + B01111001= 121, + B01111010= 122, + B01111011= 123, + B01111100= 124, + B01111101= 125, + B01111110= 126, + B01111111= 127, + B10000000= 128, + B10000001= 129, + B10000010= 130, + B10000011= 131, + B10000100= 132, + B10000101= 133, + B10000110= 134, + B10000111= 135, + B10001000= 136, + B10001001= 137, + B10001010= 138, + B10001011= 139, + B10001100= 140, + B10001101= 141, + B10001110= 142, + B10001111= 143, + B10010000= 144, + B10010001= 145, + B10010010= 146, + B10010011= 147, + B10010100= 148, + B10010101= 149, + B10010110= 150, + B10010111= 151, + B10011000= 152, + B10011001= 153, + B10011010= 154, + B10011011= 155, + B10011100= 156, + B10011101= 157, + B10011110= 158, + B10011111= 159, + B10100000= 160, + B10100001= 161, + B10100010= 162, + B10100011= 163, + B10100100= 164, + B10100101= 165, + B10100110= 166, + B10100111= 167, + B10101000= 168, + B10101001= 169, + B10101010= 170, + B10101011= 171, + B10101100= 172, + B10101101= 173, + B10101110= 174, + B10101111= 175, + B10110000= 176, + B10110001= 177, + B10110010= 178, + B10110011= 179, + B10110100= 180, + B10110101= 181, + B10110110= 182, + B10110111= 183, + B10111000= 184, + B10111001= 185, + B10111010= 186, + B10111011= 187, + B10111100= 188, + B10111101= 189, + B10111110= 190, + B10111111= 191, + B11000000= 192, + B11000001= 193, + B11000010= 194, + B11000011= 195, + B11000100= 196, + B11000101= 197, + B11000110= 198, + B11000111= 199, + B11001000= 200, + B11001001= 201, + B11001010= 202, + B11001011= 203, + B11001100= 204, + B11001101= 205, + B11001110= 206, + B11001111= 207, + B11010000= 208, + B11010001= 209, + B11010010= 210, + B11010011= 211, + B11010100= 212, + B11010101= 213, + B11010110= 214, + B11010111= 215, + B11011000= 216, + B11011001= 217, + B11011010= 218, + B11011011= 219, + B11011100= 220, + B11011101= 221, + B11011110= 222, + B11011111= 223, + B11100000= 224, + B11100001= 225, + B11100010= 226, + B11100011= 227, + B11100100= 228, + B11100101= 229, + B11100110= 230, + B11100111= 231, + B11101000= 232, + B11101001= 233, + B11101010= 234, + B11101011= 235, + B11101100= 236, + B11101101= 237, + B11101110= 238, + B11101111= 239, + B11110000= 240, + B11110001= 241, + B11110010= 242, + B11110011= 243, + B11110100= 244, + B11110101= 245, + B11110110= 246, + B11110111= 247, + B11111000= 248, + B11111001= 249, + B11111010= 250, + B11111011= 251, + B11111100= 252, + B11111101= 253, + B11111110= 254, + B11111111= 255 +}; diff --git a/f4se/xbyak/xbyak_mnemonic.h b/f4se/xbyak/xbyak_mnemonic.h new file mode 100644 index 0000000..ac5be96 --- /dev/null +++ b/f4se/xbyak/xbyak_mnemonic.h @@ -0,0 +1,1461 @@ +const char *getVersionString() const { return "4.87"; } +void packssdw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x6B); } +void packsswb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x63); } +void packuswb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x67); } +void pand(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDB); } +void pandn(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDF); } +void pmaddwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF5); } +void pmulhuw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE4); } +void pmulhw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE5); } +void pmullw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD5); } +void por(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEB); } +void punpckhbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x68); } +void punpckhwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x69); } +void punpckhdq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x6A); } +void punpcklbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x60); } +void punpcklwd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x61); } +void punpckldq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x62); } +void pxor(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEF); } +void pavgb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE0); } +void pavgw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE3); } +void pmaxsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEE); } +void pmaxub(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDE); } +void pminsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEA); } +void pminub(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDA); } +void psadbw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF6); } +void paddq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD4); } +void pmuludq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF4); } +void psubq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFB); } +void paddb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFC); } +void paddw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFD); } +void paddd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFE); } +void paddsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xEC); } +void paddsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xED); } +void paddusb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDC); } +void paddusw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xDD); } +void pcmpeqb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x74); } +void pcmpeqw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x75); } +void pcmpeqd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x76); } +void pcmpgtb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x64); } +void pcmpgtw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x65); } +void pcmpgtd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x66); } +void psllw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF1); } +void pslld(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF2); } +void psllq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF3); } +void psraw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE1); } +void psrad(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE2); } +void psrlw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD1); } +void psrld(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD2); } +void psrlq(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD3); } +void psubb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF8); } +void psubw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xF9); } +void psubd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xFA); } +void psubsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE8); } +void psubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xE9); } +void psubusb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD8); } +void psubusw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0xD9); } +void psllw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 6); } +void pslld(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 6); } +void psllq(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x73, 6); } +void psraw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 4); } +void psrad(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 4); } +void psrlw(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x71, 2); } +void psrld(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x72, 2); } +void psrlq(const Mmx& mmx, int imm8) { opMMX_IMM(mmx, imm8, 0x73, 2); } +void pslldq(const Xmm& xmm, int imm8) { opMMX_IMM(xmm, imm8, 0x73, 7); } +void psrldq(const Xmm& xmm, int imm8) { opMMX_IMM(xmm, imm8, 0x73, 3); } +void pshufw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0x00, imm8); } +void pshuflw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0xF2, imm8); } +void pshufhw(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0xF3, imm8); } +void pshufd(const Mmx& mmx, const Operand& op, uint8 imm8) { opMMX(mmx, op, 0x70, 0x66, imm8); } +void movdqa(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x6F, 0x66); } +void movdqa(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x7F); } +void movdqu(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x6F, 0xF3); } +void movdqu(const Address& addr, const Xmm& xmm) { db(0xF3); opModM(addr, xmm, 0x0F, 0x7F); } +void movaps(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x28, 0x100); } +void movaps(const Address& addr, const Xmm& xmm) { opModM(addr, xmm, 0x0F, 0x29); } +void movss(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0xF3); } +void movss(const Address& addr, const Xmm& xmm) { db(0xF3); opModM(addr, xmm, 0x0F, 0x11); } +void movups(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0x100); } +void movups(const Address& addr, const Xmm& xmm) { opModM(addr, xmm, 0x0F, 0x11); } +void movapd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x28, 0x66); } +void movapd(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x29); } +void movsd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0xF2); } +void movsd(const Address& addr, const Xmm& xmm) { db(0xF2); opModM(addr, xmm, 0x0F, 0x11); } +void movupd(const Xmm& xmm, const Operand& op) { opMMX(xmm, op, 0x10, 0x66); } +void movupd(const Address& addr, const Xmm& xmm) { db(0x66); opModM(addr, xmm, 0x0F, 0x11); } +void addps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0x100, isXMM_XMMorMEM); } +void addss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0xF3, isXMM_XMMorMEM); } +void addpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0x66, isXMM_XMMorMEM); } +void addsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x58, 0xF2, isXMM_XMMorMEM); } +void andnps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x55, 0x100, isXMM_XMMorMEM); } +void andnpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x55, 0x66, isXMM_XMMorMEM); } +void andps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x54, 0x100, isXMM_XMMorMEM); } +void andpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x54, 0x66, isXMM_XMMorMEM); } +void cmpps(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0x100, isXMM_XMMorMEM, imm8); } +void cmpss(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0xF3, isXMM_XMMorMEM, imm8); } +void cmppd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0x66, isXMM_XMMorMEM, imm8); } +void cmpsd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC2, 0xF2, isXMM_XMMorMEM, imm8); } +void divps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0x100, isXMM_XMMorMEM); } +void divss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0xF3, isXMM_XMMorMEM); } +void divpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0x66, isXMM_XMMorMEM); } +void divsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5E, 0xF2, isXMM_XMMorMEM); } +void maxps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0x100, isXMM_XMMorMEM); } +void maxss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0xF3, isXMM_XMMorMEM); } +void maxpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0x66, isXMM_XMMorMEM); } +void maxsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5F, 0xF2, isXMM_XMMorMEM); } +void minps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0x100, isXMM_XMMorMEM); } +void minss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0xF3, isXMM_XMMorMEM); } +void minpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0x66, isXMM_XMMorMEM); } +void minsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5D, 0xF2, isXMM_XMMorMEM); } +void mulps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0x100, isXMM_XMMorMEM); } +void mulss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0xF3, isXMM_XMMorMEM); } +void mulpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0x66, isXMM_XMMorMEM); } +void mulsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x59, 0xF2, isXMM_XMMorMEM); } +void orps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x56, 0x100, isXMM_XMMorMEM); } +void orpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x56, 0x66, isXMM_XMMorMEM); } +void rcpps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x53, 0x100, isXMM_XMMorMEM); } +void rcpss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x53, 0xF3, isXMM_XMMorMEM); } +void rsqrtps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x52, 0x100, isXMM_XMMorMEM); } +void rsqrtss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x52, 0xF3, isXMM_XMMorMEM); } +void shufps(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC6, 0x100, isXMM_XMMorMEM, imm8); } +void shufpd(const Xmm& xmm, const Operand& op, uint8 imm8) { opGen(xmm, op, 0xC6, 0x66, isXMM_XMMorMEM, imm8); } +void sqrtps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0x100, isXMM_XMMorMEM); } +void sqrtss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0xF3, isXMM_XMMorMEM); } +void sqrtpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0x66, isXMM_XMMorMEM); } +void sqrtsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x51, 0xF2, isXMM_XMMorMEM); } +void subps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0x100, isXMM_XMMorMEM); } +void subss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0xF3, isXMM_XMMorMEM); } +void subpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0x66, isXMM_XMMorMEM); } +void subsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5C, 0xF2, isXMM_XMMorMEM); } +void unpckhps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x100, isXMM_XMMorMEM); } +void unpckhpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x66, isXMM_XMMorMEM); } +void unpcklps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x100, isXMM_XMMorMEM); } +void unpcklpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x66, isXMM_XMMorMEM); } +void xorps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x57, 0x100, isXMM_XMMorMEM); } +void xorpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x57, 0x66, isXMM_XMMorMEM); } +void maskmovdqu(const Xmm& reg1, const Xmm& reg2) { db(0x66); opModR(reg1, reg2, 0x0F, 0xF7); } +void movhlps(const Xmm& reg1, const Xmm& reg2) { opModR(reg1, reg2, 0x0F, 0x12); } +void movlhps(const Xmm& reg1, const Xmm& reg2) { opModR(reg1, reg2, 0x0F, 0x16); } +void punpckhqdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x6D, 0x66, isXMM_XMMorMEM); } +void punpcklqdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x6C, 0x66, isXMM_XMMorMEM); } +void comiss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2F, 0x100, isXMM_XMMorMEM); } +void ucomiss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2E, 0x100, isXMM_XMMorMEM); } +void comisd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2F, 0x66, isXMM_XMMorMEM); } +void ucomisd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2E, 0x66, isXMM_XMMorMEM); } +void cvtpd2ps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0x66, isXMM_XMMorMEM); } +void cvtps2pd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0x100, isXMM_XMMorMEM); } +void cvtsd2ss(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0xF2, isXMM_XMMorMEM); } +void cvtss2sd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5A, 0xF3, isXMM_XMMorMEM); } +void cvtpd2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0xF2, isXMM_XMMorMEM); } +void cvttpd2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0x66, isXMM_XMMorMEM); } +void cvtdq2pd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xE6, 0xF3, isXMM_XMMorMEM); } +void cvtps2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0x66, isXMM_XMMorMEM); } +void cvttps2dq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0xF3, isXMM_XMMorMEM); } +void cvtdq2ps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x5B, 0x100, isXMM_XMMorMEM); } +void addsubpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xD0, 0x66, isXMM_XMMorMEM); } +void addsubps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xD0, 0xF2, isXMM_XMMorMEM); } +void haddpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7C, 0x66, isXMM_XMMorMEM); } +void haddps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7C, 0xF2, isXMM_XMMorMEM); } +void hsubpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7D, 0x66, isXMM_XMMorMEM); } +void hsubps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x7D, 0xF2, isXMM_XMMorMEM); } +void movddup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x12, 0xF2, isXMM_XMMorMEM); } +void movshdup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x16, 0xF3, isXMM_XMMorMEM); } +void movsldup(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x12, 0xF3, isXMM_XMMorMEM); } +void cvtpi2ps(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0x100, isXMM_MMXorMEM); } +void cvtps2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0x100, isMMX_XMMorMEM); } +void cvtsi2ss(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0xF3, isXMM_REG32orMEM); } +void cvtss2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0xF3, isREG32_XMMorMEM); } +void cvttps2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0x100, isMMX_XMMorMEM); } +void cvttss2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0xF3, isREG32_XMMorMEM); } +void cvtpi2pd(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0x66, isXMM_MMXorMEM); } +void cvtpd2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0x66, isMMX_XMMorMEM); } +void cvtsi2sd(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2A, 0xF2, isXMM_REG32orMEM); } +void cvtsd2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2D, 0xF2, isREG32_XMMorMEM); } +void cvttpd2pi(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0x66, isMMX_XMMorMEM); } +void cvttsd2si(const Operand& reg, const Operand& op) { opGen(reg, op, 0x2C, 0xF2, isREG32_XMMorMEM); } +void prefetcht0(const Address& addr) { opModM(addr, Reg32(1), 0x0F, B00011000); } +void prefetcht1(const Address& addr) { opModM(addr, Reg32(2), 0x0F, B00011000); } +void prefetcht2(const Address& addr) { opModM(addr, Reg32(3), 0x0F, B00011000); } +void prefetchnta(const Address& addr) { opModM(addr, Reg32(0), 0x0F, B00011000); } +void movhps(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x16, 0x100); } +void movlps(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x12, 0x100); } +void movhpd(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x16, 0x66); } +void movlpd(const Operand& op1, const Operand& op2) { opMovXMM(op1, op2, 0x12, 0x66); } +void cmovo(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 0); } +void jo(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x70, 0x80, 0x0F); } +void jo(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x70, 0x80, 0x0F); } +void seto(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 0); } +void cmovno(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 1); } +void jno(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x71, 0x81, 0x0F); } +void jno(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x71, 0x81, 0x0F); } +void setno(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 1); } +void cmovb(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 2); } +void jb(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void jb(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void setb(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 2); } +void cmovc(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 2); } +void jc(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void jc(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void setc(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 2); } +void cmovnae(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 2); } +void jnae(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void jnae(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x72, 0x82, 0x0F); } +void setnae(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 2); } +void cmovnb(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 3); } +void jnb(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void jnb(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void setnb(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 3); } +void cmovae(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 3); } +void jae(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void jae(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void setae(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 3); } +void cmovnc(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 3); } +void jnc(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void jnc(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x73, 0x83, 0x0F); } +void setnc(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 3); } +void cmove(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 4); } +void je(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } +void je(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } +void sete(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 4); } +void cmovz(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 4); } +void jz(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } +void jz(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x74, 0x84, 0x0F); } +void setz(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 4); } +void cmovne(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 5); } +void jne(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } +void jne(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } +void setne(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 5); } +void cmovnz(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 5); } +void jnz(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } +void jnz(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x75, 0x85, 0x0F); } +void setnz(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 5); } +void cmovbe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 6); } +void jbe(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } +void jbe(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } +void setbe(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 6); } +void cmovna(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 6); } +void jna(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } +void jna(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x76, 0x86, 0x0F); } +void setna(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 6); } +void cmovnbe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 7); } +void jnbe(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } +void jnbe(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } +void setnbe(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 7); } +void cmova(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 7); } +void ja(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } +void ja(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x77, 0x87, 0x0F); } +void seta(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 7); } +void cmovs(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 8); } +void js(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x78, 0x88, 0x0F); } +void js(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x78, 0x88, 0x0F); } +void sets(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 8); } +void cmovns(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 9); } +void jns(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x79, 0x89, 0x0F); } +void jns(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x79, 0x89, 0x0F); } +void setns(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 9); } +void cmovp(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 10); } +void jp(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } +void jp(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } +void setp(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 10); } +void cmovpe(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 10); } +void jpe(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } +void jpe(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7A, 0x8A, 0x0F); } +void setpe(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 10); } +void cmovnp(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 11); } +void jnp(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } +void jnp(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } +void setnp(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 11); } +void cmovpo(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 11); } +void jpo(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } +void jpo(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7B, 0x8B, 0x0F); } +void setpo(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 11); } +void cmovl(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 12); } +void jl(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } +void jl(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } +void setl(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 12); } +void cmovnge(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 12); } +void jnge(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } +void jnge(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7C, 0x8C, 0x0F); } +void setnge(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 12); } +void cmovnl(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 13); } +void jnl(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } +void jnl(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } +void setnl(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 13); } +void cmovge(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 13); } +void jge(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } +void jge(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7D, 0x8D, 0x0F); } +void setge(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 13); } +void cmovle(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 14); } +void jle(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } +void jle(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } +void setle(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 14); } +void cmovng(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 14); } +void jng(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } +void jng(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7E, 0x8E, 0x0F); } +void setng(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 14); } +void cmovnle(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 15); } +void jnle(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } +void jnle(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } +void setnle(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 15); } +void cmovg(const Reg32e& reg, const Operand& op) { opModRM(reg, op, op.isREG(i32e), op.isMEM(), 0x0F, B01000000 | 15); } +void jg(std::string label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } +void jg(const Label& label, LabelType type = T_AUTO) { opJmp(label, type, 0x7F, 0x8F, 0x0F); } +void setg(const Operand& op) { opR_ModM(op, 8, 0, 0x0F, B10010000 | 15); } +#ifdef XBYAK32 +void jcxz(std::string label) { db(0x67); opJmp(label, T_SHORT, 0xe3, 0, 0); } +void jcxz(const Label& label) { db(0x67); opJmp(label, T_SHORT, 0xe3, 0, 0); } +void jecxz(std::string label) { opJmp(label, T_SHORT, 0xe3, 0, 0); } +void jecxz(const Label& label) { opJmp(label, T_SHORT, 0xe3, 0, 0); } +#else +void jecxz(std::string label) { db(0x67); opJmp(label, T_SHORT, 0xe3, 0, 0); } +void jecxz(const Label& label) { db(0x67); opJmp(label, T_SHORT, 0xe3, 0, 0); } +void jrcxz(std::string label) { opJmp(label, T_SHORT, 0xe3, 0, 0); } +void jrcxz(const Label& label) { opJmp(label, T_SHORT, 0xe3, 0, 0); } +#endif +#ifdef XBYAK64 +void cdqe() { db(0x48); db(0x98); } +void cqo() { db(0x48); db(0x99); } +#else +void aaa() { db(0x37); } +void aad() { db(0xD5); db(0x0A); } +void aam() { db(0xD4); db(0x0A); } +void aas() { db(0x3F); } +void daa() { db(0x27); } +void das() { db(0x2F); } +void popad() { db(0x61); } +void popfd() { db(0x9D); } +void pusha() { db(0x60); } +void pushad() { db(0x60); } +void pushfd() { db(0x9C); } +void popa() { db(0x61); } +#endif +void cbw() { db(0x66); db(0x98); } +void cdq() { db(0x99); } +void clc() { db(0xF8); } +void cld() { db(0xFC); } +void cli() { db(0xFA); } +void cmc() { db(0xF5); } +void cpuid() { db(0x0F); db(0xA2); } +void cwd() { db(0x66); db(0x99); } +void cwde() { db(0x98); } +void lahf() { db(0x9F); } +void lock() { db(0xF0); } +void nop() { db(0x90); } +void sahf() { db(0x9E); } +void stc() { db(0xF9); } +void std() { db(0xFD); } +void sti() { db(0xFB); } +void emms() { db(0x0F); db(0x77); } +void pause() { db(0xF3); db(0x90); } +void sfence() { db(0x0F); db(0xAE); db(0xF8); } +void lfence() { db(0x0F); db(0xAE); db(0xE8); } +void mfence() { db(0x0F); db(0xAE); db(0xF0); } +void monitor() { db(0x0F); db(0x01); db(0xC8); } +void mwait() { db(0x0F); db(0x01); db(0xC9); } +void rdmsr() { db(0x0F); db(0x32); } +void rdpmc() { db(0x0F); db(0x33); } +void rdtsc() { db(0x0F); db(0x31); } +void rdtscp() { db(0x0F); db(0x01); db(0xF9); } +void ud2() { db(0x0F); db(0x0B); } +void wait() { db(0x9B); } +void fwait() { db(0x9B); } +void wbinvd() { db(0x0F); db(0x09); } +void wrmsr() { db(0x0F); db(0x30); } +void xlatb() { db(0xD7); } +void popf() { db(0x9D); } +void pushf() { db(0x9C); } +void stac() { db(0x0F); db(0x01); db(0xCB); } +void vzeroall() { db(0xC5); db(0xFC); db(0x77); } +void vzeroupper() { db(0xC5); db(0xF8); db(0x77); } +void xgetbv() { db(0x0F); db(0x01); db(0xD0); } +void f2xm1() { db(0xD9); db(0xF0); } +void fabs() { db(0xD9); db(0xE1); } +void faddp() { db(0xDE); db(0xC1); } +void fchs() { db(0xD9); db(0xE0); } +void fcom() { db(0xD8); db(0xD1); } +void fcomp() { db(0xD8); db(0xD9); } +void fcompp() { db(0xDE); db(0xD9); } +void fcos() { db(0xD9); db(0xFF); } +void fdecstp() { db(0xD9); db(0xF6); } +void fdivp() { db(0xDE); db(0xF9); } +void fdivrp() { db(0xDE); db(0xF1); } +void fincstp() { db(0xD9); db(0xF7); } +void finit() { db(0x9B); db(0xDB); db(0xE3); } +void fninit() { db(0xDB); db(0xE3); } +void fld1() { db(0xD9); db(0xE8); } +void fldl2t() { db(0xD9); db(0xE9); } +void fldl2e() { db(0xD9); db(0xEA); } +void fldpi() { db(0xD9); db(0xEB); } +void fldlg2() { db(0xD9); db(0xEC); } +void fldln2() { db(0xD9); db(0xED); } +void fldz() { db(0xD9); db(0xEE); } +void fmulp() { db(0xDE); db(0xC9); } +void fnop() { db(0xD9); db(0xD0); } +void fpatan() { db(0xD9); db(0xF3); } +void fprem() { db(0xD9); db(0xF8); } +void fprem1() { db(0xD9); db(0xF5); } +void fptan() { db(0xD9); db(0xF2); } +void frndint() { db(0xD9); db(0xFC); } +void fscale() { db(0xD9); db(0xFD); } +void fsin() { db(0xD9); db(0xFE); } +void fsincos() { db(0xD9); db(0xFB); } +void fsqrt() { db(0xD9); db(0xFA); } +void fsubp() { db(0xDE); db(0xE9); } +void fsubrp() { db(0xDE); db(0xE1); } +void ftst() { db(0xD9); db(0xE4); } +void fucom() { db(0xDD); db(0xE1); } +void fucomp() { db(0xDD); db(0xE9); } +void fucompp() { db(0xDA); db(0xE9); } +void fxam() { db(0xD9); db(0xE5); } +void fxch() { db(0xD9); db(0xC9); } +void fxtract() { db(0xD9); db(0xF4); } +void fyl2x() { db(0xD9); db(0xF1); } +void fyl2xp1() { db(0xD9); db(0xF9); } +void adc(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x10); } +void adc(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x10, 2); } +void add(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x00); } +void add(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x00, 0); } +void and_(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x20); } +void and_(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x20, 4); } +#ifndef XBYAK_NO_OP_NAMES +void and(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x20); } +void and(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x20, 4); } +#endif +void cmp(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x38); } +void cmp(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x38, 7); } +void or_(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x08); } +void or_(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x08, 1); } +#ifndef XBYAK_NO_OP_NAMES +void or(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x08); } +void or(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x08, 1); } +#endif +void sbb(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x18); } +void sbb(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x18, 3); } +void sub(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x28); } +void sub(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x28, 5); } +void xor_(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x30); } +void xor_(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x30, 6); } +#ifndef XBYAK_NO_OP_NAMES +void xor(const Operand& op1, const Operand& op2) { opRM_RM(op1, op2, 0x30); } +void xor(const Operand& op, uint32 imm) { opRM_I(op, imm, 0x30, 6); } +#endif +void dec(const Operand& op) { opIncDec(op, 0x48, 1); } +void inc(const Operand& op) { opIncDec(op, 0x40, 0); } +void bt(const Operand& op, const Reg& reg) { opModRM(reg, op, op.isREG(16|32|64) && op.getBit() == reg.getBit(), op.isMEM(), 0x0f, 0xa3); } +void bt(const Operand& op, uint8 imm) { opR_ModM(op, 16|32|64, 4, 0x0f, 0xba, NONE, false, 1); db(imm); } +void bts(const Operand& op, const Reg& reg) { opModRM(reg, op, op.isREG(16|32|64) && op.getBit() == reg.getBit(), op.isMEM(), 0x0f, 0xab); } +void bts(const Operand& op, uint8 imm) { opR_ModM(op, 16|32|64, 5, 0x0f, 0xba, NONE, false, 1); db(imm); } +void btr(const Operand& op, const Reg& reg) { opModRM(reg, op, op.isREG(16|32|64) && op.getBit() == reg.getBit(), op.isMEM(), 0x0f, 0xb3); } +void btr(const Operand& op, uint8 imm) { opR_ModM(op, 16|32|64, 6, 0x0f, 0xba, NONE, false, 1); db(imm); } +void btc(const Operand& op, const Reg& reg) { opModRM(reg, op, op.isREG(16|32|64) && op.getBit() == reg.getBit(), op.isMEM(), 0x0f, 0xbb); } +void btc(const Operand& op, uint8 imm) { opR_ModM(op, 16|32|64, 7, 0x0f, 0xba, NONE, false, 1); db(imm); } +void div(const Operand& op) { opR_ModM(op, 0, 6, 0xF6); } +void idiv(const Operand& op) { opR_ModM(op, 0, 7, 0xF6); } +void imul(const Operand& op) { opR_ModM(op, 0, 5, 0xF6); } +void mul(const Operand& op) { opR_ModM(op, 0, 4, 0xF6); } +void neg(const Operand& op) { opR_ModM(op, 0, 3, 0xF6); } +void not_(const Operand& op) { opR_ModM(op, 0, 2, 0xF6); } +#ifndef XBYAK_NO_OP_NAMES +void not(const Operand& op) { opR_ModM(op, 0, 2, 0xF6); } +#endif +void rcl(const Operand& op, int imm) { opShift(op, imm, 2); } +void rcl(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 2); } +void rcr(const Operand& op, int imm) { opShift(op, imm, 3); } +void rcr(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 3); } +void rol(const Operand& op, int imm) { opShift(op, imm, 0); } +void rol(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 0); } +void ror(const Operand& op, int imm) { opShift(op, imm, 1); } +void ror(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 1); } +void sar(const Operand& op, int imm) { opShift(op, imm, 7); } +void sar(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 7); } +void shl(const Operand& op, int imm) { opShift(op, imm, 4); } +void shl(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 4); } +void shr(const Operand& op, int imm) { opShift(op, imm, 5); } +void shr(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 5); } +void sal(const Operand& op, int imm) { opShift(op, imm, 4); } +void sal(const Operand& op, const Reg8& _cl) { opShift(op, _cl, 4); } +void shld(const Operand& op, const Reg& reg, uint8 imm) { opShxd(op, reg, imm, 0xA4); } +void shld(const Operand& op, const Reg& reg, const Reg8& _cl) { opShxd(op, reg, 0, 0xA4, &_cl); } +void shrd(const Operand& op, const Reg& reg, uint8 imm) { opShxd(op, reg, imm, 0xAC); } +void shrd(const Operand& op, const Reg& reg, const Reg8& _cl) { opShxd(op, reg, 0, 0xAC, &_cl); } +void bsf(const Reg®, const Operand& op) { opModRM(reg, op, op.isREG(16 | i32e), op.isMEM(), 0x0F, 0xBC); } +void bsr(const Reg®, const Operand& op) { opModRM(reg, op, op.isREG(16 | i32e), op.isMEM(), 0x0F, 0xBD); } +void popcnt(const Reg®, const Operand& op) { opSp1(reg, op, 0xF3, 0x0F, 0xB8); } +void tzcnt(const Reg®, const Operand& op) { opSp1(reg, op, 0xF3, 0x0F, 0xBC); } +void lzcnt(const Reg®, const Operand& op) { opSp1(reg, op, 0xF3, 0x0F, 0xBD); } +void pshufb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x00, 0x66, NONE, 0x38); } +void phaddw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x01, 0x66, NONE, 0x38); } +void phaddd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x02, 0x66, NONE, 0x38); } +void phaddsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x03, 0x66, NONE, 0x38); } +void pmaddubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x04, 0x66, NONE, 0x38); } +void phsubw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x05, 0x66, NONE, 0x38); } +void phsubd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x06, 0x66, NONE, 0x38); } +void phsubsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x07, 0x66, NONE, 0x38); } +void psignb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x08, 0x66, NONE, 0x38); } +void psignw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x09, 0x66, NONE, 0x38); } +void psignd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x0A, 0x66, NONE, 0x38); } +void pmulhrsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x0B, 0x66, NONE, 0x38); } +void pabsb(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1C, 0x66, NONE, 0x38); } +void pabsw(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1D, 0x66, NONE, 0x38); } +void pabsd(const Mmx& mmx, const Operand& op) { opMMX(mmx, op, 0x1E, 0x66, NONE, 0x38); } +void palignr(const Mmx& mmx, const Operand& op, int imm) { opMMX(mmx, op, 0x0f, 0x66, static_cast(imm), 0x3a); } +void blendvpd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x15, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void blendvps(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x14, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void packusdw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x2B, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pblendvb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x10, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pcmpeqq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x29, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void ptest(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x17, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovsxbw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x20, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovsxbd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x21, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovsxbq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x22, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovsxwd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x23, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovsxwq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x24, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovsxdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x25, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovzxbw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x30, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovzxbd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x31, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovzxbq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x32, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovzxwd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x33, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovzxwq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x34, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmovzxdq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x35, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pminsb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x38, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pminsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x39, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pminuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3A, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pminud(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3B, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmaxsb(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3C, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmaxsd(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3D, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmaxuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3E, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmaxud(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x3F, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmuldq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x28, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pmulld(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x40, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void phminposuw(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x41, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void pcmpgtq(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0x37, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void aesdec(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xDE, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void aesdeclast(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xDF, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void aesenc(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xDC, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void aesenclast(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xDD, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void aesimc(const Xmm& xmm, const Operand& op) { opGen(xmm, op, 0xDB, 0x66, isXMM_XMMorMEM, NONE, 0x38); } +void blendpd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0D, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void blendps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0C, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void dppd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x41, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void dpps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x40, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void mpsadbw(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x42, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pblendw(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0E, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundps(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x08, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundpd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x09, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundss(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0A, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void roundsd(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x0B, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpestrm(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x60, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpestri(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x61, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpistrm(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x62, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pcmpistri(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x63, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pclmulqdq(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0x44, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void aeskeygenassist(const Xmm& xmm, const Operand& op, int imm) { opGen(xmm, op, 0xDF, 0x66, isXMM_XMMorMEM, static_cast(imm), 0x3A); } +void pclmullqlqdq(const Xmm& xmm, const Operand& op) { pclmulqdq(xmm, op, 0x00); } +void pclmulhqlqdq(const Xmm& xmm, const Operand& op) { pclmulqdq(xmm, op, 0x01); } +void pclmullqhdq(const Xmm& xmm, const Operand& op) { pclmulqdq(xmm, op, 0x10); } +void pclmulhqhdq(const Xmm& xmm, const Operand& op) { pclmulqdq(xmm, op, 0x11); } +void ldmxcsr(const Address& addr) { opModM(addr, Reg32(2), 0x0F, 0xAE); } +void stmxcsr(const Address& addr) { opModM(addr, Reg32(3), 0x0F, 0xAE); } +void clflush(const Address& addr) { opModM(addr, Reg32(7), 0x0F, 0xAE); } +void fldcw(const Address& addr) { opModM(addr, Reg32(5), 0xD9, 0x100); } +void fstcw(const Address& addr) { db(0x9B); opModM(addr, Reg32(7), 0xD9, NONE); } +void movntpd(const Address& addr, const Xmm& reg) { opModM(addr, Reg16(reg.getIdx()), 0x0F, 0x2B); } +void movntdq(const Address& addr, const Xmm& reg) { opModM(addr, Reg16(reg.getIdx()), 0x0F, 0xE7); } +void movsx(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0xBE); } +void movzx(const Reg& reg, const Operand& op) { opMovxx(reg, op, 0xB6); } +void fadd(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 0, 0); } +void fiadd(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 0, 0); } +void fcom(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 2, 0); } +void fcomp(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 3, 0); } +void fdiv(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 6, 0); } +void fidiv(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 6, 0); } +void fdivr(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 7, 0); } +void fidivr(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 7, 0); } +void ficom(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 2, 0); } +void ficomp(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 3, 0); } +void fild(const Address& addr) { opFpuMem(addr, 0xDF, 0xDB, 0xDF, 0, 5); } +void fist(const Address& addr) { opFpuMem(addr, 0xDF, 0xDB, 0x00, 2, 0); } +void fistp(const Address& addr) { opFpuMem(addr, 0xDF, 0xDB, 0xDF, 3, 7); } +void fisttp(const Address& addr) { opFpuMem(addr, 0xDF, 0xDB, 0xDD, 1, 0); } +void fld(const Address& addr) { opFpuMem(addr, 0x00, 0xD9, 0xDD, 0, 0); } +void fmul(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 1, 0); } +void fimul(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 1, 0); } +void fst(const Address& addr) { opFpuMem(addr, 0x00, 0xD9, 0xDD, 2, 0); } +void fstp(const Address& addr) { opFpuMem(addr, 0x00, 0xD9, 0xDD, 3, 0); } +void fsub(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 4, 0); } +void fisub(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 4, 0); } +void fsubr(const Address& addr) { opFpuMem(addr, 0x00, 0xD8, 0xDC, 5, 0); } +void fisubr(const Address& addr) { opFpuMem(addr, 0xDE, 0xDA, 0x00, 5, 0); } +void fadd(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xD8C0, 0xDCC0); } +void fadd(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xD8C0, 0xDCC0); } +void faddp(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0x0000, 0xDEC0); } +void faddp(const Fpu& reg1) { opFpuFpu(reg1, st0, 0x0000, 0xDEC0); } +void fcmovb(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDAC0, 0x00C0); } +void fcmovb(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDAC0, 0x00C0); } +void fcmove(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDAC8, 0x00C8); } +void fcmove(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDAC8, 0x00C8); } +void fcmovbe(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDAD0, 0x00D0); } +void fcmovbe(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDAD0, 0x00D0); } +void fcmovu(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDAD8, 0x00D8); } +void fcmovu(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDAD8, 0x00D8); } +void fcmovnb(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDBC0, 0x00C0); } +void fcmovnb(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDBC0, 0x00C0); } +void fcmovne(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDBC8, 0x00C8); } +void fcmovne(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDBC8, 0x00C8); } +void fcmovnbe(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDBD0, 0x00D0); } +void fcmovnbe(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDBD0, 0x00D0); } +void fcmovnu(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDBD8, 0x00D8); } +void fcmovnu(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDBD8, 0x00D8); } +void fcomi(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDBF0, 0x00F0); } +void fcomi(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDBF0, 0x00F0); } +void fcomip(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDFF0, 0x00F0); } +void fcomip(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDFF0, 0x00F0); } +void fucomi(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDBE8, 0x00E8); } +void fucomi(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDBE8, 0x00E8); } +void fucomip(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xDFE8, 0x00E8); } +void fucomip(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xDFE8, 0x00E8); } +void fdiv(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xD8F0, 0xDCF8); } +void fdiv(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xD8F0, 0xDCF8); } +void fdivp(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0x0000, 0xDEF8); } +void fdivp(const Fpu& reg1) { opFpuFpu(reg1, st0, 0x0000, 0xDEF8); } +void fdivr(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xD8F8, 0xDCF0); } +void fdivr(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xD8F8, 0xDCF0); } +void fdivrp(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0x0000, 0xDEF0); } +void fdivrp(const Fpu& reg1) { opFpuFpu(reg1, st0, 0x0000, 0xDEF0); } +void fmul(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xD8C8, 0xDCC8); } +void fmul(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xD8C8, 0xDCC8); } +void fmulp(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0x0000, 0xDEC8); } +void fmulp(const Fpu& reg1) { opFpuFpu(reg1, st0, 0x0000, 0xDEC8); } +void fsub(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xD8E0, 0xDCE8); } +void fsub(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xD8E0, 0xDCE8); } +void fsubp(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0x0000, 0xDEE8); } +void fsubp(const Fpu& reg1) { opFpuFpu(reg1, st0, 0x0000, 0xDEE8); } +void fsubr(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0xD8E8, 0xDCE0); } +void fsubr(const Fpu& reg1) { opFpuFpu(st0, reg1, 0xD8E8, 0xDCE0); } +void fsubrp(const Fpu& reg1, const Fpu& reg2) { opFpuFpu(reg1, reg2, 0x0000, 0xDEE0); } +void fsubrp(const Fpu& reg1) { opFpuFpu(reg1, st0, 0x0000, 0xDEE0); } +void fcom(const Fpu& reg) { opFpu(reg, 0xD8, 0xD0); } +void fcomp(const Fpu& reg) { opFpu(reg, 0xD8, 0xD8); } +void ffree(const Fpu& reg) { opFpu(reg, 0xDD, 0xC0); } +void fld(const Fpu& reg) { opFpu(reg, 0xD9, 0xC0); } +void fst(const Fpu& reg) { opFpu(reg, 0xDD, 0xD0); } +void fstp(const Fpu& reg) { opFpu(reg, 0xDD, 0xD8); } +void fucom(const Fpu& reg) { opFpu(reg, 0xDD, 0xE0); } +void fucomp(const Fpu& reg) { opFpu(reg, 0xDD, 0xE8); } +void fxch(const Fpu& reg) { opFpu(reg, 0xD9, 0xC8); } +void vaddpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x58, true); } +void vaddps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x58, true); } +void vaddsd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x58, false); } +void vaddss(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F3, 0x58, false); } +void vsubpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x5C, true); } +void vsubps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x5C, true); } +void vsubsd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x5C, false); } +void vsubss(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F3, 0x5C, false); } +void vmulpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x59, true); } +void vmulps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x59, true); } +void vmulsd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x59, false); } +void vmulss(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F3, 0x59, false); } +void vdivpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x5E, true); } +void vdivps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x5E, true); } +void vdivsd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x5E, false); } +void vdivss(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F3, 0x5E, false); } +void vmaxpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x5F, true); } +void vmaxps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x5F, true); } +void vmaxsd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x5F, false); } +void vmaxss(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F3, 0x5F, false); } +void vminpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x5D, true); } +void vminps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x5D, true); } +void vminsd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x5D, false); } +void vminss(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F3, 0x5D, false); } +void vandpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x54, true); } +void vandps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x54, true); } +void vandnpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x55, true); } +void vandnps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x55, true); } +void vorpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x56, true); } +void vorps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x56, true); } +void vxorpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x57, true); } +void vxorps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F, 0x57, true); } +void vblendpd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x0D, true, 0, imm); } +void vblendpd(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x0D, true, 0, imm); } +void vblendps(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x0C, true, 0, imm); } +void vblendps(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x0C, true, 0, imm); } +void vdppd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x41, false, 0, imm); } +void vdppd(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x41, false, 0, imm); } +void vdpps(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x40, true, 0, imm); } +void vdpps(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x40, true, 0, imm); } +void vmpsadbw(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x42, true, 0, imm); } +void vmpsadbw(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x42, true, 0, imm); } +void vpblendw(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x0E, true, 0, imm); } +void vpblendw(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x0E, true, 0, imm); } +void vpblendd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x02, true, 0, imm); } +void vpblendd(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x02, true, 0, imm); } +void vroundsd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x0B, false, 0, imm); } +void vroundsd(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x0B, false, 0, imm); } +void vroundss(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x0A, false, 0, imm); } +void vroundss(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x0A, false, 0, imm); } +void vpclmulqdq(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x44, false, 0, imm); } +void vpclmulqdq(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x44, false, 0, imm); } +void vpermilps(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x0C, true, 0); } +void vpermilpd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x0D, true, 0); } +void vpsllvd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x47, true, 0); } +void vpsllvq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x47, true, 1); } +void vpsravd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x46, true, 0); } +void vpsrlvd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x45, true, 0); } +void vpsrlvq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x45, true, 1); } +void vcmppd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xC2, true, -1, imm); } +void vcmppd(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xC2, true, -1, imm); } +void vcmpps(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F, 0xC2, true, -1, imm); } +void vcmpps(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F, 0xC2, true, -1, imm); } +void vcmpsd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F2, 0xC2, false, -1, imm); } +void vcmpsd(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F2, 0xC2, false, -1, imm); } +void vcmpss(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F3, 0xC2, false, -1, imm); } +void vcmpss(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F3, 0xC2, false, -1, imm); } +void vcvtsd2ss(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F2, 0x5A, false, -1); } +void vcvtsd2ss(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F2, 0x5A, false, -1); } +void vcvtss2sd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F3, 0x5A, false, -1); } +void vcvtss2sd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F3, 0x5A, false, -1); } +void vinsertps(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x21, false, 0, imm); } +void vinsertps(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x21, false, 0, imm); } +void vpacksswb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x63, true, -1); } +void vpacksswb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x63, true, -1); } +void vpackssdw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x6B, true, -1); } +void vpackssdw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x6B, true, -1); } +void vpackuswb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x67, true, -1); } +void vpackuswb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x67, true, -1); } +void vpackusdw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x2B, true, -1); } +void vpackusdw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x2B, true, -1); } +void vpaddb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xFC, true, -1); } +void vpaddb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xFC, true, -1); } +void vpaddw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xFD, true, -1); } +void vpaddw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xFD, true, -1); } +void vpaddd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xFE, true, -1); } +void vpaddd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xFE, true, -1); } +void vpaddq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xD4, true, -1); } +void vpaddq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xD4, true, -1); } +void vpaddsb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xEC, true, -1); } +void vpaddsb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xEC, true, -1); } +void vpaddsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xED, true, -1); } +void vpaddsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xED, true, -1); } +void vpaddusb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xDC, true, -1); } +void vpaddusb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xDC, true, -1); } +void vpaddusw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xDD, true, -1); } +void vpaddusw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xDD, true, -1); } +void vpalignr(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x0F, true, -1, imm); } +void vpalignr(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F3A | PP_66, 0x0F, true, -1, imm); } +void vpand(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xDB, true, -1); } +void vpand(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xDB, true, -1); } +void vpandn(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xDF, true, -1); } +void vpandn(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xDF, true, -1); } +void vpavgb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE0, true, -1); } +void vpavgb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE0, true, -1); } +void vpavgw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE3, true, -1); } +void vpavgw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE3, true, -1); } +void vpcmpeqb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x74, true, -1); } +void vpcmpeqb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x74, true, -1); } +void vpcmpeqw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x75, true, -1); } +void vpcmpeqw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x75, true, -1); } +void vpcmpeqd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x76, true, -1); } +void vpcmpeqd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x76, true, -1); } +void vpcmpeqq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x29, true, -1); } +void vpcmpeqq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x29, true, -1); } +void vpcmpgtb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x64, true, -1); } +void vpcmpgtb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x64, true, -1); } +void vpcmpgtw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x65, true, -1); } +void vpcmpgtw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x65, true, -1); } +void vpcmpgtd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x66, true, -1); } +void vpcmpgtd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x66, true, -1); } +void vpcmpgtq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x37, true, -1); } +void vpcmpgtq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x37, true, -1); } +void vphaddw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x01, true, -1); } +void vphaddw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x01, true, -1); } +void vphaddd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x02, true, -1); } +void vphaddd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x02, true, -1); } +void vphaddsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x03, true, -1); } +void vphaddsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x03, true, -1); } +void vphsubw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x05, true, -1); } +void vphsubw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x05, true, -1); } +void vphsubd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x06, true, -1); } +void vphsubd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x06, true, -1); } +void vphsubsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x07, true, -1); } +void vphsubsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x07, true, -1); } +void vpmaddwd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF5, true, -1); } +void vpmaddwd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF5, true, -1); } +void vpmaddubsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x04, true, -1); } +void vpmaddubsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x04, true, -1); } +void vpmaxsb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x3C, true, -1); } +void vpmaxsb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x3C, true, -1); } +void vpmaxsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xEE, true, -1); } +void vpmaxsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xEE, true, -1); } +void vpmaxsd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x3D, true, -1); } +void vpmaxsd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x3D, true, -1); } +void vpmaxub(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xDE, true, -1); } +void vpmaxub(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xDE, true, -1); } +void vpmaxuw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x3E, true, -1); } +void vpmaxuw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x3E, true, -1); } +void vpmaxud(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x3F, true, -1); } +void vpmaxud(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x3F, true, -1); } +void vpminsb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x38, true, -1); } +void vpminsb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x38, true, -1); } +void vpminsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xEA, true, -1); } +void vpminsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xEA, true, -1); } +void vpminsd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x39, true, -1); } +void vpminsd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x39, true, -1); } +void vpminub(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xDA, true, -1); } +void vpminub(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xDA, true, -1); } +void vpminuw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x3A, true, -1); } +void vpminuw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x3A, true, -1); } +void vpminud(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x3B, true, -1); } +void vpminud(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x3B, true, -1); } +void vpmulhuw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE4, true, -1); } +void vpmulhuw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE4, true, -1); } +void vpmulhrsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x0B, true, -1); } +void vpmulhrsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x0B, true, -1); } +void vpmulhw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE5, true, -1); } +void vpmulhw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE5, true, -1); } +void vpmullw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xD5, true, -1); } +void vpmullw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xD5, true, -1); } +void vpmulld(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x40, true, -1); } +void vpmulld(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x40, true, -1); } +void vpmuludq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF4, false, -1); } +void vpmuludq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF4, false, -1); } +void vpmuldq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x28, true, -1); } +void vpmuldq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x28, true, -1); } +void vpor(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xEB, true, -1); } +void vpor(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xEB, true, -1); } +void vpsadbw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF6, true, -1); } +void vpsadbw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF6, true, -1); } +void vpshufb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x00, true, -1); } +void vpsignb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x08, true, -1); } +void vpsignb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x08, true, -1); } +void vpsignw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x09, true, -1); } +void vpsignw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x09, true, -1); } +void vpsignd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F38 | PP_66, 0x0A, true, -1); } +void vpsignd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F38 | PP_66, 0x0A, true, -1); } +void vpsllw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF1, true, -1); } +void vpsllw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF1, true, -1); } +void vpslld(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF2, true, -1); } +void vpslld(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF2, true, -1); } +void vpsllq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF3, true, -1); } +void vpsllq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF3, true, -1); } +void vpsraw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE1, true, -1); } +void vpsraw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE1, true, -1); } +void vpsrad(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE2, true, -1); } +void vpsrad(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE2, true, -1); } +void vpsrlw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xD1, true, -1); } +void vpsrlw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xD1, true, -1); } +void vpsrld(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xD2, true, -1); } +void vpsrld(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xD2, true, -1); } +void vpsrlq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xD3, true, -1); } +void vpsrlq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xD3, true, -1); } +void vpsubb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF8, true, -1); } +void vpsubb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF8, true, -1); } +void vpsubw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xF9, true, -1); } +void vpsubw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xF9, true, -1); } +void vpsubd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xFA, true, -1); } +void vpsubd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xFA, true, -1); } +void vpsubq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xFB, true, -1); } +void vpsubq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xFB, true, -1); } +void vpsubsb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE8, true, -1); } +void vpsubsb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE8, true, -1); } +void vpsubsw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xE9, true, -1); } +void vpsubsw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xE9, true, -1); } +void vpsubusb(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xD8, true, -1); } +void vpsubusb(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xD8, true, -1); } +void vpsubusw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xD9, true, -1); } +void vpsubusw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xD9, true, -1); } +void vpunpckhbw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x68, true, -1); } +void vpunpckhbw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x68, true, -1); } +void vpunpckhwd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x69, true, -1); } +void vpunpckhwd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x69, true, -1); } +void vpunpckhdq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x6A, true, -1); } +void vpunpckhdq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x6A, true, -1); } +void vpunpckhqdq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x6D, true, -1); } +void vpunpckhqdq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x6D, true, -1); } +void vpunpcklbw(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x60, true, -1); } +void vpunpcklbw(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x60, true, -1); } +void vpunpcklwd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x61, true, -1); } +void vpunpcklwd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x61, true, -1); } +void vpunpckldq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x62, true, -1); } +void vpunpckldq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x62, true, -1); } +void vpunpcklqdq(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x6C, true, -1); } +void vpunpcklqdq(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x6C, true, -1); } +void vpxor(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xEF, true, -1); } +void vpxor(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xEF, true, -1); } +void vrcpss(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F3, 0x53, false, -1); } +void vrcpss(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F3, 0x53, false, -1); } +void vrsqrtss(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F3, 0x52, false, -1); } +void vrsqrtss(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F3, 0x52, false, -1); } +void vshufpd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0xC6, true, -1, imm); } +void vshufpd(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0xC6, true, -1, imm); } +void vshufps(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { opAVX_X_X_XM(x1, x2, op, MM_0F, 0xC6, true, -1, imm); } +void vshufps(const Xmm& xmm, const Operand& op, uint8 imm) { opAVX_X_X_XM(xmm, xmm, op, MM_0F, 0xC6, true, -1, imm); } +void vsqrtsd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F2, 0x51, false, -1); } +void vsqrtsd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F2, 0x51, false, -1); } +void vsqrtss(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F3, 0x51, false, -1); } +void vsqrtss(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_F3, 0x51, false, -1); } +void vunpckhpd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x15, true, -1); } +void vunpckhpd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x15, true, -1); } +void vunpckhps(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F, 0x15, true, -1); } +void vunpckhps(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F, 0x15, true, -1); } +void vunpcklpd(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F | PP_66, 0x14, true, -1); } +void vunpcklpd(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F | PP_66, 0x14, true, -1); } +void vunpcklps(const Xmm& x1, const Xmm& x2, const Operand& op) { opAVX_X_X_XM(x1, x2, op, MM_0F, 0x14, true, -1); } +void vunpcklps(const Xmm& xmm, const Operand& op) { opAVX_X_X_XM(xmm, xmm, op, MM_0F, 0x14, true, -1); } +void vaeskeygenassist(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0xDF, false, 0, imm); } +void vroundpd(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x09, true, 0, imm); } +void vroundps(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x08, true, 0, imm); } +void vpermilpd(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x05, true, 0, imm); } +void vpermilps(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x04, true, 0, imm); } +void vpcmpestri(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x61, false, 0, imm); } +void vpcmpestrm(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x60, false, 0, imm); } +void vpcmpistri(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x63, false, 0, imm); } +void vpcmpistrm(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F3A | PP_66, 0x62, false, 0, imm); } +void vtestps(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x0E, true, 0); } +void vtestpd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x0F, true, 0); } +void vcomisd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x2F, false, -1); } +void vcomiss(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x2F, false, -1); } +void vcvtdq2ps(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x5B, true, -1); } +void vcvtps2dq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x5B, true, -1); } +void vcvttps2dq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_F3, 0x5B, true, -1); } +void vmovapd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x28, true, -1); } +void vmovaps(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x28, true, -1); } +void vmovddup(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_F2, 0x12, true, -1); } +void vmovdqa(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x6F, true, -1); } +void vmovdqu(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_F3, 0x6F, true, -1); } +void vmovshdup(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_F3, 0x16, true, -1); } +void vmovsldup(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_F3, 0x12, true, -1); } +void vmovupd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x10, true, -1); } +void vmovups(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x10, true, -1); } +void vpabsb(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x1C, true, -1); } +void vpabsw(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x1D, true, -1); } +void vpabsd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x1E, true, -1); } +void vphminposuw(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x41, false, -1); } +void vpmovsxbw(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x20, true, -1); } +void vpmovsxbd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x21, true, -1); } +void vpmovsxbq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x22, true, -1); } +void vpmovsxwd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x23, true, -1); } +void vpmovsxwq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x24, true, -1); } +void vpmovsxdq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x25, true, -1); } +void vpmovzxbw(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x30, true, -1); } +void vpmovzxbd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x31, true, -1); } +void vpmovzxbq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x32, true, -1); } +void vpmovzxwd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x33, true, -1); } +void vpmovzxwq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x34, true, -1); } +void vpmovzxdq(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x35, true, -1); } +void vpshufd(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x70, true, -1, imm); } +void vpshufhw(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_F3, 0x70, true, -1, imm); } +void vpshuflw(const Xmm& xm, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_F2, 0x70, true, -1, imm); } +void vptest(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F38 | PP_66, 0x17, false, -1); } +void vrcpps(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x53, true, -1); } +void vrsqrtps(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x52, true, -1); } +void vsqrtpd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x51, true, -1); } +void vsqrtps(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x51, true, -1); } +void vucomisd(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F | PP_66, 0x2E, false, -1); } +void vucomiss(const Xmm& xm, const Operand& op) { opAVX_X_XM_IMM(xm, op, MM_0F, 0x2E, false, -1); } +void vmovapd(const Address& addr, const Xmm& xmm) { opAVX_X_XM_IMM(xmm, addr, MM_0F | PP_66, 0x29, true, -1); } +void vmovaps(const Address& addr, const Xmm& xmm) { opAVX_X_XM_IMM(xmm, addr, MM_0F, 0x29, true, -1); } +void vmovdqa(const Address& addr, const Xmm& xmm) { opAVX_X_XM_IMM(xmm, addr, MM_0F | PP_66, 0x7F, true, -1); } +void vmovdqu(const Address& addr, const Xmm& xmm) { opAVX_X_XM_IMM(xmm, addr, MM_0F | PP_F3, 0x7F, true, -1); } +void vmovupd(const Address& addr, const Xmm& xmm) { opAVX_X_XM_IMM(xmm, addr, MM_0F | PP_66, 0x11, true, -1); } +void vmovups(const Address& addr, const Xmm& xmm) { opAVX_X_XM_IMM(xmm, addr, MM_0F, 0x11, true, -1); } +void vaddsubpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0xD0, true, -1); } +void vaddsubps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0xD0, true, -1); } +void vhaddpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x7C, true, -1); } +void vhaddps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x7C, true, -1); } +void vhsubpd(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_66, 0x7D, true, -1); } +void vhsubps(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F | PP_F2, 0x7D, true, -1); } +void vaesenc(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xDC, false, 0); } +void vaesenclast(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xDD, false, 0); } +void vaesdec(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xDE, false, 0); } +void vaesdeclast(const Xmm& xmm, const Operand& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xDF, false, 0); } +void vmaskmovps(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, MM_0F38 | PP_66, 0x2C, true, 0); } +void vmaskmovps(const Address& addr, const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x2, x1, addr, MM_0F38 | PP_66, 0x2E, true, 0); } +void vmaskmovpd(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, MM_0F38 | PP_66, 0x2D, true, 0); } +void vmaskmovpd(const Address& addr, const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x2, x1, addr, MM_0F38 | PP_66, 0x2F, true, 0); } +void vpmaskmovd(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, MM_0F38 | PP_66, 0x8C, true, 0); } +void vpmaskmovd(const Address& addr, const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x2, x1, addr, MM_0F38 | PP_66, 0x8E, true, 0); } +void vpmaskmovq(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, MM_0F38 | PP_66, 0x8C, true, 1); } +void vpmaskmovq(const Address& addr, const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x2, x1, addr, MM_0F38 | PP_66, 0x8E, true, 1); } +void vpermd(const Ymm& y1, const Ymm& y2, const Operand& op) { opAVX_X_X_XM(y1, y2, op, MM_0F38 | PP_66, 0x36, true, 0); } +void vpermps(const Ymm& y1, const Ymm& y2, const Operand& op) { opAVX_X_X_XM(y1, y2, op, MM_0F38 | PP_66, 0x16, true, 0); } +void vpermq(const Ymm& y, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(y, op, MM_0F3A | PP_66, 0x00, true, 1, imm); } +void vpermpd(const Ymm& y, const Operand& op, uint8 imm) { opAVX_X_XM_IMM(y, op, MM_0F3A | PP_66, 0x01, true, 1, imm); } +void cmpeqpd(const Xmm& x, const Operand& op) { cmppd(x, op, 0); } +void vcmpeqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 0); } +void vcmpeqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 0); } +void cmpltpd(const Xmm& x, const Operand& op) { cmppd(x, op, 1); } +void vcmpltpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 1); } +void vcmpltpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 1); } +void cmplepd(const Xmm& x, const Operand& op) { cmppd(x, op, 2); } +void vcmplepd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 2); } +void vcmplepd(const Xmm& x, const Operand& op) { vcmppd(x, op, 2); } +void cmpunordpd(const Xmm& x, const Operand& op) { cmppd(x, op, 3); } +void vcmpunordpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 3); } +void vcmpunordpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 3); } +void cmpneqpd(const Xmm& x, const Operand& op) { cmppd(x, op, 4); } +void vcmpneqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 4); } +void vcmpneqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 4); } +void cmpnltpd(const Xmm& x, const Operand& op) { cmppd(x, op, 5); } +void vcmpnltpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 5); } +void vcmpnltpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 5); } +void cmpnlepd(const Xmm& x, const Operand& op) { cmppd(x, op, 6); } +void vcmpnlepd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 6); } +void vcmpnlepd(const Xmm& x, const Operand& op) { vcmppd(x, op, 6); } +void cmpordpd(const Xmm& x, const Operand& op) { cmppd(x, op, 7); } +void vcmpordpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 7); } +void vcmpordpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 7); } +void vcmpeq_uqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 8); } +void vcmpeq_uqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 8); } +void vcmpngepd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 9); } +void vcmpngepd(const Xmm& x, const Operand& op) { vcmppd(x, op, 9); } +void vcmpngtpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 10); } +void vcmpngtpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 10); } +void vcmpfalsepd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 11); } +void vcmpfalsepd(const Xmm& x, const Operand& op) { vcmppd(x, op, 11); } +void vcmpneq_oqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 12); } +void vcmpneq_oqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 12); } +void vcmpgepd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 13); } +void vcmpgepd(const Xmm& x, const Operand& op) { vcmppd(x, op, 13); } +void vcmpgtpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 14); } +void vcmpgtpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 14); } +void vcmptruepd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 15); } +void vcmptruepd(const Xmm& x, const Operand& op) { vcmppd(x, op, 15); } +void vcmpeq_ospd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 16); } +void vcmpeq_ospd(const Xmm& x, const Operand& op) { vcmppd(x, op, 16); } +void vcmplt_oqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 17); } +void vcmplt_oqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 17); } +void vcmple_oqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 18); } +void vcmple_oqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 18); } +void vcmpunord_spd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 19); } +void vcmpunord_spd(const Xmm& x, const Operand& op) { vcmppd(x, op, 19); } +void vcmpneq_uspd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 20); } +void vcmpneq_uspd(const Xmm& x, const Operand& op) { vcmppd(x, op, 20); } +void vcmpnlt_uqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 21); } +void vcmpnlt_uqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 21); } +void vcmpnle_uqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 22); } +void vcmpnle_uqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 22); } +void vcmpord_spd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 23); } +void vcmpord_spd(const Xmm& x, const Operand& op) { vcmppd(x, op, 23); } +void vcmpeq_uspd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 24); } +void vcmpeq_uspd(const Xmm& x, const Operand& op) { vcmppd(x, op, 24); } +void vcmpnge_uqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 25); } +void vcmpnge_uqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 25); } +void vcmpngt_uqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 26); } +void vcmpngt_uqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 26); } +void vcmpfalse_ospd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 27); } +void vcmpfalse_ospd(const Xmm& x, const Operand& op) { vcmppd(x, op, 27); } +void vcmpneq_ospd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 28); } +void vcmpneq_ospd(const Xmm& x, const Operand& op) { vcmppd(x, op, 28); } +void vcmpge_oqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 29); } +void vcmpge_oqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 29); } +void vcmpgt_oqpd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 30); } +void vcmpgt_oqpd(const Xmm& x, const Operand& op) { vcmppd(x, op, 30); } +void vcmptrue_uspd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmppd(x1, x2, op, 31); } +void vcmptrue_uspd(const Xmm& x, const Operand& op) { vcmppd(x, op, 31); } +void cmpeqps(const Xmm& x, const Operand& op) { cmpps(x, op, 0); } +void vcmpeqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 0); } +void vcmpeqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 0); } +void cmpltps(const Xmm& x, const Operand& op) { cmpps(x, op, 1); } +void vcmpltps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 1); } +void vcmpltps(const Xmm& x, const Operand& op) { vcmpps(x, op, 1); } +void cmpleps(const Xmm& x, const Operand& op) { cmpps(x, op, 2); } +void vcmpleps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 2); } +void vcmpleps(const Xmm& x, const Operand& op) { vcmpps(x, op, 2); } +void cmpunordps(const Xmm& x, const Operand& op) { cmpps(x, op, 3); } +void vcmpunordps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 3); } +void vcmpunordps(const Xmm& x, const Operand& op) { vcmpps(x, op, 3); } +void cmpneqps(const Xmm& x, const Operand& op) { cmpps(x, op, 4); } +void vcmpneqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 4); } +void vcmpneqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 4); } +void cmpnltps(const Xmm& x, const Operand& op) { cmpps(x, op, 5); } +void vcmpnltps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 5); } +void vcmpnltps(const Xmm& x, const Operand& op) { vcmpps(x, op, 5); } +void cmpnleps(const Xmm& x, const Operand& op) { cmpps(x, op, 6); } +void vcmpnleps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 6); } +void vcmpnleps(const Xmm& x, const Operand& op) { vcmpps(x, op, 6); } +void cmpordps(const Xmm& x, const Operand& op) { cmpps(x, op, 7); } +void vcmpordps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 7); } +void vcmpordps(const Xmm& x, const Operand& op) { vcmpps(x, op, 7); } +void vcmpeq_uqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 8); } +void vcmpeq_uqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 8); } +void vcmpngeps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 9); } +void vcmpngeps(const Xmm& x, const Operand& op) { vcmpps(x, op, 9); } +void vcmpngtps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 10); } +void vcmpngtps(const Xmm& x, const Operand& op) { vcmpps(x, op, 10); } +void vcmpfalseps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 11); } +void vcmpfalseps(const Xmm& x, const Operand& op) { vcmpps(x, op, 11); } +void vcmpneq_oqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 12); } +void vcmpneq_oqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 12); } +void vcmpgeps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 13); } +void vcmpgeps(const Xmm& x, const Operand& op) { vcmpps(x, op, 13); } +void vcmpgtps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 14); } +void vcmpgtps(const Xmm& x, const Operand& op) { vcmpps(x, op, 14); } +void vcmptrueps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 15); } +void vcmptrueps(const Xmm& x, const Operand& op) { vcmpps(x, op, 15); } +void vcmpeq_osps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 16); } +void vcmpeq_osps(const Xmm& x, const Operand& op) { vcmpps(x, op, 16); } +void vcmplt_oqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 17); } +void vcmplt_oqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 17); } +void vcmple_oqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 18); } +void vcmple_oqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 18); } +void vcmpunord_sps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 19); } +void vcmpunord_sps(const Xmm& x, const Operand& op) { vcmpps(x, op, 19); } +void vcmpneq_usps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 20); } +void vcmpneq_usps(const Xmm& x, const Operand& op) { vcmpps(x, op, 20); } +void vcmpnlt_uqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 21); } +void vcmpnlt_uqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 21); } +void vcmpnle_uqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 22); } +void vcmpnle_uqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 22); } +void vcmpord_sps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 23); } +void vcmpord_sps(const Xmm& x, const Operand& op) { vcmpps(x, op, 23); } +void vcmpeq_usps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 24); } +void vcmpeq_usps(const Xmm& x, const Operand& op) { vcmpps(x, op, 24); } +void vcmpnge_uqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 25); } +void vcmpnge_uqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 25); } +void vcmpngt_uqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 26); } +void vcmpngt_uqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 26); } +void vcmpfalse_osps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 27); } +void vcmpfalse_osps(const Xmm& x, const Operand& op) { vcmpps(x, op, 27); } +void vcmpneq_osps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 28); } +void vcmpneq_osps(const Xmm& x, const Operand& op) { vcmpps(x, op, 28); } +void vcmpge_oqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 29); } +void vcmpge_oqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 29); } +void vcmpgt_oqps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 30); } +void vcmpgt_oqps(const Xmm& x, const Operand& op) { vcmpps(x, op, 30); } +void vcmptrue_usps(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpps(x1, x2, op, 31); } +void vcmptrue_usps(const Xmm& x, const Operand& op) { vcmpps(x, op, 31); } +void cmpeqsd(const Xmm& x, const Operand& op) { cmpsd(x, op, 0); } +void vcmpeqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 0); } +void vcmpeqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 0); } +void cmpltsd(const Xmm& x, const Operand& op) { cmpsd(x, op, 1); } +void vcmpltsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 1); } +void vcmpltsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 1); } +void cmplesd(const Xmm& x, const Operand& op) { cmpsd(x, op, 2); } +void vcmplesd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 2); } +void vcmplesd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 2); } +void cmpunordsd(const Xmm& x, const Operand& op) { cmpsd(x, op, 3); } +void vcmpunordsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 3); } +void vcmpunordsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 3); } +void cmpneqsd(const Xmm& x, const Operand& op) { cmpsd(x, op, 4); } +void vcmpneqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 4); } +void vcmpneqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 4); } +void cmpnltsd(const Xmm& x, const Operand& op) { cmpsd(x, op, 5); } +void vcmpnltsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 5); } +void vcmpnltsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 5); } +void cmpnlesd(const Xmm& x, const Operand& op) { cmpsd(x, op, 6); } +void vcmpnlesd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 6); } +void vcmpnlesd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 6); } +void cmpordsd(const Xmm& x, const Operand& op) { cmpsd(x, op, 7); } +void vcmpordsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 7); } +void vcmpordsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 7); } +void vcmpeq_uqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 8); } +void vcmpeq_uqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 8); } +void vcmpngesd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 9); } +void vcmpngesd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 9); } +void vcmpngtsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 10); } +void vcmpngtsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 10); } +void vcmpfalsesd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 11); } +void vcmpfalsesd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 11); } +void vcmpneq_oqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 12); } +void vcmpneq_oqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 12); } +void vcmpgesd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 13); } +void vcmpgesd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 13); } +void vcmpgtsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 14); } +void vcmpgtsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 14); } +void vcmptruesd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 15); } +void vcmptruesd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 15); } +void vcmpeq_ossd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 16); } +void vcmpeq_ossd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 16); } +void vcmplt_oqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 17); } +void vcmplt_oqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 17); } +void vcmple_oqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 18); } +void vcmple_oqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 18); } +void vcmpunord_ssd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 19); } +void vcmpunord_ssd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 19); } +void vcmpneq_ussd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 20); } +void vcmpneq_ussd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 20); } +void vcmpnlt_uqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 21); } +void vcmpnlt_uqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 21); } +void vcmpnle_uqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 22); } +void vcmpnle_uqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 22); } +void vcmpord_ssd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 23); } +void vcmpord_ssd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 23); } +void vcmpeq_ussd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 24); } +void vcmpeq_ussd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 24); } +void vcmpnge_uqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 25); } +void vcmpnge_uqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 25); } +void vcmpngt_uqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 26); } +void vcmpngt_uqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 26); } +void vcmpfalse_ossd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 27); } +void vcmpfalse_ossd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 27); } +void vcmpneq_ossd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 28); } +void vcmpneq_ossd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 28); } +void vcmpge_oqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 29); } +void vcmpge_oqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 29); } +void vcmpgt_oqsd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 30); } +void vcmpgt_oqsd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 30); } +void vcmptrue_ussd(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpsd(x1, x2, op, 31); } +void vcmptrue_ussd(const Xmm& x, const Operand& op) { vcmpsd(x, op, 31); } +void cmpeqss(const Xmm& x, const Operand& op) { cmpss(x, op, 0); } +void vcmpeqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 0); } +void vcmpeqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 0); } +void cmpltss(const Xmm& x, const Operand& op) { cmpss(x, op, 1); } +void vcmpltss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 1); } +void vcmpltss(const Xmm& x, const Operand& op) { vcmpss(x, op, 1); } +void cmpless(const Xmm& x, const Operand& op) { cmpss(x, op, 2); } +void vcmpless(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 2); } +void vcmpless(const Xmm& x, const Operand& op) { vcmpss(x, op, 2); } +void cmpunordss(const Xmm& x, const Operand& op) { cmpss(x, op, 3); } +void vcmpunordss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 3); } +void vcmpunordss(const Xmm& x, const Operand& op) { vcmpss(x, op, 3); } +void cmpneqss(const Xmm& x, const Operand& op) { cmpss(x, op, 4); } +void vcmpneqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 4); } +void vcmpneqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 4); } +void cmpnltss(const Xmm& x, const Operand& op) { cmpss(x, op, 5); } +void vcmpnltss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 5); } +void vcmpnltss(const Xmm& x, const Operand& op) { vcmpss(x, op, 5); } +void cmpnless(const Xmm& x, const Operand& op) { cmpss(x, op, 6); } +void vcmpnless(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 6); } +void vcmpnless(const Xmm& x, const Operand& op) { vcmpss(x, op, 6); } +void cmpordss(const Xmm& x, const Operand& op) { cmpss(x, op, 7); } +void vcmpordss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 7); } +void vcmpordss(const Xmm& x, const Operand& op) { vcmpss(x, op, 7); } +void vcmpeq_uqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 8); } +void vcmpeq_uqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 8); } +void vcmpngess(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 9); } +void vcmpngess(const Xmm& x, const Operand& op) { vcmpss(x, op, 9); } +void vcmpngtss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 10); } +void vcmpngtss(const Xmm& x, const Operand& op) { vcmpss(x, op, 10); } +void vcmpfalsess(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 11); } +void vcmpfalsess(const Xmm& x, const Operand& op) { vcmpss(x, op, 11); } +void vcmpneq_oqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 12); } +void vcmpneq_oqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 12); } +void vcmpgess(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 13); } +void vcmpgess(const Xmm& x, const Operand& op) { vcmpss(x, op, 13); } +void vcmpgtss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 14); } +void vcmpgtss(const Xmm& x, const Operand& op) { vcmpss(x, op, 14); } +void vcmptruess(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 15); } +void vcmptruess(const Xmm& x, const Operand& op) { vcmpss(x, op, 15); } +void vcmpeq_osss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 16); } +void vcmpeq_osss(const Xmm& x, const Operand& op) { vcmpss(x, op, 16); } +void vcmplt_oqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 17); } +void vcmplt_oqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 17); } +void vcmple_oqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 18); } +void vcmple_oqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 18); } +void vcmpunord_sss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 19); } +void vcmpunord_sss(const Xmm& x, const Operand& op) { vcmpss(x, op, 19); } +void vcmpneq_usss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 20); } +void vcmpneq_usss(const Xmm& x, const Operand& op) { vcmpss(x, op, 20); } +void vcmpnlt_uqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 21); } +void vcmpnlt_uqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 21); } +void vcmpnle_uqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 22); } +void vcmpnle_uqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 22); } +void vcmpord_sss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 23); } +void vcmpord_sss(const Xmm& x, const Operand& op) { vcmpss(x, op, 23); } +void vcmpeq_usss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 24); } +void vcmpeq_usss(const Xmm& x, const Operand& op) { vcmpss(x, op, 24); } +void vcmpnge_uqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 25); } +void vcmpnge_uqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 25); } +void vcmpngt_uqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 26); } +void vcmpngt_uqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 26); } +void vcmpfalse_osss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 27); } +void vcmpfalse_osss(const Xmm& x, const Operand& op) { vcmpss(x, op, 27); } +void vcmpneq_osss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 28); } +void vcmpneq_osss(const Xmm& x, const Operand& op) { vcmpss(x, op, 28); } +void vcmpge_oqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 29); } +void vcmpge_oqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 29); } +void vcmpgt_oqss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 30); } +void vcmpgt_oqss(const Xmm& x, const Operand& op) { vcmpss(x, op, 30); } +void vcmptrue_usss(const Xmm& x1, const Xmm& x2, const Operand& op) { vcmpss(x1, x2, op, 31); } +void vcmptrue_usss(const Xmm& x, const Operand& op) { vcmpss(x, op, 31); } +void vmovhpd(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !op2.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x, op1, op2, MM_0F | PP_66, 0x16, false); } +void vmovhpd(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_66, 0x17, false); } +void vmovhps(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !op2.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x, op1, op2, MM_0F, 0x16, false); } +void vmovhps(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F, 0x17, false); } +void vmovlpd(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !op2.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x, op1, op2, MM_0F | PP_66, 0x12, false); } +void vmovlpd(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_66, 0x13, false); } +void vmovlps(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !op2.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x, op1, op2, MM_0F, 0x12, false); } +void vmovlps(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F, 0x13, false); } +void vfmadd132pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x98, true, 1); } +void vfmadd213pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA8, true, 1); } +void vfmadd231pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB8, true, 1); } +void vfmadd132ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x98, true, 0); } +void vfmadd213ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA8, true, 0); } +void vfmadd231ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB8, true, 0); } +void vfmadd132sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x99, false, 1); } +void vfmadd213sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA9, false, 1); } +void vfmadd231sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB9, false, 1); } +void vfmadd132ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x99, false, 0); } +void vfmadd213ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA9, false, 0); } +void vfmadd231ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB9, false, 0); } +void vfmaddsub132pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x96, true, 1); } +void vfmaddsub213pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA6, true, 1); } +void vfmaddsub231pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB6, true, 1); } +void vfmaddsub132ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x96, true, 0); } +void vfmaddsub213ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA6, true, 0); } +void vfmaddsub231ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB6, true, 0); } +void vfmsubadd132pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x97, true, 1); } +void vfmsubadd213pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA7, true, 1); } +void vfmsubadd231pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB7, true, 1); } +void vfmsubadd132ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x97, true, 0); } +void vfmsubadd213ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xA7, true, 0); } +void vfmsubadd231ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xB7, true, 0); } +void vfmsub132pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9A, true, 1); } +void vfmsub213pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAA, true, 1); } +void vfmsub231pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBA, true, 1); } +void vfmsub132ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9A, true, 0); } +void vfmsub213ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAA, true, 0); } +void vfmsub231ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBA, true, 0); } +void vfmsub132sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9B, false, 1); } +void vfmsub213sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAB, false, 1); } +void vfmsub231sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBB, false, 1); } +void vfmsub132ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9B, false, 0); } +void vfmsub213ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAB, false, 0); } +void vfmsub231ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBB, false, 0); } +void vfnmadd132pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9C, true, 1); } +void vfnmadd213pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAC, true, 1); } +void vfnmadd231pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBC, true, 1); } +void vfnmadd132ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9C, true, 0); } +void vfnmadd213ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAC, true, 0); } +void vfnmadd231ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBC, true, 0); } +void vfnmadd132sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9D, false, 1); } +void vfnmadd213sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAD, false, 1); } +void vfnmadd231sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBD, false, 1); } +void vfnmadd132ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9D, false, 0); } +void vfnmadd213ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAD, false, 0); } +void vfnmadd231ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBD, false, 0); } +void vfnmsub132pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9E, true, 1); } +void vfnmsub213pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAE, true, 1); } +void vfnmsub231pd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBE, true, 1); } +void vfnmsub132ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9E, true, 0); } +void vfnmsub213ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAE, true, 0); } +void vfnmsub231ps(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBE, true, 0); } +void vfnmsub132sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9F, false, 1); } +void vfnmsub213sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAF, false, 1); } +void vfnmsub231sd(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBF, false, 1); } +void vfnmsub132ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0x9F, false, 0); } +void vfnmsub213ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xAF, false, 0); } +void vfnmsub231ss(const Xmm& xmm, const Xmm& op1, const Operand& op2 = Operand()) { opAVX_X_X_XM(xmm, op1, op2, MM_0F38 | PP_66, 0xBF, false, 0); } +void vaesimc(const Xmm& x, const Operand& op) { opAVX_X_XM_IMM(x, op, MM_0F38 | PP_66, 0xDB, false, 0); } +void vbroadcastf128(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, MM_0F38 | PP_66, 0x1A, true, 0); } +void vbroadcasti128(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, MM_0F38 | PP_66, 0x5A, true, 0); } +void vbroadcastsd(const Ymm& y, const Operand& op) { if (!(op.isXMM() || op.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(y, op, MM_0F38 | PP_66, 0x19, true, 0); } +void vbroadcastss(const Xmm& x, const Operand& op) { if (!(op.isXMM() || op.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(x, op, MM_0F38 | PP_66, 0x18, true, 0); } +void vpbroadcastb(const Xmm& x, const Operand& op) { if (!(op.isXMM() || op.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(x, op, MM_0F38 | PP_66, 0x78, true, 0); } +void vpbroadcastw(const Xmm& x, const Operand& op) { if (!(op.isXMM() || op.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(x, op, MM_0F38 | PP_66, 0x79, true, 0); } +void vpbroadcastd(const Xmm& x, const Operand& op) { if (!(op.isXMM() || op.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(x, op, MM_0F38 | PP_66, 0x58, true, 0); } +void vpbroadcastq(const Xmm& x, const Operand& op) { if (!(op.isXMM() || op.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_XM_IMM(x, op, MM_0F38 | PP_66, 0x59, true, 0); } +void vextractf128(const Operand& op, const Ymm& y, uint8 imm) { opAVX_X_X_XMcvt(y, y.isXMM() ? xm0 : ym0, op, op.isXMM(), Operand::YMM, MM_0F3A | PP_66, 0x19, true, 0, imm); } +void vextracti128(const Operand& op, const Ymm& y, uint8 imm) { opAVX_X_X_XMcvt(y, y.isXMM() ? xm0 : ym0, op, op.isXMM(), Operand::YMM, MM_0F3A | PP_66, 0x39, true, 0, imm); } +void vextractps(const Operand& op, const Xmm& x, uint8 imm) { if (!(op.isREG(32) || op.isMEM()) || x.isYMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, x.isXMM() ? xm0 : ym0, op, op.isREG(), Operand::XMM, MM_0F3A | PP_66, 0x17, false, 0, imm); } +void vinsertf128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XMcvt(y1, y2, op, op.isXMM(), Operand::YMM, MM_0F3A | PP_66, 0x18, true, 0, imm); } +void vinserti128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XMcvt(y1, y2, op, op.isXMM(), Operand::YMM, MM_0F3A | PP_66, 0x38, true, 0, imm); } +void vperm2f128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XM(y1, y2, op, MM_0F3A | PP_66, 0x06, true, 0, imm); } +void vperm2i128(const Ymm& y1, const Ymm& y2, const Operand& op, uint8 imm) { opAVX_X_X_XM(y1, y2, op, MM_0F3A | PP_66, 0x46, true, 0, imm); } +void vlddqu(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, x.isXMM() ? xm0 : ym0, addr, MM_0F | PP_F2, 0xF0, true, 0); } +void vldmxcsr(const Address& addr) { opAVX_X_X_XM(xm2, xm0, addr, MM_0F, 0xAE, false, -1); } +void vstmxcsr(const Address& addr) { opAVX_X_X_XM(xm3, xm0, addr, MM_0F, 0xAE, false, -1); } +void vmaskmovdqu(const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x1, xm0, x2, MM_0F | PP_66, 0xF7, false, -1); } +void vpextrb(const Operand& op, const Xmm& x, uint8 imm) { if (!op.isREG(i32e) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, xm0, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x14, false, -1, imm); } +void vpextrw(const Reg& r, const Xmm& x, uint8 imm) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, x, MM_0F | PP_66, 0xC5, false, r.isBit(64) ? 1 : 0, imm); } +void vpextrw(const Address& addr, const Xmm& x, uint8 imm) { opAVX_X_X_XM(x, xm0, addr, MM_0F3A | PP_66, 0x15, false, -1, imm); } +void vpextrd(const Operand& op, const Xmm& x, uint8 imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, xm0, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x16, false, 0, imm); } +void vpinsrb(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x1, x2, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x20, false, -1, imm); } +void vpinsrb(const Xmm& x, const Operand& op, uint8 imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, x, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x20, false, -1, imm); } +void vpinsrw(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x1, x2, op, !op.isMEM(), Operand::XMM, MM_0F | PP_66, 0xC4, false, -1, imm); } +void vpinsrw(const Xmm& x, const Operand& op, uint8 imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, x, op, !op.isMEM(), Operand::XMM, MM_0F | PP_66, 0xC4, false, -1, imm); } +void vpinsrd(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x1, x2, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x22, false, 0, imm); } +void vpinsrd(const Xmm& x, const Operand& op, uint8 imm) { if (!op.isREG(32) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, x, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x22, false, 0, imm); } +void vpmovmskb(const Reg32e& r, const Xmm& x) { bool isYMM= x.isYMM(); opAVX_X_X_XM(isYMM ? Ymm(r.getIdx()) : Xmm(r.getIdx()), isYMM ? ym0 : xm0, x, MM_0F | PP_66, 0xD7, true); } +void vpslldq(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym7 : xm7, x1, x2, MM_0F | PP_66, 0x73, true, -1, imm); } +void vpslldq(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym7 : xm7, x, x, MM_0F | PP_66, 0x73, true, -1, imm); } +void vpsrldq(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym3 : xm3, x1, x2, MM_0F | PP_66, 0x73, true, -1, imm); } +void vpsrldq(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym3 : xm3, x, x, MM_0F | PP_66, 0x73, true, -1, imm); } +void vpsllw(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym6 : xm6, x1, x2, MM_0F | PP_66, 0x71, true, -1, imm); } +void vpsllw(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym6 : xm6, x, x, MM_0F | PP_66, 0x71, true, -1, imm); } +void vpslld(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym6 : xm6, x1, x2, MM_0F | PP_66, 0x72, true, -1, imm); } +void vpslld(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym6 : xm6, x, x, MM_0F | PP_66, 0x72, true, -1, imm); } +void vpsllq(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym6 : xm6, x1, x2, MM_0F | PP_66, 0x73, true, -1, imm); } +void vpsllq(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym6 : xm6, x, x, MM_0F | PP_66, 0x73, true, -1, imm); } +void vpsraw(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym4 : xm4, x1, x2, MM_0F | PP_66, 0x71, true, -1, imm); } +void vpsraw(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym4 : xm4, x, x, MM_0F | PP_66, 0x71, true, -1, imm); } +void vpsrad(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym4 : xm4, x1, x2, MM_0F | PP_66, 0x72, true, -1, imm); } +void vpsrad(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym4 : xm4, x, x, MM_0F | PP_66, 0x72, true, -1, imm); } +void vpsrlw(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym2 : xm2, x1, x2, MM_0F | PP_66, 0x71, true, -1, imm); } +void vpsrlw(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym2 : xm2, x, x, MM_0F | PP_66, 0x71, true, -1, imm); } +void vpsrld(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym2 : xm2, x1, x2, MM_0F | PP_66, 0x72, true, -1, imm); } +void vpsrld(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym2 : xm2, x, x, MM_0F | PP_66, 0x72, true, -1, imm); } +void vpsrlq(const Xmm& x1, const Xmm& x2, uint8 imm) { opAVX_X_X_XM(x1.isYMM() ? ym2 : xm2, x1, x2, MM_0F | PP_66, 0x73, true, -1, imm); } +void vpsrlq(const Xmm& x, uint8 imm) { opAVX_X_X_XM(x.isYMM() ? ym2 : xm2, x, x, MM_0F | PP_66, 0x73, true, -1, imm); } +void vblendvpd(const Xmm& x1, const Xmm& x2, const Operand& op, const Xmm& x4) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x4B, true, -1, x4.getIdx() << 4); } +void vblendvpd(const Xmm& x1, const Operand& op, const Xmm& x4) { opAVX_X_X_XM(x1, x1, op, MM_0F3A | PP_66, 0x4B, true, -1, x4.getIdx() << 4); } +void vblendvps(const Xmm& x1, const Xmm& x2, const Operand& op, const Xmm& x4) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x4A, true, -1, x4.getIdx() << 4); } +void vblendvps(const Xmm& x1, const Operand& op, const Xmm& x4) { opAVX_X_X_XM(x1, x1, op, MM_0F3A | PP_66, 0x4A, true, -1, x4.getIdx() << 4); } +void vpblendvb(const Xmm& x1, const Xmm& x2, const Operand& op, const Xmm& x4) { opAVX_X_X_XM(x1, x2, op, MM_0F3A | PP_66, 0x4C, false, -1, x4.getIdx() << 4); } +void vpblendvb(const Xmm& x1, const Operand& op, const Xmm& x4) { opAVX_X_X_XM(x1, x1, op, MM_0F3A | PP_66, 0x4C, false, -1, x4.getIdx() << 4); } +void vmovd(const Xmm& x, const Reg32& reg) { opAVX_X_X_XM(x, xm0, Xmm(reg.getIdx()), MM_0F | PP_66, 0x6E, false, 0); } +void vmovd(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_66, 0x6E, false, 0); } +void vmovd(const Reg32& reg, const Xmm& x) { opAVX_X_X_XM(x, xm0, Xmm(reg.getIdx()), MM_0F | PP_66, 0x7E, false, 0); } +void vmovd(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_66, 0x7E, false, 0); } +void vmovq(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_F3, 0x7E, false, -1); } +void vmovq(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_66, 0xD6, false, -1); } +void vmovq(const Xmm& x1, const Xmm& x2) { opAVX_X_X_XM(x1, xm0, x2, MM_0F | PP_F3, 0x7E, false, -1); } +void vmovhlps(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x1, x2, op, MM_0F, 0x12, false); } +void vmovlhps(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x1, x2, op, MM_0F, 0x16, false); } +void vmovmskpd(const Reg& r, const Xmm& x) { if (!r.isBit(i32e)) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x.isXMM() ? Xmm(r.getIdx()) : Ymm(r.getIdx()), x.isXMM() ? xm0 : ym0, x, MM_0F | PP_66, 0x50, true, 0); } +void vmovmskps(const Reg& r, const Xmm& x) { if (!r.isBit(i32e)) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x.isXMM() ? Xmm(r.getIdx()) : Ymm(r.getIdx()), x.isXMM() ? xm0 : ym0, x, MM_0F, 0x50, true, 0); } +void vmovntdq(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, x.isXMM() ? xm0 : ym0, addr, MM_0F | PP_66, 0xE7, true); } +void vmovntpd(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, x.isXMM() ? xm0 : ym0, addr, MM_0F | PP_66, 0x2B, true); } +void vmovntps(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, x.isXMM() ? xm0 : ym0, addr, MM_0F, 0x2B, true); } +void vmovntdqa(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, x.isXMM() ? xm0 : ymm0, addr, MM_0F38 | PP_66, 0x2A, true); } +void vmovsd(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F2, 0x10, false); } +void vmovsd(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_F2, 0x10, false); } +void vmovsd(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_F2, 0x11, false); } +void vmovss(const Xmm& x1, const Xmm& x2, const Operand& op = Operand()) { if (!op.isNone() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(x1, x2, op, MM_0F | PP_F3, 0x10, false); } +void vmovss(const Xmm& x, const Address& addr) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_F3, 0x10, false); } +void vmovss(const Address& addr, const Xmm& x) { opAVX_X_X_XM(x, xm0, addr, MM_0F | PP_F3, 0x11, false); } +void vcvtss2si(const Reg32& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F3, 0x2D, false, 0); } +void vcvttss2si(const Reg32& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F3, 0x2C, false, 0); } +void vcvtsd2si(const Reg32& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F2, 0x2D, false, 0); } +void vcvttsd2si(const Reg32& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F2, 0x2C, false, 0); } +void vcvtsi2ss(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !(op2.isREG(i32e) || op2.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, op1, op2, op2.isREG(), Operand::XMM, MM_0F | PP_F3, 0x2A, false, (op1.isMEM() || op2.isMEM()) ? -1 : (op1.isREG(32) || op2.isREG(32)) ? 0 : 1); } +void vcvtsi2sd(const Xmm& x, const Operand& op1, const Operand& op2 = Operand()) { if (!op2.isNone() && !(op2.isREG(i32e) || op2.isMEM())) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, op1, op2, op2.isREG(), Operand::XMM, MM_0F | PP_F2, 0x2A, false, (op1.isMEM() || op2.isMEM()) ? -1 : (op1.isREG(32) || op2.isREG(32)) ? 0 : 1); } +void vcvtps2pd(const Xmm& x, const Operand& op) { if (!op.isMEM() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, x.isXMM() ? xm0 : ym0, op, !op.isMEM(), x.isXMM() ? Operand::XMM : Operand::YMM, MM_0F, 0x5A, true); } +void vcvtdq2pd(const Xmm& x, const Operand& op) { if (!op.isMEM() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, x.isXMM() ? xm0 : ym0, op, !op.isMEM(), x.isXMM() ? Operand::XMM : Operand::YMM, MM_0F | PP_F3, 0xE6, true); } +void vcvtpd2ps(const Xmm& x, const Operand& op) { if (x.isYMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(op.isYMM() ? Ymm(x.getIdx()) : x, op.isYMM() ? ym0 : xm0, op, MM_0F | PP_66, 0x5A, true); } +void vcvtpd2dq(const Xmm& x, const Operand& op) { if (x.isYMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(op.isYMM() ? Ymm(x.getIdx()) : x, op.isYMM() ? ym0 : xm0, op, MM_0F | PP_F2, 0xE6, true); } +void vcvttpd2dq(const Xmm& x, const Operand& op) { if (x.isYMM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XM(op.isYMM() ? Ymm(x.getIdx()) : x, op.isYMM() ? ym0 : xm0, op, MM_0F | PP_66, 0xE6, true); } +void vcvtph2ps(const Xmm& x, const Operand& op) { if (!op.isMEM() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opVex(x, NULL, &op, MM_0F38 | PP_66, 0x13, 0); } +void vcvtps2ph(const Operand& op, const Xmm& x, uint8 imm) { if (!op.isMEM() && !op.isXMM()) throw Error(ERR_BAD_COMBINATION); opVex(x, NULL, &op, MM_0F3A | PP_66, 0x1d, 0, imm); } +#ifdef XBYAK64 +void vmovq(const Xmm& x, const Reg64& reg) { opAVX_X_X_XM(x, xm0, Xmm(reg.getIdx()), MM_0F | PP_66, 0x6E, false, 1); } +void vmovq(const Reg64& reg, const Xmm& x) { opAVX_X_X_XM(x, xm0, Xmm(reg.getIdx()), MM_0F | PP_66, 0x7E, false, 1); } +void vpextrq(const Operand& op, const Xmm& x, uint8 imm) { if (!op.isREG(64) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, xm0, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x16, false, 1, imm); } +void vpinsrq(const Xmm& x1, const Xmm& x2, const Operand& op, uint8 imm) { if (!op.isREG(64) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x1, x2, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x22, false, 1, imm); } +void vpinsrq(const Xmm& x, const Operand& op, uint8 imm) { if (!op.isREG(64) && !op.isMEM()) throw Error(ERR_BAD_COMBINATION); opAVX_X_X_XMcvt(x, x, op, !op.isMEM(), Operand::XMM, MM_0F3A | PP_66, 0x22, false, 1, imm); } +void vcvtss2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F3, 0x2D, false, 1); } +void vcvttss2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F3, 0x2C, false, 1); } +void vcvtsd2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F2, 0x2D, false, 1); } +void vcvttsd2si(const Reg64& r, const Operand& op) { opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, MM_0F | PP_F2, 0x2C, false, 1); } +#endif +void andn(const Reg32e& r1, const Reg32e& r2, const Operand& op) { opGpr(r1, r2, op, MM_0F38, 0xf2, true); } +void mulx(const Reg32e& r1, const Reg32e& r2, const Operand& op) { opGpr(r1, r2, op, MM_0F38 | PP_F2, 0xf6, true); } +void pdep(const Reg32e& r1, const Reg32e& r2, const Operand& op) { opGpr(r1, r2, op, MM_0F38 | PP_F2, 0xf5, true); } +void pext(const Reg32e& r1, const Reg32e& r2, const Operand& op) { opGpr(r1, r2, op, MM_0F38 | PP_F3, 0xf5, true); } +void bextr(const Reg32e& r1, const Operand& op, const Reg32e& r2) { opGpr(r1, op, r2, MM_0F38, 0xf7, false); } +void bzhi(const Reg32e& r1, const Operand& op, const Reg32e& r2) { opGpr(r1, op, r2, MM_0F38, 0xf5, false); } +void sarx(const Reg32e& r1, const Operand& op, const Reg32e& r2) { opGpr(r1, op, r2, MM_0F38 | PP_F3, 0xf7, false); } +void shlx(const Reg32e& r1, const Operand& op, const Reg32e& r2) { opGpr(r1, op, r2, MM_0F38 | PP_66, 0xf7, false); } +void shrx(const Reg32e& r1, const Operand& op, const Reg32e& r2) { opGpr(r1, op, r2, MM_0F38 | PP_F2, 0xf7, false); } +void blsi(const Reg32e& r, const Operand& op) { opGpr(Reg32e(3, r.getBit()), op, r, MM_0F38, 0xf3, false); } +void blsmsk(const Reg32e& r, const Operand& op) { opGpr(Reg32e(2, r.getBit()), op, r, MM_0F38, 0xf3, false); } +void blsr(const Reg32e& r, const Operand& op) { opGpr(Reg32e(1, r.getBit()), op, r, MM_0F38, 0xf3, false); } +void vgatherdpd(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x92, 1, 0); } +void vgatherqpd(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x93, 1, 1); } +void vgatherdps(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x92, 0, 1); } +void vgatherqps(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x93, 0, 2); } +void vpgatherdd(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x90, 0, 1); } +void vpgatherqd(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x91, 0, 2); } +void vpgatherdq(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x90, 1, 0); } +void vpgatherqq(const Xmm& x1, const Address& addr, const Xmm& x2) { opGather(x1, addr, x2, MM_0F38 | PP_66, 0x91, 1, 1); } diff --git a/f4se/xbyak/xbyak_util.h b/f4se/xbyak/xbyak_util.h new file mode 100644 index 0000000..3a7c2c2 --- /dev/null +++ b/f4se/xbyak/xbyak_util.h @@ -0,0 +1,533 @@ +#ifndef XBYAK_XBYAK_UTIL_H_ +#define XBYAK_XBYAK_UTIL_H_ + +/** + utility class and functions for Xbyak + Xbyak::util::Clock ; rdtsc timer + Xbyak::util::Cpu ; detect CPU + @note this header is UNDER CONSTRUCTION! +*/ +#include "xbyak.h" + +#ifdef _MSC_VER + #if (_MSC_VER < 1400) && defined(XBYAK32) + static inline __declspec(naked) void __cpuid(int[4], int) + { + __asm { + push ebx + push esi + mov eax, dword ptr [esp + 4 * 2 + 8] // eaxIn + cpuid + mov esi, dword ptr [esp + 4 * 2 + 4] // data + mov dword ptr [esi], eax + mov dword ptr [esi + 4], ebx + mov dword ptr [esi + 8], ecx + mov dword ptr [esi + 12], edx + pop esi + pop ebx + ret + } + } + #else + #include // for __cpuid + #endif +#else + #ifndef __GNUC_PREREQ + #define __GNUC_PREREQ(major, minor) ((((__GNUC__) << 16) + (__GNUC_MINOR__)) >= (((major) << 16) + (minor))) + #endif + #if __GNUC_PREREQ(4, 3) && !defined(__APPLE__) + #include + #else + #if defined(__APPLE__) && defined(XBYAK32) // avoid err : can't find a register in class `BREG' while reloading `asm' + #define __cpuid(eaxIn, a, b, c, d) __asm__ __volatile__("pushl %%ebx\ncpuid\nmovl %%ebp, %%esi\npopl %%ebx" : "=a"(a), "=S"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) + #define __cpuid_count(eaxIn, ecxIn, a, b, c, d) __asm__ __volatile__("pushl %%ebx\ncpuid\nmovl %%ebp, %%esi\npopl %%ebx" : "=a"(a), "=S"(b), "=c"(c), "=d"(d) : "0"(eaxIn), "2"(ecxIn)) + #else + #define __cpuid(eaxIn, a, b, c, d) __asm__ __volatile__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn)) + #define __cpuid_count(eaxIn, ecxIn, a, b, c, d) __asm__ __volatile__("cpuid\n" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(eaxIn), "2"(ecxIn)) + #endif + #endif +#endif + +#ifdef _MSC_VER +extern "C" unsigned __int64 __xgetbv(int); +#endif + +namespace Xbyak { namespace util { + +/** + CPU detection class +*/ +class Cpu { + uint64 type_; + unsigned int get32bitAsBE(const char *x) const + { + return x[0] | (x[1] << 8) | (x[2] << 16) | (x[3] << 24); + } + unsigned int mask(int n) const + { + return (1U << n) - 1; + } + void setFamily() + { + unsigned int data[4]; + getCpuid(1, data); + stepping = data[0] & mask(4); + model = (data[0] >> 4) & mask(4); + family = (data[0] >> 8) & mask(4); + // type = (data[0] >> 12) & mask(2); + extModel = (data[0] >> 16) & mask(4); + extFamily = (data[0] >> 20) & mask(8); + if (family == 0x0f) { + displayFamily = family + extFamily; + } else { + displayFamily = family; + } + if (family == 6 || family == 0x0f) { + displayModel = (extModel << 4) + model; + } else { + displayModel = model; + } + } +public: + int model; + int family; + int stepping; + int extModel; + int extFamily; + int displayFamily; // family + extFamily + int displayModel; // model + extModel + static inline void getCpuid(unsigned int eaxIn, unsigned int data[4]) + { +#ifdef _MSC_VER + __cpuid(reinterpret_cast(data), eaxIn); +#else + __cpuid(eaxIn, data[0], data[1], data[2], data[3]); +#endif + } + static inline void getCpuidEx(unsigned int eaxIn, unsigned int ecxIn, unsigned int data[4]) + { +#ifdef _MSC_VER + __cpuidex(reinterpret_cast(data), eaxIn, ecxIn); +#else + __cpuid_count(eaxIn, ecxIn, data[0], data[1], data[2], data[3]); +#endif + } + static inline uint64 getXfeature() + { +#ifdef _MSC_VER + return __xgetbv(0); +#else + unsigned int eax, edx; + // xgetvb is not support on gcc 4.2 +// __asm__ volatile("xgetbv" : "=a"(eax), "=d"(edx) : "c"(0)); + __asm__ volatile(".byte 0x0f, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0)); + return ((uint64)edx << 32) | eax; +#endif + } + typedef uint64 Type; + static const Type NONE = 0; + static const Type tMMX = 1 << 0; + static const Type tMMX2 = 1 << 1; + static const Type tCMOV = 1 << 2; + static const Type tSSE = 1 << 3; + static const Type tSSE2 = 1 << 4; + static const Type tSSE3 = 1 << 5; + static const Type tSSSE3 = 1 << 6; + static const Type tSSE41 = 1 << 7; + static const Type tSSE42 = 1 << 8; + static const Type tPOPCNT = 1 << 9; + static const Type tAESNI = 1 << 10; + static const Type tSSE5 = 1 << 11; + static const Type tOSXSAVE = 1 << 12; + static const Type tPCLMULQDQ = 1 << 13; + static const Type tAVX = 1 << 14; + static const Type tFMA = 1 << 15; + + static const Type t3DN = 1 << 16; + static const Type tE3DN = 1 << 17; + static const Type tSSE4a = 1 << 18; + static const Type tRDTSCP = 1 << 19; + static const Type tAVX2 = 1 << 20; + static const Type tBMI1 = 1 << 21; // andn, bextr, blsi, blsmsk, blsr, tzcnt + static const Type tBMI2 = 1 << 22; // bzhi, mulx, pdep, pext, rorx, sarx, shlx, shrx + static const Type tLZCNT = 1 << 23; + + static const Type tINTEL = 1 << 24; + static const Type tAMD = 1 << 25; + + static const Type tENHANCED_REP = 1 << 26; // enhanced rep movsb/stosb + static const Type tRDRAND = 1 << 27; + static const Type tADX = 1 << 28; // adcx, adox + static const Type tRDSEED = 1 << 29; // rdseed + static const Type tSMAP = 1 << 30; // stac + static const Type tHLE = uint64(1) << 31; // xacquire, xrelease, xtest + static const Type tRTM = uint64(1) << 32; // xbegin, xend, xabort + static const Type tF16C = uint64(1) << 33; // vcvtph2ps, vcvtps2ph + static const Type tMOVBE = uint64(1) << 34; // mobve + + Cpu() + : type_(NONE) + { + unsigned int data[4]; + getCpuid(0, data); + const unsigned int maxNum = data[0]; + static const char intel[] = "ntel"; + static const char amd[] = "cAMD"; + if (data[2] == get32bitAsBE(amd)) { + type_ |= tAMD; + getCpuid(0x80000001, data); + if (data[3] & (1U << 31)) type_ |= t3DN; + if (data[3] & (1U << 15)) type_ |= tCMOV; + if (data[3] & (1U << 30)) type_ |= tE3DN; + if (data[3] & (1U << 22)) type_ |= tMMX2; + if (data[3] & (1U << 27)) type_ |= tRDTSCP; + } + if (data[2] == get32bitAsBE(intel)) { + type_ |= tINTEL; + getCpuid(0x80000001, data); + if (data[3] & (1U << 27)) type_ |= tRDTSCP; + if (data[2] & (1U << 5)) type_ |= tLZCNT; + } + getCpuid(1, data); + if (data[2] & (1U << 0)) type_ |= tSSE3; + if (data[2] & (1U << 9)) type_ |= tSSSE3; + if (data[2] & (1U << 19)) type_ |= tSSE41; + if (data[2] & (1U << 20)) type_ |= tSSE42; + if (data[2] & (1U << 22)) type_ |= tMOVBE; + if (data[2] & (1U << 23)) type_ |= tPOPCNT; + if (data[2] & (1U << 25)) type_ |= tAESNI; + if (data[2] & (1U << 1)) type_ |= tPCLMULQDQ; + if (data[2] & (1U << 27)) type_ |= tOSXSAVE; + if (data[2] & (1U << 30)) type_ |= tRDRAND; + if (data[2] & (1U << 29)) type_ |= tF16C; + + if (data[3] & (1U << 15)) type_ |= tCMOV; + if (data[3] & (1U << 23)) type_ |= tMMX; + if (data[3] & (1U << 25)) type_ |= tMMX2 | tSSE; + if (data[3] & (1U << 26)) type_ |= tSSE2; + + if (type_ & tOSXSAVE) { + // check XFEATURE_ENABLED_MASK[2:1] = '11b' + uint64 bv = getXfeature(); + if ((bv & 6) == 6) { + if (data[2] & (1U << 28)) type_ |= tAVX; + if (data[2] & (1U << 12)) type_ |= tFMA; + } + } + if (maxNum >= 7) { + getCpuidEx(7, 0, data); + if (type_ & tAVX && data[1] & 0x20) type_ |= tAVX2; + if (data[1] & (1U << 3)) type_ |= tBMI1; + if (data[1] & (1U << 8)) type_ |= tBMI2; + if (data[1] & (1U << 9)) type_ |= tENHANCED_REP; + if (data[1] & (1U << 18)) type_ |= tRDSEED; + if (data[1] & (1U << 19)) type_ |= tADX; + if (data[1] & (1U << 20)) type_ |= tSMAP; + if (data[1] & (1U << 4)) type_ |= tHLE; + if (data[1] & (1U << 11)) type_ |= tRTM; + } + setFamily(); + } + void putFamily() + { + printf("family=%d, model=%X, stepping=%d, extFamily=%d, extModel=%X\n", + family, model, stepping, extFamily, extModel); + printf("display:family=%X, model=%X\n", displayFamily, displayModel); + } + bool has(Type type) const + { + return (type & type_) != 0; + } +}; + +class Clock { +public: + static inline uint64 getRdtsc() + { +#ifdef _MSC_VER + return __rdtsc(); +#else + unsigned int eax, edx; + __asm__ volatile("rdtsc" : "=a"(eax), "=d"(edx)); + return ((uint64)edx << 32) | eax; +#endif + } + Clock() + : clock_(0) + , count_(0) + { + } + void begin() + { + clock_ -= getRdtsc(); + } + void end() + { + clock_ += getRdtsc(); + count_++; + } + int getCount() const { return count_; } + uint64 getClock() const { return clock_; } + void clear() { count_ = 0; clock_ = 0; } +private: + uint64 clock_; + int count_; +}; + +#ifdef XBYAK64 +const int UseRCX = 1 << 6; +const int UseRDX = 1 << 7; + +class Pack { + static const size_t maxTblNum = 10; + const Xbyak::Reg64 *tbl_[maxTblNum]; + size_t n_; +public: + Pack() : n_(0) {} + Pack(const Xbyak::Reg64 *tbl, size_t n) { init(tbl, n); } + Pack(const Pack& rhs) + : n_(rhs.n_) + { + if (n_ > maxTblNum) throw Error(ERR_INTERNAL); + for (size_t i = 0; i < n_; i++) tbl_[i] = rhs.tbl_[i]; + } + Pack(const Xbyak::Reg64& t0) + { n_ = 1; tbl_[0] = &t0; } + Pack(const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 2; tbl_[0] = &t0; tbl_[1] = &t1; } + Pack(const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 3; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; } + Pack(const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 4; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; } + Pack(const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 5; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; } + Pack(const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 6; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; } + Pack(const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 7; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; } + Pack(const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 8; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; } + Pack(const Xbyak::Reg64& t8, const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 9; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; tbl_[8] = &t8; } + Pack(const Xbyak::Reg64& t9, const Xbyak::Reg64& t8, const Xbyak::Reg64& t7, const Xbyak::Reg64& t6, const Xbyak::Reg64& t5, const Xbyak::Reg64& t4, const Xbyak::Reg64& t3, const Xbyak::Reg64& t2, const Xbyak::Reg64& t1, const Xbyak::Reg64& t0) + { n_ = 10; tbl_[0] = &t0; tbl_[1] = &t1; tbl_[2] = &t2; tbl_[3] = &t3; tbl_[4] = &t4; tbl_[5] = &t5; tbl_[6] = &t6; tbl_[7] = &t7; tbl_[8] = &t8; tbl_[9] = &t9; } + Pack& append(const Xbyak::Reg64& t) + { + if (n_ == 10) { + fprintf(stderr, "ERR Pack::can't append\n"); + throw Error(ERR_BAD_PARAMETER); + } + tbl_[n_++] = &t; + return *this; + } + void init(const Xbyak::Reg64 *tbl, size_t n) + { + if (n > maxTblNum) { + fprintf(stderr, "ERR Pack::init bad n=%d\n", (int)n); + throw Error(ERR_BAD_PARAMETER); + } + n_ = n; + for (size_t i = 0; i < n; i++) { + tbl_[i] = &tbl[i]; + } + } + const Xbyak::Reg64& operator[](size_t n) const + { + if (n >= n_) { + fprintf(stderr, "ERR Pack bad n=%d\n", (int)n); + throw Error(ERR_BAD_PARAMETER); + } + return *tbl_[n]; + } + size_t size() const { return n_; } + /* + get tbl[pos, pos + num) + */ + Pack sub(size_t pos, size_t num = size_t(-1)) const + { + if (num == size_t(-1)) num = n_ - pos; + if (pos + num > n_) { + fprintf(stderr, "ERR Pack::sub bad pos=%d, num=%d\n", (int)pos, (int)num); + throw Error(ERR_BAD_PARAMETER); + } + Pack pack; + pack.n_ = num; + for (size_t i = 0; i < num; i++) { + pack.tbl_[i] = tbl_[pos + i]; + } + return pack; + } + void put() const + { + for (size_t i = 0; i < n_; i++) { + printf("%s ", tbl_[i]->toString()); + } + printf("\n"); + } +}; + +class StackFrame { +#ifdef XBYAK64_WIN + static const int noSaveNum = 6; + static const int rcxPos = 0; + static const int rdxPos = 1; +#else + static const int noSaveNum = 8; + static const int rcxPos = 3; + static const int rdxPos = 2; +#endif + Xbyak::CodeGenerator *code_; + int pNum_; + int tNum_; + bool useRcx_; + bool useRdx_; + int saveNum_; + int P_; + bool makeEpilog_; + Xbyak::Reg64 pTbl_[4]; + Xbyak::Reg64 tTbl_[10]; + Pack p_; + Pack t_; + StackFrame(const StackFrame&); + void operator=(const StackFrame&); +public: + const Pack& p; + const Pack& t; + /* + make stack frame + @param sf [in] this + @param pNum [in] num of function parameter(0 <= pNum <= 4) + @param tNum [in] num of temporary register(0 <= tNum <= 10, with UseRCX, UseRDX) + @param stackSizeByte [in] local stack size + @param makeEpilog [in] automatically call close() if true + + you can use + rax + gp0, ..., gp(pNum - 1) + gt0, ..., gt(tNum-1) + rcx if tNum & UseRCX + rdx if tNum & UseRDX + rsp[0..stackSizeByte - 1] + */ + StackFrame(Xbyak::CodeGenerator *code, int pNum, int tNum = 0, int stackSizeByte = 0, bool makeEpilog = true) + : code_(code) + , pNum_(pNum) + , tNum_(tNum & ~(UseRCX | UseRDX)) + , useRcx_((tNum & UseRCX) != 0) + , useRdx_((tNum & UseRDX) != 0) + , saveNum_(0) + , P_(0) + , makeEpilog_(makeEpilog) + , p(p_) + , t(t_) + { + using namespace Xbyak; + if (pNum < 0 || pNum > 4) throw Error(ERR_BAD_PNUM); + const int allRegNum = pNum + tNum_ + (useRcx_ ? 1 : 0) + (useRdx_ ? 1 : 0); + if (allRegNum < pNum || allRegNum > 14) throw Error(ERR_BAD_TNUM); + const Reg64& _rsp = code->rsp; + const AddressFrame& _ptr = code->ptr; + saveNum_ = (std::max)(0, allRegNum - noSaveNum); + const int *tbl = getOrderTbl() + noSaveNum; + P_ = saveNum_ + (stackSizeByte + 7) / 8; + if (P_ > 0 && (P_ & 1) == 0) P_++; // here (rsp % 16) == 8, then increment P_ for 16 byte alignment + P_ *= 8; + if (P_ > 0) code->sub(_rsp, P_); +#ifdef XBYAK64_WIN + for (int i = 0; i < (std::min)(saveNum_, 4); i++) { + code->mov(_ptr [_rsp + P_ + (i + 1) * 8], Reg64(tbl[i])); + } + for (int i = 4; i < saveNum_; i++) { + code->mov(_ptr [_rsp + P_ - 8 * (saveNum_ - i)], Reg64(tbl[i])); + } +#else + for (int i = 0; i < saveNum_; i++) { + code->mov(_ptr [_rsp + P_ - 8 * (saveNum_ - i)], Reg64(tbl[i])); + } +#endif + int pos = 0; + for (int i = 0; i < pNum; i++) { + pTbl_[i] = Xbyak::Reg64(getRegIdx(pos)); + } + for (int i = 0; i < tNum_; i++) { + tTbl_[i] = Xbyak::Reg64(getRegIdx(pos)); + } + if (useRcx_ && rcxPos < pNum) code_->mov(code_->r10, code_->rcx); + if (useRdx_ && rdxPos < pNum) code_->mov(code_->r11, code_->rdx); + p_.init(pTbl_, pNum); + t_.init(tTbl_, tNum_); + } + /* + make epilog manually + @param callRet [in] call ret() if true + */ + void close(bool callRet = true) + { + using namespace Xbyak; + const Reg64& _rsp = code_->rsp; + const AddressFrame& _ptr = code_->ptr; + const int *tbl = getOrderTbl() + noSaveNum; +#ifdef XBYAK64_WIN + for (int i = 0; i < (std::min)(saveNum_, 4); i++) { + code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ + (i + 1) * 8]); + } + for (int i = 4; i < saveNum_; i++) { + code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ - 8 * (saveNum_ - i)]); + } +#else + for (int i = 0; i < saveNum_; i++) { + code_->mov(Reg64(tbl[i]), _ptr [_rsp + P_ - 8 * (saveNum_ - i)]); + } +#endif + if (P_ > 0) code_->add(_rsp, P_); + + if (callRet) code_->ret(); + } + ~StackFrame() + { + if (!makeEpilog_) return; + try { + close(); + } catch (std::exception& e) { + printf("ERR:StackFrame %s\n", e.what()); + exit(1); + } catch (...) { + printf("ERR:StackFrame otherwise\n"); + exit(1); + } + } +private: + const int *getOrderTbl() const + { + using namespace Xbyak; + static const int tbl[] = { +#ifdef XBYAK64_WIN + Operand::RCX, Operand::RDX, Operand::R8, Operand::R9, Operand::R10, Operand::R11, Operand::RDI, Operand::RSI, +#else + Operand::RDI, Operand::RSI, Operand::RDX, Operand::RCX, Operand::R8, Operand::R9, Operand::R10, Operand::R11, +#endif + Operand::RBX, Operand::RBP, Operand::R12, Operand::R13, Operand::R14, Operand::R15 + }; + return &tbl[0]; + } + int getRegIdx(int& pos) const + { + assert(pos < 14); + using namespace Xbyak; + const int *tbl = getOrderTbl(); + int r = tbl[pos++]; + if (useRcx_) { + if (r == Operand::RCX) { return Operand::R10; } + if (r == Operand::R10) { r = tbl[pos++]; } + } + if (useRdx_) { + if (r == Operand::RDX) { return Operand::R11; } + if (r == Operand::R11) { return tbl[pos++]; } + } + return r; + } +}; +#endif + +} } // end of util +#endif From 4e895910a7642ccf6774a336de66d237ebac9fc9 Mon Sep 17 00:00:00 2001 From: ericncream Date: Sun, 22 Dec 2019 20:33:08 -0800 Subject: [PATCH 02/66] Create README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..a07bbe4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# OpenCBP_FO4 +OpenCBP Physics for Fallout4 +Visual Studio 2017 and later. From 353be7092bbb5effa97ab4957b904b67e08ff750 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Wed, 1 Jan 2020 16:19:41 -0800 Subject: [PATCH 03/66] Decent save point: many transformations fixed, rotations back in. --- CBPSSE/CBPSSE.vcxproj | 6 ++ CBPSSE/SimObj.cpp | 28 ++----- CBPSSE/SimObj.h | 6 +- CBPSSE/Thing.cpp | 189 ++++++++++++++++++++++++++++++++---------- CBPSSE/Thing.h | 10 +-- CBPSSE/config.cpp | 36 ++++++++ CBPSSE/scan.cpp | 32 ++----- 7 files changed, 210 insertions(+), 97 deletions(-) diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index 11603d6..b458cc5 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -145,6 +145,12 @@ %(AdditionalLibraryDirectories) + + copy "$(TargetPath)" "$(Fallout4Path)\$(TargetFileName)" + + + Installing DLL to FO4 Plugins... + diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 145dd70..074af1a 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -5,28 +5,17 @@ #include "log.h" +enum femaleBodyType {CBBE_BODY, FG_BODY}; // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory +const char *leftScrotumName_BT2 = "Penis_Balls_CBP_01"; +const char *rightScrotumName_BT2 = "Penis_Balls_CBP_02"; -const char *leftBreastName = "Breast_CBP_R_02"; -const char *rightBreastName = "Breast_CBP_L_02"; -const char *leftButtName = "Butt_CBP_R_01"; -const char *rightButtName = "Butt_CBP_L_01"; -const char *bellyName = "HDT Belly"; +std::unordered_map configMap; -const char *scrotumName = "NPC GenitalsScrotum [GenScrot]"; -const char *leftScrotumName = "Penis_Balls_CBP_01"; -const char *rightScrotumName = "Penis_Balls_CBP_02"; - -std::unordered_map configMap = { - {leftBreastName, "Breast"}, {rightBreastName, "Breast"}, - {leftButtName, "Butt"}, {rightButtName, "Butt"}, - /*{bellyName, "Belly"}*/ }; - - -std::vector femaleBones = { leftBreastName, rightBreastName, leftButtName, rightButtName, /*bellyName*/ }; +std::vector femaleBones; SimObj::SimObj(Actor *actor, config_t &config) - : things(5){ + : things(4){ id = actor->formID; } @@ -72,16 +61,17 @@ bool SimObj::actorValid(Actor *actor) { void SimObj::update(Actor *actor) { if (!bound) return; - logger.error("update\n"); + //logger.error("update\n"); for (auto &t : things) { t.second.update(actor); } - logger.error("end SimObj update\n"); + //logger.error("end SimObj update\n"); } bool SimObj::updateConfig(config_t & config) { for (auto &t : things) { std::string §ion = configMap[t.first]; + logger.info("updateConfig in SimObj: %s, %s\n", section, t.first); auto ¢ry = config[section]; t.second.updateConfig(centry); } diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index 80b5d38..e90d412 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -24,8 +24,4 @@ class SimObj { extern std::vector femaleBones; -extern const char *leftBreastName; -extern const char *rightBreastName; -extern const char *leftButtName; -extern const char *rightButtName; -extern const char *bellyName; \ No newline at end of file +extern std::unordered_map configMap; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 3c70772..fb20e1d 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -4,7 +4,13 @@ #include #define PI 3.14159265 -#define DEBUG 0 +#define DEBUG 1 +#define TRANSFORM_DEBUG 0 + +std::unordered_map origLocalPos; +std::unordered_map origLocalRot; +const char * skeletonNif_boneName = "skeleton.nif"; +const char* COM_boneName = "COM"; Thing::Thing(NiAVObject* obj, BSFixedString& name) : boneName(name) @@ -13,20 +19,19 @@ Thing::Thing(NiAVObject* obj, BSFixedString& name) oldWorldPos = obj->m_worldTransform.pos; time = clock(); - firstRun = true; } Thing::~Thing() { } void showPos(NiPoint3 &p) { - logger.info("%8.2f %8.2f %8.2f\n", p.x, p.y, p.z); + logger.info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); } void showRot(NiMatrix43 &r) { - logger.info("%8.2f %8.2f %8.2f %8.2f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); - logger.info("%8.2f %8.2f %8.2f %8.2f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); - logger.info("%8.2f %8.2f %8.2f %8.2f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); } @@ -49,7 +54,9 @@ void Thing::updateConfig(configEntry_t & centry) { linearX = centry["linearX"]; linearY = centry["linearY"]; linearZ = centry["linearZ"]; - rotational = centry["rotational"]; + rotationalX = centry["rotationalX"]; + rotationalY = centry["rotationalY"]; + rotationalZ = centry["rotationalZ"]; // Optional entries for backwards compatability if (centry.find("timeStep") != centry.end()) timeStep = centry["timeStep"]; @@ -93,7 +100,7 @@ void Thing::update(Actor *actor) { time = newTime; if (deltaT > 64) deltaT = 64; if (deltaT < 8) deltaT = 8; - + auto loadedState = actor->unkF0; if (!loadedState || !loadedState->rootNode) { logger.error("No loaded state for actor %08x\n", actor->formID); @@ -111,45 +118,81 @@ void Thing::update(Actor *actor) { return; } -#if DEBUG +#if TRANSFORM_DEBUG auto scene_obj = obj; while (scene_obj->m_parent && scene_obj->m_name != "skeleton.nif") { logger.info(scene_obj->m_name); logger.info("\n---\n"); + logger.error("Actual m_localTransform.pos: "); showPos(scene_obj->m_localTransform.pos); + logger.error("Actual m_worldTransform.pos: "); showPos(scene_obj->m_worldTransform.pos); logger.info("---\n"); + //logger.error("Actual m_localTransform.rot Matrix:\n"); showRot(scene_obj->m_localTransform.rot); + //logger.error("Actual m_worldTransform.rot Matrix:\n"); showRot(scene_obj->m_worldTransform.rot); logger.info("---\n"); if (scene_obj->m_parent) { + logger.error("Calculated m_worldTransform.pos 1: "); showPos((scene_obj->m_worldTransform.rot * scene_obj->m_localTransform.pos) + scene_obj->m_parent->m_worldTransform.pos); // m_worldTransform.pos + logger.error("Calculated m_worldTransform.pos 2: "); + showPos((scene_obj->m_parent->m_worldTransform.rot * scene_obj->m_localTransform.pos) + scene_obj->m_parent->m_worldTransform.pos); // m_worldTransform.pos + logger.error("Calculated m_worldTransform.rot Matrix:\n"); showRot(scene_obj->m_localTransform.rot * scene_obj->m_parent->m_worldTransform.rot); // m_worldTransform.rot } scene_obj = scene_obj->m_parent; } #endif - if (firstRun) { - orig_local_pos = obj->m_localTransform.pos; - orig_local_rot = obj->m_localTransform.rot; + if (origLocalPos.find(boneName.c_str()) == origLocalPos.end()) { + logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); + logger.error("firstRun pos Set: "); + origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); + showPos(obj->m_localTransform.pos); + } + if (origLocalRot.find(boneName.c_str()) == origLocalRot.end()) { + logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); + logger.error("firstRun rot Set:\n"); + origLocalRot.emplace(boneName.c_str(), obj->m_localTransform.rot); + showRot(obj->m_localTransform.rot); + } + + auto skeleton_obj = obj; + NiAVObject * com_obj; + while (skeleton_obj->m_parent) + { + if (skeleton_obj->m_parent->m_name == BSFixedString(COM_boneName)) { + com_obj = skeleton_obj->m_parent; + } + else if (skeleton_obj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { + skeleton_obj = skeleton_obj->m_parent; + break; + } + skeleton_obj = skeleton_obj->m_parent; } #if DEBUG - logger.error("bone %s for actor %08x\n", boneName.c_str(), actor->formID); + logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeleton_obj->m_name.c_str()); + showRot(skeleton_obj->m_worldTransform.rot); //showPos(obj->m_parent->m_worldTransform.pos + obj->m_localTransform.pos); showPos((obj->m_localTransform.rot.Transpose() * obj->m_localTransform.pos)); #endif + //NiMatrix43 orig_world_rot = orig_local_rot * obj->m_parent->m_worldTransform.rot; - // Offset to move Center of Mass make rotaional motion more significant - // This target is - NiPoint3 target; + NiMatrix43 targetRot = obj->m_parent->m_worldTransform.rot * skeleton_obj->m_localTransform.rot.Transpose() * com_obj->m_localTransform.rot.Transpose() * skeleton_obj->m_localTransform.rot.Transpose(); + NiPoint3 origWorldPos = /*(obj->m_parent->m_worldTransform.rot * origLocalPos.at(boneName.c_str())) + */obj->m_parent->m_worldTransform.pos; + //NiPoint3 orig_world_pos2 = (obj->m_parent->m_worldTransform.rot * obj->m_localTransform.rot.Transpose() * obj->m_localTransform.pos) + obj->m_parent->m_worldTransform.pos; + //NiPoint3 orig_world_pos3 = (obj->m_parent->m_worldTransform.rot * obj->m_localTransform.rot * orig_local_pos) + obj->m_parent->m_worldTransform.pos; + NiPoint3 orig_world_pos4 = (obj->m_localTransform.rot * obj->m_localTransform.pos) + obj->m_parent->m_worldTransform.pos; + + // Offset to move Center of Mass make rotational motion more significant + NiPoint3 target = (targetRot * NiPoint3(0, cogOffset, 0)) + origWorldPos; // TODO: left and right with same parents transforms should be different... example: the butt // Relative Left //if (obj->m_localTransform.pos.x < 0.0) { - target = (obj->m_worldTransform.rot * NiPoint3(0, cogOffset, 0)) + obj->m_worldTransform.pos; //} //// Relative Right //else { @@ -160,24 +203,41 @@ void Thing::update(Actor *actor) { #if DEBUG logger.error("World Position: "); showPos(obj->m_worldTransform.pos); - logger.error("Target: "); + logger.error("Target with cogOffset %8.4f: ", cogOffset); showPos(target); + logger.error("Target Rotation:\n"); + showRot(targetRot); + logger.error("cogOffset x Transformation:"); + showPos(targetRot * NiPoint3(cogOffset, 0, 0)); + logger.error("cogOffset y Transformation:"); + showPos(targetRot * NiPoint3(0, cogOffset, 0)); + logger.error("cogOffset z Transformation:"); + showPos(targetRot * NiPoint3(0, 0, cogOffset)); + //logger.error("orig_world_pos: "); + //showPos(orig_world_pos); + //logger.error("orig_world_pos2: "); + //showPos(orig_world_pos2); + //logger.error("orig_world_pos3: "); + //showPos(orig_world_pos3); + logger.error("orig_world_pos4: "); + showPos(orig_world_pos4); #endif // diff is Difference in position between old and new world position NiPoint3 diff = target - oldWorldPos; // move up in rotated angle for gravity correction - diff += obj->m_worldTransform.rot * NiPoint3(0, 0, gravityCorrection); + diff += targetRot * NiPoint3(0, 0, gravityCorrection); #if DEBUG - logger.error("Diff after gravity correction: "); + logger.error("Diff after gravity correction %f: ", gravityCorrection); showPos(diff); + #endif if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { logger.error("transform reset\n"); - obj->m_localTransform.pos = NiPoint3(0, 0, 0); + obj->m_localTransform.pos = origLocalPos.at(boneName.c_str()); oldWorldPos = target; velocity = NiPoint3(0, 0, 0); time = clock(); @@ -188,12 +248,12 @@ void Thing::update(Actor *actor) { // Compute the "Spring" Force NiPoint3 diff2(diff.x * diff.x * sgn(diff.x), diff.y * diff.y * sgn(diff.y), diff.z * diff.z * sgn(diff.z)); - NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - NiPoint3(0, 0, gravityBias); + NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - (targetRot * NiPoint3(0, 0, gravityBias)); #if DEBUG logger.error("Diff2: "); showPos(diff2); - logger.error("Force: "); + logger.error("Force with stiffness %f, stiffness2 %f, gravity bias %f: ", stiffness, stiffness2, gravityBias); showPos(force); #endif @@ -207,62 +267,105 @@ void Thing::update(Actor *actor) { deltaT -= timeTick; } while (deltaT >= timeTick); - NiPoint3 newPos = oldWorldPos + posDelta; + + //NiPoint3 newPos = oldWorldPos; + + oldWorldPos = diff + target; + #if DEBUG - logger.error("posDelta: "); - showPos(posDelta); + //logger.error("posDelta: "); + //showPos(posDelta); logger.error("newPos: "); showPos(newPos); #endif // clamp the difference to stop the breast severely lagging at low framerates - diff = newPos- target; + diff = newPos - target; + +//#if DEBUG +// logger.error("Diff after gravity correction %f: ", gravityCorrection); +// logger.error("%8.4f\n", -diff.x + target.x - oldWorldPos.x); +// logger.error("%8.4f\n", -diff.y + target.y - oldWorldPos.y); +// logger.error("%8.4f\n", -diff.z + target.z - oldWorldPos.z); +//#endif +// if ((-diff.x + target.x - oldWorldPos.x) < 0.1 && +// (-diff.y + target.y - oldWorldPos.y) < 0.1 && +// (-diff.z + target.z - oldWorldPos.z) < 0.1) { +//#if DEBUG +// logger.error("Diff set to 0: "); +// showPos(diff); +//#endif +// diff = NiPoint3(0, 0, 0); +// } diff.x = clamp(diff.x, -maxOffset, maxOffset); diff.y = clamp(diff.y, -maxOffset, maxOffset); - diff.z = clamp(diff.z-gravityCorrection, -maxOffset, maxOffset) + gravityCorrection; + diff.z = clamp(diff.z - gravityCorrection, -maxOffset, maxOffset) + gravityCorrection; // Old clamping with "gravity correction" was kind of ugly so I took it out - //oldWorldPos = diff + target; + oldWorldPos = target + diff; #if DEBUG logger.error("diff from newPos: "); showPos(diff); + logger.error("oldWorldPos: "); + showPos(oldWorldPos); #endif //logger.error("set positions\n"); // move the bones based on the supplied weightings // Convert the world translations into local coordinates - auto invRot = obj->m_localTransform.rot * obj->m_worldTransform.rot.Transpose(); - auto local_diff = invRot * diff; + //auto invRot = obj->m_localTransform.rot * obj->m_parent->m_worldTransform.rot.Transpose(); + auto invRot = obj->m_parent->m_worldTransform.rot; + auto localDiff = invRot * diff; //showPos(diff); //showPos(local_diff); // remove component along bone - might want something closer to world - //ldiff.y = 0; + //localDiff.y = 0; + //oldWorldPos = (obj->m_parent->m_worldTransform.rot * (obj->m_localTransform.rot.Transpose() * localDiff)) + target; oldWorldPos = diff + target; //showRot(obj->m_parent->m_worldTransform.rot * invRot); - #if DEBUG + logger.error("invRot x=10 Transformation:"); + showPos(invRot * NiPoint3(10, 0, 0)); + logger.error("invRot y=10 Transformation:"); + showPos(invRot * NiPoint3(0, 10, 0)); + logger.error("invRot z=10 Transformation:"); + showPos(invRot * NiPoint3(0, 0, 10)); + logger.error("oldWorldPos: "); + showPos(oldWorldPos); logger.error("localTransform.pos: "); showPos(obj->m_localTransform.pos); - logger.error("local_diff: "); - showPos(local_diff); + logger.error("localDiff: "); + showPos(localDiff); #endif // scale positions from config - obj->m_localTransform.pos.x = orig_local_pos.x + (local_diff.x * linearX); - obj->m_localTransform.pos.y = orig_local_pos.y + (local_diff.y * linearY); - obj->m_localTransform.pos.z = orig_local_pos.z + (local_diff.z * linearZ); + NiPoint3 newLocalPos = NiPoint3((localDiff.x * linearX) + origLocalPos.at(boneName.c_str()).x, + (localDiff.y * linearY) + origLocalPos.at(boneName.c_str()).y, + (localDiff.z * linearZ) + origLocalPos.at(boneName.c_str()).z + ); + obj->m_localTransform.pos = newLocalPos; // do some rotation - auto rdiff = local_diff * rotational; + auto rotDiff = localDiff; + rotDiff.x *= rotationalX; + rotDiff.y *= rotationalY; + rotDiff.z *= rotationalZ; + #if DEBUG - logger.error("rdiff: "); - showPos(rdiff); + logger.error("localTransform.pos after: "); + showPos(obj->m_localTransform.pos); + logger.error("origLocalPos:"); + showPos(origLocalPos.at(boneName.c_str())); + logger.error("origLocalRot:"); + showRot(origLocalRot.at(boneName.c_str())); + //logger.error("rotDiff: "); + //showPos(rotDiff); #endif float heading, attitude, bank; - orig_local_rot.GetEulerAngles(&heading, &attitude, &bank); - obj->m_localTransform.rot.SetEulerAngles(heading + rdiff.x, attitude + rdiff.y, bank + rdiff.z); + origLocalRot.at(boneName.c_str()).GetEulerAngles(&heading, &attitude, &bank); + obj->m_localTransform.rot.SetEulerAngles(heading + rotDiff.z, attitude + rotDiff.y, bank + rotDiff.x); } firstRun = false; diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index db11ca3..94eabf7 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -9,8 +9,8 @@ class Thing { BSFixedString boneName; NiPoint3 oldWorldPos; NiPoint3 velocity; - NiPoint3 orig_local_pos; - NiMatrix43 orig_local_rot; + //NiPoint3 origLocalPos; + //NiMatrix43 origLocalRot; clock_t time; public: @@ -26,13 +26,13 @@ class Thing { float linearX = 0; float linearY = 0; float linearZ = 0; - float rotational = 0.1; + float rotationalX = 0.1; + float rotationalY = 0.1; + float rotationalZ = 0.1; float timeStep = 1.0f; boolean firstRun; - //std::unordered_map boneRotationMatrices; - Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 986a111..3822ab1 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -13,6 +13,15 @@ int configReloadCount = 60; config_t config; +const char* leftBreastName_FG = "Breast_CBP_L_02"; +const char* rightBreastName_FG = "Breast_CBP_R_02"; +const char* leftButtName_FG = "Butt_CBP_L_01"; +const char* rightButtName_FG = "Butt_CBP_R_01"; + +const char* leftBreastName_CBBE = "LBreast_skin"; +const char* rightBreastName_CBBE = "RBreast_skin"; +const char* leftButtName_CBBE = "LButtFat_skin"; +const char* rightButtName_CBBE = "RButtFat_skin"; void loadConfig() { char buffer[1024]; @@ -44,5 +53,32 @@ void loadConfig() { } while (!feof(fh)); fclose(fh); + // temporary config setup until ini format + if (config["Breast"]["FG"] == 1.0) { + femaleBones.push_back(leftBreastName_FG); + femaleBones.push_back(rightBreastName_FG); + configMap.insert({ leftBreastName_FG, "Breast" }); + configMap.insert({ rightBreastName_FG, "Breast" }); + } + else { + // Default is CBBE + femaleBones.push_back(leftBreastName_CBBE); + femaleBones.push_back(rightBreastName_CBBE); + configMap.insert({ leftBreastName_CBBE, "Breast" }); + configMap.insert({ rightBreastName_CBBE, "Breast" }); + } + if (config["Butt"]["FG"] == 1.0) { + femaleBones.push_back(leftButtName_FG); + femaleBones.push_back(rightButtName_FG); + configMap.insert({ leftButtName_FG, "Butt" }); + configMap.insert({ rightButtName_FG, "Butt" }); + } + else { + // Default is CBBE + femaleBones.push_back(leftButtName_CBBE); + femaleBones.push_back(rightButtName_CBBE); + configMap.insert({ leftButtName_CBBE, "Butt" }); + configMap.insert({ rightButtName_CBBE, "Butt" }); + } configReloadCount = config["Tuning"]["rate"]; } diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 7227384..9200251 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -164,25 +164,6 @@ void updateActors() { } } } - //for (int i = 0; i < cell->refData.maxSize; i++) { - // auto ref = cell->refData.refArray[i]; - // if (ref.unk08 != NULL && ref.ref) { - // auto actor = DYNAMIC_CAST(ref.ref, TESObjectREFR, Actor); - // if (actor && actor->unkF0) { - // auto soIt = actors.find(actor->formID); - // if (soIt == actors.end()) { - // //logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); - // auto obj = SimObj(actor, config); - // if (obj.actorValid(actor)) { - // actors.emplace(actor->formID, obj); - // actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); - // } - // } else if (soIt->second.actorValid(actor)) { - // actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); - // } - // } - // } - //} } //static bool done = false; @@ -199,6 +180,7 @@ void updateActors() { // done = true; //} + // Reload config static int count = 0; if (configReloadCount && count++ > configReloadCount) { count = 0; @@ -207,13 +189,14 @@ void updateActors() { a.second.updateConfig(config); } } - logger.error("Updating %d entites\n", actorEntries.size()); + + //logger.error("Updating %d entities\n", actorEntries.size()); for (auto &a : actorEntries) { - auto objIt = actors.find(a.id); - if (objIt == actors.end()) { - logger.error("missing Sim Object\n"); + auto objIterator = actors.find(a.id); + if (objIterator == actors.end()) { + //logger.error("Sim Object not found in tracked actors\n"); } else { - auto &obj = objIt->second; + auto &obj = objIterator->second; if (obj.isBound()) { obj.update(a.actor); } else { @@ -240,7 +223,6 @@ class ScanDelegate : public ITaskDelegate { virtual void Dispose() { delete this; } - }; From 09f5ed76f040b4b58da64ccf92eefa6ac59378e7 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Wed, 1 Jan 2020 19:35:15 -0800 Subject: [PATCH 04/66] Corrected world transformation position calculation. Tiny amount of cleanup. --- CBPSSE/Thing.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index fb20e1d..7ae238e 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -135,23 +135,23 @@ void Thing::update(Actor *actor) { showRot(scene_obj->m_worldTransform.rot); logger.info("---\n"); if (scene_obj->m_parent) { - logger.error("Calculated m_worldTransform.pos 1: "); - showPos((scene_obj->m_worldTransform.rot * scene_obj->m_localTransform.pos) + scene_obj->m_parent->m_worldTransform.pos); // m_worldTransform.pos - logger.error("Calculated m_worldTransform.pos 2: "); - showPos((scene_obj->m_parent->m_worldTransform.rot * scene_obj->m_localTransform.pos) + scene_obj->m_parent->m_worldTransform.pos); // m_worldTransform.pos + logger.error("Calculated m_worldTransform.pos: "); + showPos((scene_obj->m_parent->m_worldTransform.rot.Transpose() * scene_obj->m_localTransform.pos) + scene_obj->m_parent->m_worldTransform.pos); logger.error("Calculated m_worldTransform.rot Matrix:\n"); - showRot(scene_obj->m_localTransform.rot * scene_obj->m_parent->m_worldTransform.rot); // m_worldTransform.rot + showRot(scene_obj->m_localTransform.rot * scene_obj->m_parent->m_worldTransform.rot); } scene_obj = scene_obj->m_parent; } #endif + // Save the bone's original local position if it already hasn't if (origLocalPos.find(boneName.c_str()) == origLocalPos.end()) { logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); logger.error("firstRun pos Set: "); origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); showPos(obj->m_localTransform.pos); } + // Save the bone's original local rotation if it already hasn't if (origLocalRot.find(boneName.c_str()) == origLocalRot.end()) { logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); logger.error("firstRun rot Set:\n"); @@ -176,16 +176,13 @@ void Thing::update(Actor *actor) { #if DEBUG logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeleton_obj->m_name.c_str()); showRot(skeleton_obj->m_worldTransform.rot); - //showPos(obj->m_parent->m_worldTransform.pos + obj->m_localTransform.pos); - showPos((obj->m_localTransform.rot.Transpose() * obj->m_localTransform.pos)); + showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif //NiMatrix43 orig_world_rot = orig_local_rot * obj->m_parent->m_worldTransform.rot; NiMatrix43 targetRot = obj->m_parent->m_worldTransform.rot * skeleton_obj->m_localTransform.rot.Transpose() * com_obj->m_localTransform.rot.Transpose() * skeleton_obj->m_localTransform.rot.Transpose(); - NiPoint3 origWorldPos = /*(obj->m_parent->m_worldTransform.rot * origLocalPos.at(boneName.c_str())) + */obj->m_parent->m_worldTransform.pos; - //NiPoint3 orig_world_pos2 = (obj->m_parent->m_worldTransform.rot * obj->m_localTransform.rot.Transpose() * obj->m_localTransform.pos) + obj->m_parent->m_worldTransform.pos; - //NiPoint3 orig_world_pos3 = (obj->m_parent->m_worldTransform.rot * obj->m_localTransform.rot * orig_local_pos) + obj->m_parent->m_worldTransform.pos; - NiPoint3 orig_world_pos4 = (obj->m_localTransform.rot * obj->m_localTransform.pos) + obj->m_parent->m_worldTransform.pos; + NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; + NiPoint3 calcWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos) + obj->m_parent->m_worldTransform.pos; // Offset to move Center of Mass make rotational motion more significant NiPoint3 target = (targetRot * NiPoint3(0, cogOffset, 0)) + origWorldPos; @@ -203,6 +200,8 @@ void Thing::update(Actor *actor) { #if DEBUG logger.error("World Position: "); showPos(obj->m_worldTransform.pos); + logger.error("Parent World Position difference: "); + showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); logger.error("Target with cogOffset %8.4f: ", cogOffset); showPos(target); logger.error("Target Rotation:\n"); @@ -213,14 +212,8 @@ void Thing::update(Actor *actor) { showPos(targetRot * NiPoint3(0, cogOffset, 0)); logger.error("cogOffset z Transformation:"); showPos(targetRot * NiPoint3(0, 0, cogOffset)); - //logger.error("orig_world_pos: "); - //showPos(orig_world_pos); - //logger.error("orig_world_pos2: "); - //showPos(orig_world_pos2); - //logger.error("orig_world_pos3: "); - //showPos(orig_world_pos3); - logger.error("orig_world_pos4: "); - showPos(orig_world_pos4); + logger.error("Calculated World Position: "); + showPos(calcWorldPos); #endif // diff is Difference in position between old and new world position @@ -365,7 +358,8 @@ void Thing::update(Actor *actor) { #endif float heading, attitude, bank; origLocalRot.at(boneName.c_str()).GetEulerAngles(&heading, &attitude, &bank); - obj->m_localTransform.rot.SetEulerAngles(heading + rotDiff.z, attitude + rotDiff.y, bank + rotDiff.x); + // Note... this is supposed in the rotational space of m_localTransform + obj->m_localTransform.rot.SetEulerAngles(heading + rotDiff.x, attitude + rotDiff.y, bank + rotDiff.z); } firstRun = false; From 36e7a14fc617a1b408aba20ca7bfd9502c6d25dd Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Wed, 1 Jan 2020 19:54:41 -0800 Subject: [PATCH 05/66] Rotation experiment to get it more accurate to world space, probably won't work as intended. And some cleanup of variables. --- CBPSSE/Thing.cpp | 58 +++++++++++++++++++++++------------------------- CBPSSE/Thing.h | 2 -- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 7ae238e..5af764c 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -119,28 +119,28 @@ void Thing::update(Actor *actor) { } #if TRANSFORM_DEBUG - auto scene_obj = obj; - while (scene_obj->m_parent && scene_obj->m_name != "skeleton.nif") + auto sceneObj = obj; + while (sceneObj->m_parent && sceneObj->m_name != "skeleton.nif") { - logger.info(scene_obj->m_name); + logger.info(sceneObj->m_name); logger.info("\n---\n"); logger.error("Actual m_localTransform.pos: "); - showPos(scene_obj->m_localTransform.pos); + showPos(sceneObj->m_localTransform.pos); logger.error("Actual m_worldTransform.pos: "); - showPos(scene_obj->m_worldTransform.pos); + showPos(sceneObj->m_worldTransform.pos); logger.info("---\n"); //logger.error("Actual m_localTransform.rot Matrix:\n"); - showRot(scene_obj->m_localTransform.rot); + showRot(sceneObj->m_localTransform.rot); //logger.error("Actual m_worldTransform.rot Matrix:\n"); - showRot(scene_obj->m_worldTransform.rot); + showRot(sceneObj->m_worldTransform.rot); logger.info("---\n"); - if (scene_obj->m_parent) { + if (sceneObj->m_parent) { logger.error("Calculated m_worldTransform.pos: "); - showPos((scene_obj->m_parent->m_worldTransform.rot.Transpose() * scene_obj->m_localTransform.pos) + scene_obj->m_parent->m_worldTransform.pos); + showPos((sceneObj->m_parent->m_worldTransform.rot.Transpose() * sceneObj->m_localTransform.pos) + sceneObj->m_parent->m_worldTransform.pos); logger.error("Calculated m_worldTransform.rot Matrix:\n"); - showRot(scene_obj->m_localTransform.rot * scene_obj->m_parent->m_worldTransform.rot); + showRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); } - scene_obj = scene_obj->m_parent; + sceneObj = sceneObj->m_parent; } #endif @@ -159,28 +159,28 @@ void Thing::update(Actor *actor) { showRot(obj->m_localTransform.rot); } - auto skeleton_obj = obj; - NiAVObject * com_obj; - while (skeleton_obj->m_parent) + auto skeletonObj = obj; + NiAVObject * comObj; + while (skeletonObj->m_parent) { - if (skeleton_obj->m_parent->m_name == BSFixedString(COM_boneName)) { - com_obj = skeleton_obj->m_parent; + if (skeletonObj->m_parent->m_name == BSFixedString(COM_boneName)) { + comObj = skeletonObj->m_parent; } - else if (skeleton_obj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { - skeleton_obj = skeleton_obj->m_parent; + else if (skeletonObj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { + skeletonObj = skeletonObj->m_parent; break; } - skeleton_obj = skeleton_obj->m_parent; + skeletonObj = skeletonObj->m_parent; } #if DEBUG - logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeleton_obj->m_name.c_str()); - showRot(skeleton_obj->m_worldTransform.rot); - showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); + logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); + showRot(skeletonObj->m_worldTransform.rot); + //showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif - //NiMatrix43 orig_world_rot = orig_local_rot * obj->m_parent->m_worldTransform.rot; + //NiMatrix43 origWorldRot = origLocalRot * obj->m_parent->m_worldTransform.rot; - NiMatrix43 targetRot = obj->m_parent->m_worldTransform.rot * skeleton_obj->m_localTransform.rot.Transpose() * com_obj->m_localTransform.rot.Transpose() * skeleton_obj->m_localTransform.rot.Transpose(); + NiMatrix43 targetRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose() * skeletonObj->m_localTransform.rot.Transpose(); NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; NiPoint3 calcWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos) + obj->m_parent->m_worldTransform.pos; @@ -295,13 +295,13 @@ void Thing::update(Actor *actor) { diff.y = clamp(diff.y, -maxOffset, maxOffset); diff.z = clamp(diff.z - gravityCorrection, -maxOffset, maxOffset) + gravityCorrection; // Old clamping with "gravity correction" was kind of ugly so I took it out - oldWorldPos = target + diff; + //oldWorldPos = target + diff; #if DEBUG logger.error("diff from newPos: "); showPos(diff); - logger.error("oldWorldPos: "); - showPos(oldWorldPos); + //logger.error("oldWorldPos: "); + //showPos(oldWorldPos); #endif //logger.error("set positions\n"); @@ -316,7 +316,6 @@ void Thing::update(Actor *actor) { // remove component along bone - might want something closer to world //localDiff.y = 0; - //oldWorldPos = (obj->m_parent->m_worldTransform.rot * (obj->m_localTransform.rot.Transpose() * localDiff)) + target; oldWorldPos = diff + target; //showRot(obj->m_parent->m_worldTransform.rot * invRot); #if DEBUG @@ -341,7 +340,7 @@ void Thing::update(Actor *actor) { obj->m_localTransform.pos = newLocalPos; // do some rotation - auto rotDiff = localDiff; + auto rotDiff = targetRot * diff; rotDiff.x *= rotationalX; rotDiff.y *= rotationalY; rotDiff.z *= rotationalZ; @@ -362,7 +361,6 @@ void Thing::update(Actor *actor) { obj->m_localTransform.rot.SetEulerAngles(heading + rotDiff.x, attitude + rotDiff.y, bank + rotDiff.z); } - firstRun = false; #if DEBUG logger.error("end update()\n"); #endif diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 94eabf7..9793dd5 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -31,8 +31,6 @@ class Thing { float rotationalZ = 0.1; float timeStep = 1.0f; - boolean firstRun; - Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); From 4f54d2492b90f7565eecceb38f80e44a151c1421 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Thu, 2 Jan 2020 01:48:58 -0800 Subject: [PATCH 06/66] Modified project names for FO4. More tinkering to space transformations. More cleanup. --- CBPSSE/CBPSSE.vcxproj | 2 +- CBPSSE/Thing.cpp | 56 ++++++++++------------------------- CBPSSE.sln => OpenCBP_FO4.sln | 0 3 files changed, 16 insertions(+), 42 deletions(-) rename CBPSSE.sln => OpenCBP_FO4.sln (100%) diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index b458cc5..a57b394 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -23,7 +23,7 @@ {49438A48-1C92-4D07-97C0-BDF0744386F9} CBBPSSE 10.0.18362.0 - CBPSSE + OpenCBP_FO4 diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 5af764c..f37f929 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -180,9 +180,8 @@ void Thing::update(Actor *actor) { #endif //NiMatrix43 origWorldRot = origLocalRot * obj->m_parent->m_worldTransform.rot; - NiMatrix43 targetRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose() * skeletonObj->m_localTransform.rot.Transpose(); + NiMatrix43 targetRot = skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot * skeletonObj->m_localTransform.rot * obj->m_parent->m_worldTransform.rot.Transpose(); NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; - NiPoint3 calcWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos) + obj->m_parent->m_worldTransform.pos; // Offset to move Center of Mass make rotational motion more significant NiPoint3 target = (targetRot * NiPoint3(0, cogOffset, 0)) + origWorldPos; @@ -202,18 +201,16 @@ void Thing::update(Actor *actor) { showPos(obj->m_worldTransform.pos); logger.error("Parent World Position difference: "); showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); - logger.error("Target with cogOffset %8.4f: ", cogOffset); - showPos(target); + logger.error("Target Rotation * cogOffset %8.4f: ", cogOffset); + showPos(targetRot * NiPoint3(0, cogOffset, 0)); logger.error("Target Rotation:\n"); showRot(targetRot); - logger.error("cogOffset x Transformation:"); - showPos(targetRot * NiPoint3(cogOffset, 0, 0)); - logger.error("cogOffset y Transformation:"); - showPos(targetRot * NiPoint3(0, cogOffset, 0)); - logger.error("cogOffset z Transformation:"); - showPos(targetRot * NiPoint3(0, 0, cogOffset)); - logger.error("Calculated World Position: "); - showPos(calcWorldPos); + //logger.error("cogOffset x Transformation:"); + //showPos(targetRot * NiPoint3(cogOffset, 0, 0)); + //logger.error("cogOffset y Transformation:"); + //showPos(targetRot * NiPoint3(0, cogOffset, 0)); + //logger.error("cogOffset z Transformation:"); + //showPos(targetRot * NiPoint3(0, 0, cogOffset)); #endif // diff is Difference in position between old and new world position @@ -225,7 +222,6 @@ void Thing::update(Actor *actor) { #if DEBUG logger.error("Diff after gravity correction %f: ", gravityCorrection); showPos(diff); - #endif if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { @@ -262,8 +258,6 @@ void Thing::update(Actor *actor) { NiPoint3 newPos = oldWorldPos + posDelta; - //NiPoint3 newPos = oldWorldPos; - oldWorldPos = diff + target; #if DEBUG @@ -275,25 +269,9 @@ void Thing::update(Actor *actor) { // clamp the difference to stop the breast severely lagging at low framerates diff = newPos - target; -//#if DEBUG -// logger.error("Diff after gravity correction %f: ", gravityCorrection); -// logger.error("%8.4f\n", -diff.x + target.x - oldWorldPos.x); -// logger.error("%8.4f\n", -diff.y + target.y - oldWorldPos.y); -// logger.error("%8.4f\n", -diff.z + target.z - oldWorldPos.z); -//#endif -// if ((-diff.x + target.x - oldWorldPos.x) < 0.1 && -// (-diff.y + target.y - oldWorldPos.y) < 0.1 && -// (-diff.z + target.z - oldWorldPos.z) < 0.1) { -//#if DEBUG -// logger.error("Diff set to 0: "); -// showPos(diff); -//#endif -// diff = NiPoint3(0, 0, 0); -// } - diff.x = clamp(diff.x, -maxOffset, maxOffset); diff.y = clamp(diff.y, -maxOffset, maxOffset); - diff.z = clamp(diff.z - gravityCorrection, -maxOffset, maxOffset) + gravityCorrection; // Old clamping with "gravity correction" was kind of ugly so I took it out + diff.z = clamp(diff.z - gravityCorrection, -maxOffset, maxOffset) + gravityCorrection; //oldWorldPos = target + diff; @@ -304,20 +282,15 @@ void Thing::update(Actor *actor) { //showPos(oldWorldPos); #endif - //logger.error("set positions\n"); // move the bones based on the supplied weightings // Convert the world translations into local coordinates - //auto invRot = obj->m_localTransform.rot * obj->m_parent->m_worldTransform.rot.Transpose(); auto invRot = obj->m_parent->m_worldTransform.rot; auto localDiff = invRot * diff; - //showPos(diff); - //showPos(local_diff); // remove component along bone - might want something closer to world //localDiff.y = 0; oldWorldPos = diff + target; - //showRot(obj->m_parent->m_worldTransform.rot * invRot); #if DEBUG logger.error("invRot x=10 Transformation:"); showPos(invRot * NiPoint3(10, 0, 0)); @@ -352,14 +325,15 @@ void Thing::update(Actor *actor) { showPos(origLocalPos.at(boneName.c_str())); logger.error("origLocalRot:"); showRot(origLocalRot.at(boneName.c_str())); - //logger.error("rotDiff: "); - //showPos(rotDiff); + logger.error("rotDiff: "); + showPos(rotDiff); #endif float heading, attitude, bank; + + // EulerAngles are in radians origLocalRot.at(boneName.c_str()).GetEulerAngles(&heading, &attitude, &bank); // Note... this is supposed in the rotational space of m_localTransform - obj->m_localTransform.rot.SetEulerAngles(heading + rotDiff.x, attitude + rotDiff.y, bank + rotDiff.z); - + obj->m_localTransform.rot.SetEulerAngles(heading + rotDiff.z, attitude + rotDiff.y, bank + rotDiff.x); } #if DEBUG logger.error("end update()\n"); diff --git a/CBPSSE.sln b/OpenCBP_FO4.sln similarity index 100% rename from CBPSSE.sln rename to OpenCBP_FO4.sln From 2b0bacfef36b9a4bf893ff5d46f011323bc450a2 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sun, 12 Jan 2020 04:48:54 -0800 Subject: [PATCH 07/66] More changes to transformation calculations. Added new cogOffset dimensions. --- CBPSSE/SimObj.cpp | 2 +- CBPSSE/Thing.cpp | 110 +++++++++++++++++++++++++++++++--------------- CBPSSE/Thing.h | 4 ++ CBPSSE/config.cpp | 2 +- 4 files changed, 80 insertions(+), 38 deletions(-) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 074af1a..c3a1333 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -71,7 +71,7 @@ void SimObj::update(Actor *actor) { bool SimObj::updateConfig(config_t & config) { for (auto &t : things) { std::string §ion = configMap[t.first]; - logger.info("updateConfig in SimObj: %s, %s\n", section, t.first); + //logger.info("updateConfig in SimObj: %s, %s\n", section, t.first); auto ¢ry = config[section]; t.second.updateConfig(centry); } diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index f37f929..c59144b 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -4,7 +4,7 @@ #include #define PI 3.14159265 -#define DEBUG 1 +#define DEBUG 0 #define TRANSFORM_DEBUG 0 std::unordered_map origLocalPos; @@ -65,6 +65,9 @@ void Thing::updateConfig(configEntry_t & centry) { gravityBias = centry["gravityBias"]; gravityCorrection = centry["gravityCorrection"]; cogOffset = centry["cogOffset"]; + cogOffsetX = centry["cogOffsetX"]; + cogOffsetZ = centry["cogOffsetZ"]; + fusionGirlEnabled = centry["FG"] == 1.0; if (timeTick <= 1) timeTick = 1; @@ -134,12 +137,12 @@ void Thing::update(Actor *actor) { //logger.error("Actual m_worldTransform.rot Matrix:\n"); showRot(sceneObj->m_worldTransform.rot); logger.info("---\n"); - if (sceneObj->m_parent) { - logger.error("Calculated m_worldTransform.pos: "); - showPos((sceneObj->m_parent->m_worldTransform.rot.Transpose() * sceneObj->m_localTransform.pos) + sceneObj->m_parent->m_worldTransform.pos); - logger.error("Calculated m_worldTransform.rot Matrix:\n"); - showRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); - } + //if (sceneObj->m_parent) { + // logger.error("Calculated m_worldTransform.pos: "); + // showPos((sceneObj->m_parent->m_worldTransform.rot.Transpose() * sceneObj->m_localTransform.pos) + sceneObj->m_parent->m_worldTransform.pos); + // logger.error("Calculated m_worldTransform.rot Matrix:\n"); + // showRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); + //} sceneObj = sceneObj->m_parent; } #endif @@ -161,6 +164,7 @@ void Thing::update(Actor *actor) { auto skeletonObj = obj; NiAVObject * comObj; + bool skeletonFound = false; while (skeletonObj->m_parent) { if (skeletonObj->m_parent->m_name == BSFixedString(COM_boneName)) { @@ -168,23 +172,39 @@ void Thing::update(Actor *actor) { } else if (skeletonObj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { skeletonObj = skeletonObj->m_parent; + skeletonFound = true; break; } skeletonObj = skeletonObj->m_parent; } - + if (skeletonFound == false) { + logger.error("Couldn't find skeleton for actor %08x\n", actor->formID); + return; + } #if DEBUG logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); showRot(skeletonObj->m_worldTransform.rot); //showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif //NiMatrix43 origWorldRot = origLocalRot * obj->m_parent->m_worldTransform.rot; - - NiMatrix43 targetRot = skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot * skeletonObj->m_localTransform.rot * obj->m_parent->m_worldTransform.rot.Transpose(); + NiMatrix43 targetRot; + if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { +#if DEBUG + logger.error("FG Breasts Transform targetRot\n"); +#endif + targetRot = skeletonObj->m_localTransform.rot.Transpose(); + } + else { + //targetRot = skeletonObj->m_localTransform.rot.Transpose() * + // comObj->m_localTransform.rot * + // skeletonObj->m_localTransform.rot * + // obj->m_parent->m_worldTransform.rot.Transpose(); + targetRot = skeletonObj->m_localTransform.rot.Transpose(); + } NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; // Offset to move Center of Mass make rotational motion more significant - NiPoint3 target = (targetRot * NiPoint3(0, cogOffset, 0)) + origWorldPos; + NiPoint3 target = (targetRot * NiPoint3(0, cogOffset, cogOffsetZ)) + origWorldPos; // TODO: left and right with same parents transforms should be different... example: the butt // Relative Left @@ -199,18 +219,18 @@ void Thing::update(Actor *actor) { #if DEBUG logger.error("World Position: "); showPos(obj->m_worldTransform.pos); - logger.error("Parent World Position difference: "); - showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); + //logger.error("Parent World Position difference: "); + //showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); logger.error("Target Rotation * cogOffset %8.4f: ", cogOffset); showPos(targetRot * NiPoint3(0, cogOffset, 0)); - logger.error("Target Rotation:\n"); - showRot(targetRot); - //logger.error("cogOffset x Transformation:"); - //showPos(targetRot * NiPoint3(cogOffset, 0, 0)); - //logger.error("cogOffset y Transformation:"); - //showPos(targetRot * NiPoint3(0, cogOffset, 0)); - //logger.error("cogOffset z Transformation:"); - //showPos(targetRot * NiPoint3(0, 0, cogOffset)); + //logger.error("Target Rotation:\n"); + //showRot(targetRot); + logger.error("cogOffset x Transformation:"); + showPos(targetRot * NiPoint3(cogOffset, 0, 0)); + logger.error("cogOffset y Transformation:"); + showPos(targetRot * NiPoint3(0, cogOffset, 0)); + logger.error("cogOffset z Transformation:"); + showPos(targetRot * NiPoint3(0, 0, cogOffset)); #endif // diff is Difference in position between old and new world position @@ -256,7 +276,7 @@ void Thing::update(Actor *actor) { deltaT -= timeTick; } while (deltaT >= timeTick); - NiPoint3 newPos = oldWorldPos + posDelta; + NiPoint3 newPos = oldWorldPos +posDelta; oldWorldPos = diff + target; @@ -284,8 +304,25 @@ void Thing::update(Actor *actor) { // move the bones based on the supplied weightings // Convert the world translations into local coordinates - auto invRot = obj->m_parent->m_worldTransform.rot; - auto localDiff = invRot * diff; + + NiMatrix43 invRot; + + if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { +#if DEBUG + logger.error("FG Breasts Transform invRot\n"); +#endif + invRot = obj->m_parent->m_worldTransform.rot; + } + else { + //invRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose(); + invRot = obj->m_parent->m_worldTransform.rot; + } + + auto localDiff = NiPoint3(diff.x * linearX, + diff.y * linearY, + diff.z * linearZ); + auto rotDiff = localDiff; + localDiff = invRot * localDiff; // remove component along bone - might want something closer to world //localDiff.y = 0; @@ -306,14 +343,18 @@ void Thing::update(Actor *actor) { showPos(localDiff); #endif // scale positions from config - NiPoint3 newLocalPos = NiPoint3((localDiff.x * linearX) + origLocalPos.at(boneName.c_str()).x, - (localDiff.y * linearY) + origLocalPos.at(boneName.c_str()).y, - (localDiff.z * linearZ) + origLocalPos.at(boneName.c_str()).z + NiPoint3 newLocalPos = NiPoint3((localDiff.x) + origLocalPos.at(boneName.c_str()).x, + (localDiff.y) + origLocalPos.at(boneName.c_str()).y, + (localDiff.z) + origLocalPos.at(boneName.c_str()).z ); obj->m_localTransform.pos = newLocalPos; // do some rotation - auto rotDiff = targetRot * diff; + //auto rotDiff = targetRot.Transpose() * diff; +#if DEBUG + logger.error("rotDiff: "); + showPos(rotDiff); +#endif rotDiff.x *= rotationalX; rotDiff.y *= rotationalY; rotDiff.z *= rotationalZ; @@ -325,15 +366,12 @@ void Thing::update(Actor *actor) { showPos(origLocalPos.at(boneName.c_str())); logger.error("origLocalRot:"); showRot(origLocalRot.at(boneName.c_str())); - logger.error("rotDiff: "); - showPos(rotDiff); + #endif - float heading, attitude, bank; - - // EulerAngles are in radians - origLocalRot.at(boneName.c_str()).GetEulerAngles(&heading, &attitude, &bank); - // Note... this is supposed in the rotational space of m_localTransform - obj->m_localTransform.rot.SetEulerAngles(heading + rotDiff.z, attitude + rotDiff.y, bank + rotDiff.x); + // Do rotation. + NiMatrix43 standardRot; + standardRot.SetEulerAngles(rotDiff.x, rotDiff.y, rotDiff.z); + obj->m_localTransform.rot = standardRot * origLocalRot.at(boneName.c_str()); } #if DEBUG logger.error("end update()\n"); diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 9793dd5..ea70dab 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -19,6 +19,9 @@ class Thing { float damping = 0.2f; float maxOffset = 5.0f; float cogOffset = 0.0f; + float cogOffsetX = 0.0f; + float cogOffsetZ = 0.0f; + float gravityBias = 0.0f; float gravityCorrection = 0.0f; //float zOffset = 0.0f; // Computed based on GravityBias value @@ -30,6 +33,7 @@ class Thing { float rotationalY = 0.1; float rotationalZ = 0.1; float timeStep = 1.0f; + bool fusionGirlEnabled; Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 3822ab1..10d27b7 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -25,7 +25,7 @@ const char* rightButtName_CBBE = "RButtFat_skin"; void loadConfig() { char buffer[1024]; - logger.info("loadConfig\n"); + //logger.info("loadConfig\n"); FILE *fh = fopen("Data\\F4SE\\Plugins\\CBPConfig.txt", "r"); if (!fh) { logger.error("Failed to open config file CBPConfig.txt\n"); From cd42d245f45f54a5fe2e52b570a54f8c25611b57 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Tue, 14 Jan 2020 02:20:07 -0800 Subject: [PATCH 08/66] Added separate maxOffsets for each axis. Transformation modification to CBP right breast. --- CBPSSE/Thing.cpp | 33 ++++++++++++++++++++------------- CBPSSE/Thing.h | 6 ++++-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index c59144b..a60be8b 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -49,7 +49,9 @@ void Thing::updateConfig(configEntry_t & centry) { stiffness = centry["stiffness"]; stiffness2 = centry["stiffness2"]; damping = centry["damping"]; - maxOffset = centry["maxoffset"]; + maxOffsetX = centry["maxoffsetX"]; + maxOffsetY = centry["maxoffsetY"]; + maxOffsetZ = centry["maxoffsetZ"]; timeTick = centry["timetick"]; linearX = centry["linearX"]; linearY = centry["linearY"]; @@ -64,7 +66,7 @@ void Thing::updateConfig(configEntry_t & centry) { timeStep = 1.0f; gravityBias = centry["gravityBias"]; gravityCorrection = centry["gravityCorrection"]; - cogOffset = centry["cogOffset"]; + cogOffsetY = centry["cogOffsetY"]; cogOffsetX = centry["cogOffsetX"]; cogOffsetZ = centry["cogOffsetZ"]; fusionGirlEnabled = centry["FG"] == 1.0; @@ -204,7 +206,7 @@ void Thing::update(Actor *actor) { NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; // Offset to move Center of Mass make rotational motion more significant - NiPoint3 target = (targetRot * NiPoint3(0, cogOffset, cogOffsetZ)) + origWorldPos; + NiPoint3 target = (targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; // TODO: left and right with same parents transforms should be different... example: the butt // Relative Left @@ -221,22 +223,22 @@ void Thing::update(Actor *actor) { showPos(obj->m_worldTransform.pos); //logger.error("Parent World Position difference: "); //showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); - logger.error("Target Rotation * cogOffset %8.4f: ", cogOffset); - showPos(targetRot * NiPoint3(0, cogOffset, 0)); + logger.error("Target Rotation * cogOffsetY %8.4f: ", cogOffsetY); + showPos(targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)); //logger.error("Target Rotation:\n"); //showRot(targetRot); logger.error("cogOffset x Transformation:"); - showPos(targetRot * NiPoint3(cogOffset, 0, 0)); + showPos(targetRot * NiPoint3(cogOffsetX, 0, 0)); logger.error("cogOffset y Transformation:"); - showPos(targetRot * NiPoint3(0, cogOffset, 0)); + showPos(targetRot * NiPoint3(0, cogOffsetY, 0)); logger.error("cogOffset z Transformation:"); - showPos(targetRot * NiPoint3(0, 0, cogOffset)); + showPos(targetRot * NiPoint3(0, 0, cogOffsetZ)); #endif // diff is Difference in position between old and new world position NiPoint3 diff = target - oldWorldPos; - // move up in rotated angle for gravity correction + // Move up in for gravity correction diff += targetRot * NiPoint3(0, 0, gravityCorrection); #if DEBUG @@ -289,9 +291,9 @@ void Thing::update(Actor *actor) { // clamp the difference to stop the breast severely lagging at low framerates diff = newPos - target; - diff.x = clamp(diff.x, -maxOffset, maxOffset); - diff.y = clamp(diff.y, -maxOffset, maxOffset); - diff.z = clamp(diff.z - gravityCorrection, -maxOffset, maxOffset) + gravityCorrection; + diff.x = clamp(diff.x, -maxOffsetX, maxOffsetX); + diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); + diff.z = clamp(diff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; //oldWorldPos = target + diff; @@ -307,7 +309,12 @@ void Thing::update(Actor *actor) { NiMatrix43 invRot; - if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { + if (obj->m_name == BSFixedString("Breast_CBP_R_02")) { + NiMatrix43 standardRot; + standardRot.SetEulerAngles(0, PI, 0); + invRot = standardRot * obj->m_parent->m_worldTransform.rot; + } + else if (obj->m_name == BSFixedString("Breast_CBP_L_02")) { #if DEBUG logger.error("FG Breasts Transform invRot\n"); #endif diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index ea70dab..83057e8 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -17,9 +17,11 @@ class Thing { float stiffness = 0.5f; float stiffness2 = 0.0f; float damping = 0.2f; - float maxOffset = 5.0f; - float cogOffset = 0.0f; + float maxOffsetX = 5.0f; + float maxOffsetY = 5.0f; + float maxOffsetZ = 5.0f; float cogOffsetX = 0.0f; + float cogOffsetY = 0.0f; float cogOffsetZ = 0.0f; float gravityBias = 0.0f; From 230f9f24c30e96dc3df1d9244b1a3a06741af70b Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Thu, 16 Jan 2020 01:59:37 -0800 Subject: [PATCH 09/66] Added a rotation for motion and made configs per bone basis. --- CBPSSE/SimObj.cpp | 21 ++++++++-------- CBPSSE/SimObj.h | 8 +++--- CBPSSE/Thing.cpp | 33 +++++++++++------------- CBPSSE/Thing.h | 5 ++++ CBPSSE/config.cpp | 64 ++++++++++++++++++++++++++--------------------- 5 files changed, 70 insertions(+), 61 deletions(-) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index c3a1333..3b5680f 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -10,9 +10,9 @@ enum femaleBodyType {CBBE_BODY, FG_BODY}; const char *leftScrotumName_BT2 = "Penis_Balls_CBP_01"; const char *rightScrotumName_BT2 = "Penis_Balls_CBP_02"; -std::unordered_map configMap; +//std::unordered_map configMap; -std::vector femaleBones; +std::vector femaleBones; SimObj::SimObj(Actor *actor, config_t &config) : things(4){ @@ -23,7 +23,7 @@ SimObj::~SimObj() { } -bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &config) +bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &config) { logger.error("bind\n"); @@ -33,8 +33,9 @@ bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t & bound = true; things.clear(); - for (const char * &b : boneNames) { - BSFixedString cs(b); + for (std::string b : boneNames) { + const char* bone_c_str = b.c_str(); + BSFixedString cs(bone_c_str); auto bone = loadedData->rootNode->GetObjectByName(&cs); if (!bone) { logger.info("Failed to find Bone %s for actor %08x\n", b, actor->formID); @@ -69,11 +70,11 @@ void SimObj::update(Actor *actor) { } bool SimObj::updateConfig(config_t & config) { - for (auto &t : things) { - std::string §ion = configMap[t.first]; - //logger.info("updateConfig in SimObj: %s, %s\n", section, t.first); - auto ¢ry = config[section]; - t.second.updateConfig(centry); + for (auto &thing : things) { + std::string &boneName = std::string(thing.first); + logger.info("updateConfig in SimObj: %s, %s\n", boneName, thing.first); + //auto ¢ry = config[section]; + thing.second.updateConfig(config[boneName]); } return true; } diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index e90d412..d6e72dd 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -10,11 +10,11 @@ class SimObj { UInt32 id = 0; bool bound = false; public: - std::unordered_map things; + std::unordered_map things; SimObj(Actor *actor, config_t &config); SimObj() {} ~SimObj(); - bool bind(Actor *actor, std::vector &boneNames, config_t &config); + bool bind(Actor *actor, std::vector &boneNames, config_t &config); bool actorValid(Actor *actor); void update(Actor *actor); bool updateConfig(config_t &config); @@ -23,5 +23,5 @@ class SimObj { }; -extern std::vector femaleBones; -extern std::unordered_map configMap; \ No newline at end of file +extern std::vector femaleBones; +//extern std::unordered_map configMap; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index a60be8b..55a1eed 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -3,7 +3,7 @@ #include "f4se\NiNodes.h" #include -#define PI 3.14159265 +#define DEG_TO_RAD 3.14159265 / 180 #define DEBUG 0 #define TRANSFORM_DEBUG 0 @@ -59,6 +59,9 @@ void Thing::updateConfig(configEntry_t & centry) { rotationalX = centry["rotationalX"]; rotationalY = centry["rotationalY"]; rotationalZ = centry["rotationalZ"]; + rotationX = centry["rotationX"]; + rotationY = centry["rotationY"]; + rotationZ = centry["rotationZ"]; // Optional entries for backwards compatability if (centry.find("timeStep") != centry.end()) timeStep = centry["timeStep"]; @@ -208,15 +211,6 @@ void Thing::update(Actor *actor) { // Offset to move Center of Mass make rotational motion more significant NiPoint3 target = (targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; - // TODO: left and right with same parents transforms should be different... example: the butt - // Relative Left - //if (obj->m_localTransform.pos.x < 0.0) { - //} - //// Relative Right - //else { - // target = (obj->m_worldTransform.rot * - // (obj->m_localTransform.pos + NiPoint3(0, 0, 0))) + obj->m_parent->m_worldTransform.pos; - //} #if DEBUG logger.error("World Position: "); @@ -309,20 +303,23 @@ void Thing::update(Actor *actor) { NiMatrix43 invRot; - if (obj->m_name == BSFixedString("Breast_CBP_R_02")) { - NiMatrix43 standardRot; - standardRot.SetEulerAngles(0, PI, 0); - invRot = standardRot * obj->m_parent->m_worldTransform.rot; - } - else if (obj->m_name == BSFixedString("Breast_CBP_L_02")) { + if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { #if DEBUG logger.error("FG Breasts Transform invRot\n"); #endif - invRot = obj->m_parent->m_worldTransform.rot; + NiMatrix43 standardRot; + standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, + rotationY * DEG_TO_RAD, + rotationZ * DEG_TO_RAD); + invRot = standardRot * obj->m_parent->m_worldTransform.rot; } else { //invRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose(); - invRot = obj->m_parent->m_worldTransform.rot; + NiMatrix43 standardRot; + standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, + rotationY * DEG_TO_RAD, + rotationZ * DEG_TO_RAD); + invRot = standardRot * obj->m_parent->m_worldTransform.rot; } auto localDiff = NiPoint3(diff.x * linearX, diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 83057e8..3e4e5ac 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -34,6 +34,11 @@ class Thing { float rotationalX = 0.1; float rotationalY = 0.1; float rotationalZ = 0.1; + + float rotationX = 0.0; + float rotationY = 0.0; + float rotationZ = 0.0; + float timeStep = 1.0f; bool fusionGirlEnabled; diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 10d27b7..542cd19 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -4,7 +4,7 @@ #include #include #include "config.h" - +#include #pragma warning(disable : 4996) int configReloadCount = 60; @@ -25,6 +25,7 @@ const char* rightButtName_CBBE = "RButtFat_skin"; void loadConfig() { char buffer[1024]; + std::set femaleBonesSet; //logger.info("loadConfig\n"); FILE *fh = fopen("Data\\F4SE\\Plugins\\CBPConfig.txt", "r"); if (!fh) { @@ -35,6 +36,7 @@ void loadConfig() { } //Console_Print("Reading CBP Config"); + // Rewrite this eventually config.clear(); do { auto str = fgets(buffer, 1023, fh); @@ -48,37 +50,41 @@ void loadConfig() { if (tok0 && tok1 && tok2) { config[std::string(tok0)][std::string(tok1)] = atof(tok2); } + if (std::string(tok0) != "Tuning") { + femaleBones.push_back(std::string(tok0)); + } } } } while (!feof(fh)); fclose(fh); - - // temporary config setup until ini format - if (config["Breast"]["FG"] == 1.0) { - femaleBones.push_back(leftBreastName_FG); - femaleBones.push_back(rightBreastName_FG); - configMap.insert({ leftBreastName_FG, "Breast" }); - configMap.insert({ rightBreastName_FG, "Breast" }); - } - else { - // Default is CBBE - femaleBones.push_back(leftBreastName_CBBE); - femaleBones.push_back(rightBreastName_CBBE); - configMap.insert({ leftBreastName_CBBE, "Breast" }); - configMap.insert({ rightBreastName_CBBE, "Breast" }); - } - if (config["Butt"]["FG"] == 1.0) { - femaleBones.push_back(leftButtName_FG); - femaleBones.push_back(rightButtName_FG); - configMap.insert({ leftButtName_FG, "Butt" }); - configMap.insert({ rightButtName_FG, "Butt" }); - } - else { - // Default is CBBE - femaleBones.push_back(leftButtName_CBBE); - femaleBones.push_back(rightButtName_CBBE); - configMap.insert({ leftButtName_CBBE, "Butt" }); - configMap.insert({ rightButtName_CBBE, "Butt" }); - } + femaleBonesSet = std::set(femaleBones.begin(), femaleBones.end()); + femaleBones.assign(femaleBonesSet.begin(), femaleBonesSet.end()); + //// temporary config setup until ini format + //if (config["Breast"]["FG"] == 1.0) { + // femaleBones.push_back(leftBreastName_FG); + // femaleBones.push_back(rightBreastName_FG); + // configMap.insert({ leftBreastName_FG, "Breast" }); + // configMap.insert({ rightBreastName_FG, "Breast" }); + //} + //else { + // // Default is CBBE + // femaleBones.push_back(leftBreastName_CBBE); + // femaleBones.push_back(rightBreastName_CBBE); + // configMap.insert({ leftBreastName_CBBE, "Breast" }); + // configMap.insert({ rightBreastName_CBBE, "Breast" }); + //} + //if (config["Butt"]["FG"] == 1.0) { + // femaleBones.push_back(leftButtName_FG); + // femaleBones.push_back(rightButtName_FG); + // configMap.insert({ leftButtName_FG, "Butt" }); + // configMap.insert({ rightButtName_FG, "Butt" }); + //} + //else { + // // Default is CBBE + // femaleBones.push_back(leftButtName_CBBE); + // femaleBones.push_back(rightButtName_CBBE); + // configMap.insert({ leftButtName_CBBE, "Butt" }); + // configMap.insert({ rightButtName_CBBE, "Butt" }); + //} configReloadCount = config["Tuning"]["rate"]; } From c39296a4093bd7a19a16b072e635462af6278f4a Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Fri, 17 Jan 2020 02:02:22 -0800 Subject: [PATCH 10/66] Cleanup --- CBPSSE/Thing.cpp | 569 +++++++++++++++++++++++------------------------ 1 file changed, 279 insertions(+), 290 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 55a1eed..9bac70b 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -3,7 +3,8 @@ #include "f4se\NiNodes.h" #include -#define DEG_TO_RAD 3.14159265 / 180 +constexpr auto DEG_TO_RAD = 3.14159265 / 180; + #define DEBUG 0 #define TRANSFORM_DEBUG 0 @@ -13,372 +14,360 @@ const char * skeletonNif_boneName = "skeleton.nif"; const char* COM_boneName = "COM"; Thing::Thing(NiAVObject* obj, BSFixedString& name) - : boneName(name) - , velocity(NiPoint3(0, 0, 0)) + : boneName(name) + , velocity(NiPoint3(0, 0, 0)) { - oldWorldPos = obj->m_worldTransform.pos; + oldWorldPos = obj->m_worldTransform.pos; - time = clock(); + time = clock(); } Thing::~Thing() { } void showPos(NiPoint3 &p) { - logger.info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); + logger.info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); } void showRot(NiMatrix43 &r) { - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); } float solveQuad(float a, float b, float c) { - float k1 = (-b + sqrtf(b*b - 4*a*c)) / (2 * a); - //float k2 = (-b - sqrtf(b*b - 4*a*c)) / (2 * a); - //logger.error("k2 = %f\n", k2); - return k1; + float k1 = (-b + sqrtf(b*b - 4*a*c)) / (2 * a); + //float k2 = (-b - sqrtf(b*b - 4*a*c)) / (2 * a); + //logger.error("k2 = %f\n", k2); + return k1; } void Thing::updateConfig(configEntry_t & centry) { - stiffness = centry["stiffness"]; - stiffness2 = centry["stiffness2"]; - damping = centry["damping"]; - maxOffsetX = centry["maxoffsetX"]; - maxOffsetY = centry["maxoffsetY"]; - maxOffsetZ = centry["maxoffsetZ"]; - timeTick = centry["timetick"]; - linearX = centry["linearX"]; - linearY = centry["linearY"]; - linearZ = centry["linearZ"]; - rotationalX = centry["rotationalX"]; - rotationalY = centry["rotationalY"]; - rotationalZ = centry["rotationalZ"]; - rotationX = centry["rotationX"]; - rotationY = centry["rotationY"]; - rotationZ = centry["rotationZ"]; - // Optional entries for backwards compatability - if (centry.find("timeStep") != centry.end()) - timeStep = centry["timeStep"]; - else - timeStep = 1.0f; - gravityBias = centry["gravityBias"]; - gravityCorrection = centry["gravityCorrection"]; - cogOffsetY = centry["cogOffsetY"]; - cogOffsetX = centry["cogOffsetX"]; - cogOffsetZ = centry["cogOffsetZ"]; - fusionGirlEnabled = centry["FG"] == 1.0; - if (timeTick <= 1) - timeTick = 1; - - //zOffset = solveQuad(stiffness2, stiffness, -gravityBias); - - //logger.error("z offset = %f\n", solveQuad(stiffness2, stiffness, -gravityBias)); + stiffness = centry["stiffness"]; + stiffness2 = centry["stiffness2"]; + damping = centry["damping"]; + maxOffsetX = centry["maxoffsetX"]; + maxOffsetY = centry["maxoffsetY"]; + maxOffsetZ = centry["maxoffsetZ"]; + timeTick = centry["timetick"]; + linearX = centry["linearX"]; + linearY = centry["linearY"]; + linearZ = centry["linearZ"]; + rotationalX = centry["rotationalX"]; + rotationalY = centry["rotationalY"]; + rotationalZ = centry["rotationalZ"]; + rotationX = centry["rotationX"]; + rotationY = centry["rotationY"]; + rotationZ = centry["rotationZ"]; + // Optional entries for backwards compatability + if (centry.find("timeStep") != centry.end()) + timeStep = centry["timeStep"]; + else + timeStep = 1.0f; + gravityBias = centry["gravityBias"]; + gravityCorrection = centry["gravityCorrection"]; + cogOffsetY = centry["cogOffsetY"]; + cogOffsetX = centry["cogOffsetX"]; + cogOffsetZ = centry["cogOffsetZ"]; + fusionGirlEnabled = centry["FG"] == 1.0; + if (timeTick <= 1) + timeTick = 1; + + //zOffset = solveQuad(stiffness2, stiffness, -gravityBias); + + //logger.error("z offset = %f\n", solveQuad(stiffness2, stiffness, -gravityBias)); } void Thing::dump() { - //showPos(obj->m_worldTransform.pos); - //showPos(obj->m_localTransform.pos); + //showPos(obj->m_worldTransform.pos); + //showPos(obj->m_localTransform.pos); } static float clamp(float val, float min, float max) { - if (val < min) return min; - else if (val > max) return max; - return val; + if (val < min) return min; + else if (val > max) return max; + return val; } void Thing::reset() { - // TODO + } +// Returns template int sgn(T val) { - return (T(0) < val) - (val < T(0)); + return (T(0) < val) - (val < T(0)); } void Thing::update(Actor *actor) { - auto newTime = clock(); - auto deltaT = newTime - time; - - time = newTime; - if (deltaT > 64) deltaT = 64; - if (deltaT < 8) deltaT = 8; - - auto loadedState = actor->unkF0; - if (!loadedState || !loadedState->rootNode) { - logger.error("No loaded state for actor %08x\n", actor->formID); - return; - } - auto obj = loadedState->rootNode->GetObjectByName(&boneName); - - if (!obj) { - logger.error("Couldn't get name for loaded state for actor %08x\n", actor->formID); - return; - } - - if (!obj->m_parent) { - logger.error("Couldn't get bone %s parent for actor %08x\n", boneName.c_str() , actor->formID); - return; - } + auto newTime = clock(); + auto deltaT = newTime - time; + + time = newTime; + if (deltaT > 64) deltaT = 64; + if (deltaT < 8) deltaT = 8; + + auto loadedState = actor->unkF0; + if (!loadedState || !loadedState->rootNode) { + logger.error("No loaded state for actor %08x\n", actor->formID); + return; + } + auto obj = loadedState->rootNode->GetObjectByName(&boneName); + + if (!obj) { + logger.error("Couldn't get name for loaded state for actor %08x\n", actor->formID); + return; + } + + if (!obj->m_parent) { + logger.error("Couldn't get bone %s parent for actor %08x\n", boneName.c_str() , actor->formID); + return; + } #if TRANSFORM_DEBUG - auto sceneObj = obj; - while (sceneObj->m_parent && sceneObj->m_name != "skeleton.nif") - { - logger.info(sceneObj->m_name); - logger.info("\n---\n"); - logger.error("Actual m_localTransform.pos: "); - showPos(sceneObj->m_localTransform.pos); - logger.error("Actual m_worldTransform.pos: "); - showPos(sceneObj->m_worldTransform.pos); - logger.info("---\n"); - //logger.error("Actual m_localTransform.rot Matrix:\n"); - showRot(sceneObj->m_localTransform.rot); - //logger.error("Actual m_worldTransform.rot Matrix:\n"); - showRot(sceneObj->m_worldTransform.rot); - logger.info("---\n"); - //if (sceneObj->m_parent) { - // logger.error("Calculated m_worldTransform.pos: "); - // showPos((sceneObj->m_parent->m_worldTransform.rot.Transpose() * sceneObj->m_localTransform.pos) + sceneObj->m_parent->m_worldTransform.pos); - // logger.error("Calculated m_worldTransform.rot Matrix:\n"); - // showRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); - //} - sceneObj = sceneObj->m_parent; - } + auto sceneObj = obj; + while (sceneObj->m_parent && sceneObj->m_name != "skeleton.nif") + { + logger.info(sceneObj->m_name); + logger.info("\n---\n"); + logger.error("Actual m_localTransform.pos: "); + showPos(sceneObj->m_localTransform.pos); + logger.error("Actual m_worldTransform.pos: "); + showPos(sceneObj->m_worldTransform.pos); + logger.info("---\n"); + //logger.error("Actual m_localTransform.rot Matrix:\n"); + showRot(sceneObj->m_localTransform.rot); + //logger.error("Actual m_worldTransform.rot Matrix:\n"); + showRot(sceneObj->m_worldTransform.rot); + logger.info("---\n"); + //if (sceneObj->m_parent) { + // logger.error("Calculated m_worldTransform.pos: "); + // showPos((sceneObj->m_parent->m_worldTransform.rot.Transpose() * sceneObj->m_localTransform.pos) + sceneObj->m_parent->m_worldTransform.pos); + // logger.error("Calculated m_worldTransform.rot Matrix:\n"); + // showRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); + //} + sceneObj = sceneObj->m_parent; + } #endif - // Save the bone's original local position if it already hasn't - if (origLocalPos.find(boneName.c_str()) == origLocalPos.end()) { - logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); - logger.error("firstRun pos Set: "); - origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); - showPos(obj->m_localTransform.pos); - } - // Save the bone's original local rotation if it already hasn't - if (origLocalRot.find(boneName.c_str()) == origLocalRot.end()) { - logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); - logger.error("firstRun rot Set:\n"); - origLocalRot.emplace(boneName.c_str(), obj->m_localTransform.rot); - showRot(obj->m_localTransform.rot); - } - - auto skeletonObj = obj; - NiAVObject * comObj; - bool skeletonFound = false; - while (skeletonObj->m_parent) - { - if (skeletonObj->m_parent->m_name == BSFixedString(COM_boneName)) { - comObj = skeletonObj->m_parent; - } - else if (skeletonObj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { - skeletonObj = skeletonObj->m_parent; - skeletonFound = true; - break; - } - skeletonObj = skeletonObj->m_parent; - } - if (skeletonFound == false) { - logger.error("Couldn't find skeleton for actor %08x\n", actor->formID); - return; - } -#if DEBUG - logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); - showRot(skeletonObj->m_worldTransform.rot); - //showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); -#endif - //NiMatrix43 origWorldRot = origLocalRot * obj->m_parent->m_worldTransform.rot; - NiMatrix43 targetRot; - if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { + // TODO rewrite these for performance + // Save the bone's original local position if it already hasn't + if (origLocalPos.find(boneName.c_str()) == origLocalPos.end()) { + logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); + logger.error("firstRun pos Set: "); + origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); + showPos(obj->m_localTransform.pos); + } + // Save the bone's original local rotation if it already hasn't + if (origLocalRot.find(boneName.c_str()) == origLocalRot.end()) { + logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); + logger.error("firstRun rot Set:\n"); + origLocalRot.emplace(boneName.c_str(), obj->m_localTransform.rot); + showRot(obj->m_localTransform.rot); + } + + auto skeletonObj = obj; + NiAVObject * comObj; + bool skeletonFound = false; + while (skeletonObj->m_parent) + { + if (skeletonObj->m_parent->m_name == BSFixedString(COM_boneName)) { + comObj = skeletonObj->m_parent; + } + else if (skeletonObj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { + skeletonObj = skeletonObj->m_parent; + skeletonFound = true; + break; + } + skeletonObj = skeletonObj->m_parent; + } + if (skeletonFound == false) { + logger.error("Couldn't find skeleton for actor %08x\n", actor->formID); + return; + } #if DEBUG - logger.error("FG Breasts Transform targetRot\n"); + logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); + showRot(skeletonObj->m_worldTransform.rot); + //showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif - targetRot = skeletonObj->m_localTransform.rot.Transpose(); - } - else { - //targetRot = skeletonObj->m_localTransform.rot.Transpose() * - // comObj->m_localTransform.rot * - // skeletonObj->m_localTransform.rot * - // obj->m_parent->m_worldTransform.rot.Transpose(); - targetRot = skeletonObj->m_localTransform.rot.Transpose(); - } - NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; - - // Offset to move Center of Mass make rotational motion more significant - NiPoint3 target = (targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; - + NiMatrix43 targetRot; + if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { + targetRot = skeletonObj->m_localTransform.rot.Transpose(); + } + else { + //targetRot = skeletonObj->m_localTransform.rot.Transpose() * + // comObj->m_localTransform.rot * + // skeletonObj->m_localTransform.rot * + // obj->m_parent->m_worldTransform.rot.Transpose(); + targetRot = skeletonObj->m_localTransform.rot.Transpose(); + } + NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; + + // Offset to move Center of Mass make rotational motion more significant + NiPoint3 target = (targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; #if DEBUG - logger.error("World Position: "); - showPos(obj->m_worldTransform.pos); - //logger.error("Parent World Position difference: "); - //showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); - logger.error("Target Rotation * cogOffsetY %8.4f: ", cogOffsetY); - showPos(targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)); - //logger.error("Target Rotation:\n"); - //showRot(targetRot); - logger.error("cogOffset x Transformation:"); - showPos(targetRot * NiPoint3(cogOffsetX, 0, 0)); - logger.error("cogOffset y Transformation:"); - showPos(targetRot * NiPoint3(0, cogOffsetY, 0)); - logger.error("cogOffset z Transformation:"); - showPos(targetRot * NiPoint3(0, 0, cogOffsetZ)); + logger.error("World Position: "); + showPos(obj->m_worldTransform.pos); + //logger.error("Parent World Position difference: "); + //showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); + logger.error("Target Rotation * cogOffsetY %8.4f: ", cogOffsetY); + showPos(targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)); + //logger.error("Target Rotation:\n"); + //showRot(targetRot); + logger.error("cogOffset x Transformation:"); + showPos(targetRot * NiPoint3(cogOffsetX, 0, 0)); + logger.error("cogOffset y Transformation:"); + showPos(targetRot * NiPoint3(0, cogOffsetY, 0)); + logger.error("cogOffset z Transformation:"); + showPos(targetRot * NiPoint3(0, 0, cogOffsetZ)); #endif - // diff is Difference in position between old and new world position - NiPoint3 diff = target - oldWorldPos; + // diff is Difference in position between old and new world position + NiPoint3 diff = target - oldWorldPos; - // Move up in for gravity correction - diff += targetRot * NiPoint3(0, 0, gravityCorrection); + // Move up in for gravity correction + diff += targetRot * NiPoint3(0, 0, gravityCorrection); #if DEBUG - logger.error("Diff after gravity correction %f: ", gravityCorrection); - showPos(diff); + logger.error("Diff after gravity correction %f: ", gravityCorrection); + showPos(diff); #endif - if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { - logger.error("transform reset\n"); - obj->m_localTransform.pos = origLocalPos.at(boneName.c_str()); - oldWorldPos = target; - velocity = NiPoint3(0, 0, 0); - time = clock(); - } else { + if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { + logger.error("transform reset\n"); + obj->m_localTransform.pos = origLocalPos.at(boneName.c_str()); + oldWorldPos = target; + velocity = NiPoint3(0, 0, 0); + time = clock(); + } else { - diff *= timeTick / (float)deltaT; - NiPoint3 posDelta = NiPoint3(0, 0, 0); + diff *= timeTick / (float)deltaT; + NiPoint3 posDelta = NiPoint3(0, 0, 0); - // Compute the "Spring" Force - NiPoint3 diff2(diff.x * diff.x * sgn(diff.x), diff.y * diff.y * sgn(diff.y), diff.z * diff.z * sgn(diff.z)); - NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - (targetRot * NiPoint3(0, 0, gravityBias)); + // Compute the "Spring" Force + NiPoint3 diff2(diff.x * diff.x * sgn(diff.x), diff.y * diff.y * sgn(diff.y), diff.z * diff.z * sgn(diff.z)); + NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - (targetRot * NiPoint3(0, 0, gravityBias)); #if DEBUG - logger.error("Diff2: "); - showPos(diff2); - logger.error("Force with stiffness %f, stiffness2 %f, gravity bias %f: ", stiffness, stiffness2, gravityBias); - showPos(force); + logger.error("Diff2: "); + showPos(diff2); + logger.error("Force with stiffness %f, stiffness2 %f, gravity bias %f: ", stiffness, stiffness2, gravityBias); + showPos(force); #endif - do { - // Assume mass is 1, so Accelleration is Force, can vary mass by changinf force - //velocity = (velocity + (force * timeStep)) * (1 - (damping * timeStep)); - velocity = (velocity + (force * timeStep)) - (velocity * (damping * timeStep)); + do { + // Assume mass is 1, so Accelleration is Force, can vary mass by changinf force + //velocity = (velocity + (force * timeStep)) * (1 - (damping * timeStep)); + velocity = (velocity + (force * timeStep)) - (velocity * (damping * timeStep)); - // New position accounting for time - posDelta += (velocity * timeStep); - deltaT -= timeTick; - } while (deltaT >= timeTick); + // New position accounting for time + posDelta += (velocity * timeStep); + deltaT -= timeTick; + } while (deltaT >= timeTick); - NiPoint3 newPos = oldWorldPos +posDelta; + NiPoint3 newPos = oldWorldPos +posDelta; - oldWorldPos = diff + target; + oldWorldPos = diff + target; #if DEBUG - //logger.error("posDelta: "); - //showPos(posDelta); - logger.error("newPos: "); - showPos(newPos); + //logger.error("posDelta: "); + //showPos(posDelta); + logger.error("newPos: "); + showPos(newPos); #endif - // clamp the difference to stop the breast severely lagging at low framerates - diff = newPos - target; + // clamp the difference to stop the breast severely lagging at low framerates + diff = newPos - target; - diff.x = clamp(diff.x, -maxOffsetX, maxOffsetX); - diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); - diff.z = clamp(diff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; + diff.x = clamp(diff.x, -maxOffsetX, maxOffsetX); + diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); + diff.z = clamp(diff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; - //oldWorldPos = target + diff; + //oldWorldPos = target + diff; #if DEBUG - logger.error("diff from newPos: "); - showPos(diff); - //logger.error("oldWorldPos: "); - //showPos(oldWorldPos); + logger.error("diff from newPos: "); + showPos(diff); + //logger.error("oldWorldPos: "); + //showPos(oldWorldPos); #endif - // move the bones based on the supplied weightings - // Convert the world translations into local coordinates - - NiMatrix43 invRot; - - if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { -#if DEBUG - logger.error("FG Breasts Transform invRot\n"); -#endif - NiMatrix43 standardRot; - standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, - rotationY * DEG_TO_RAD, - rotationZ * DEG_TO_RAD); - invRot = standardRot * obj->m_parent->m_worldTransform.rot; - } - else { - //invRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose(); - NiMatrix43 standardRot; - standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, - rotationY * DEG_TO_RAD, - rotationZ * DEG_TO_RAD); - invRot = standardRot * obj->m_parent->m_worldTransform.rot; - } - - auto localDiff = NiPoint3(diff.x * linearX, - diff.y * linearY, - diff.z * linearZ); - auto rotDiff = localDiff; - localDiff = invRot * localDiff; - - // remove component along bone - might want something closer to world - //localDiff.y = 0; - - oldWorldPos = diff + target; + // move the bones based on the supplied weightings + // Convert the world translations into local coordinates + + NiMatrix43 invRot; + + if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { + NiMatrix43 standardRot; + standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, + rotationY * DEG_TO_RAD, + rotationZ * DEG_TO_RAD); + invRot = standardRot * obj->m_parent->m_worldTransform.rot; + } + else { + //invRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose(); + NiMatrix43 standardRot; + standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, + rotationY * DEG_TO_RAD, + rotationZ * DEG_TO_RAD); + invRot = standardRot * obj->m_parent->m_worldTransform.rot; + } + + auto localDiff = NiPoint3(diff.x * linearX, + diff.y * linearY, + diff.z * linearZ); + auto rotDiff = localDiff; + localDiff = invRot * localDiff; + + oldWorldPos = diff + target; #if DEBUG - logger.error("invRot x=10 Transformation:"); - showPos(invRot * NiPoint3(10, 0, 0)); - logger.error("invRot y=10 Transformation:"); - showPos(invRot * NiPoint3(0, 10, 0)); - logger.error("invRot z=10 Transformation:"); - showPos(invRot * NiPoint3(0, 0, 10)); - logger.error("oldWorldPos: "); - showPos(oldWorldPos); - logger.error("localTransform.pos: "); - showPos(obj->m_localTransform.pos); - logger.error("localDiff: "); - showPos(localDiff); -#endif - // scale positions from config - NiPoint3 newLocalPos = NiPoint3((localDiff.x) + origLocalPos.at(boneName.c_str()).x, - (localDiff.y) + origLocalPos.at(boneName.c_str()).y, - (localDiff.z) + origLocalPos.at(boneName.c_str()).z - ); - obj->m_localTransform.pos = newLocalPos; - - // do some rotation - //auto rotDiff = targetRot.Transpose() * diff; -#if DEBUG - logger.error("rotDiff: "); - showPos(rotDiff); + logger.error("invRot x=10 Transformation:"); + showPos(invRot * NiPoint3(10, 0, 0)); + logger.error("invRot y=10 Transformation:"); + showPos(invRot * NiPoint3(0, 10, 0)); + logger.error("invRot z=10 Transformation:"); + showPos(invRot * NiPoint3(0, 0, 10)); + logger.error("oldWorldPos: "); + showPos(oldWorldPos); + logger.error("localTransform.pos: "); + showPos(obj->m_localTransform.pos); + logger.error("localDiff: "); + showPos(localDiff); + logger.error("rotDiff: "); + showPos(rotDiff); + #endif - rotDiff.x *= rotationalX; - rotDiff.y *= rotationalY; - rotDiff.z *= rotationalZ; + // scale positions from config + NiPoint3 newLocalPos = NiPoint3((localDiff.x) + origLocalPos.at(boneName.c_str()).x, + (localDiff.y) + origLocalPos.at(boneName.c_str()).y, + (localDiff.z) + origLocalPos.at(boneName.c_str()).z + ); + obj->m_localTransform.pos = newLocalPos; + + rotDiff.x *= rotationalX; + rotDiff.y *= rotationalY; + rotDiff.z *= rotationalZ; #if DEBUG - logger.error("localTransform.pos after: "); - showPos(obj->m_localTransform.pos); - logger.error("origLocalPos:"); - showPos(origLocalPos.at(boneName.c_str())); - logger.error("origLocalRot:"); - showRot(origLocalRot.at(boneName.c_str())); + logger.error("localTransform.pos after: "); + showPos(obj->m_localTransform.pos); + logger.error("origLocalPos:"); + showPos(origLocalPos.at(boneName.c_str())); + logger.error("origLocalRot:"); + showRot(origLocalRot.at(boneName.c_str())); #endif - // Do rotation. - NiMatrix43 standardRot; - standardRot.SetEulerAngles(rotDiff.x, rotDiff.y, rotDiff.z); - obj->m_localTransform.rot = standardRot * origLocalRot.at(boneName.c_str()); - } + // Do rotation. + NiMatrix43 standardRot; + standardRot.SetEulerAngles(rotDiff.x, rotDiff.y, rotDiff.z); + obj->m_localTransform.rot = standardRot * origLocalRot.at(boneName.c_str()); + } #if DEBUG - logger.error("end update()\n"); + logger.error("end update()\n"); #endif From 04644061636b7ce7d37bb4a0449a1909cd12981c Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sat, 18 Jan 2020 20:24:11 -0800 Subject: [PATCH 11/66] Options playerOnly, maleOnly, femaleOnly added. --- CBPSSE/SimObj.cpp | 94 +++++++-------- CBPSSE/SimObj.h | 24 ++-- CBPSSE/Thing.h | 81 +++++++------ CBPSSE/config.cpp | 139 +++++++++++----------- CBPSSE/config.h | 9 ++ CBPSSE/hookD3D.cpp | 22 ++-- CBPSSE/log.h | 8 +- CBPSSE/main.cpp | 226 ++++++++++++++++++------------------ CBPSSE/scan.cpp | 283 +++++++++++++++++++++++---------------------- 9 files changed, 446 insertions(+), 440 deletions(-) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 3b5680f..1cffaf4 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -4,19 +4,16 @@ #include "f4se/GameRTTI.h" #include "log.h" - -enum femaleBodyType {CBBE_BODY, FG_BODY}; // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory const char *leftScrotumName_BT2 = "Penis_Balls_CBP_01"; const char *rightScrotumName_BT2 = "Penis_Balls_CBP_02"; -//std::unordered_map configMap; +std::vector boneNames; -std::vector femaleBones; +SimObj::SimObj(Actor *actor, config_t &config, int num_things) { +// : things(num_things){ -SimObj::SimObj(Actor *actor, config_t &config) - : things(4){ - id = actor->formID; + id = actor->formID; } SimObj::~SimObj() { @@ -25,57 +22,56 @@ SimObj::~SimObj() { bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &config) { - logger.error("bind\n"); - - - auto loadedData = actor->unkF0; - if (loadedData && loadedData->rootNode) { - bound = true; - - things.clear(); - for (std::string b : boneNames) { - const char* bone_c_str = b.c_str(); - BSFixedString cs(bone_c_str); - auto bone = loadedData->rootNode->GetObjectByName(&cs); - if (!bone) { - logger.info("Failed to find Bone %s for actor %08x\n", b, actor->formID); - } else { - logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); - things.emplace(b, Thing(bone, cs)); - } - } - updateConfig(config); - return true; - } - return false; +// logger.error("bind\n"); + + + auto loadedData = actor->unkF0; + if (loadedData && loadedData->rootNode) { + bound = true; + + things.clear(); + for (std::string b : boneNames) { + const char* bone_c_str = b.c_str(); + BSFixedString cs(bone_c_str); + auto bone = loadedData->rootNode->GetObjectByName(&cs); + if (!bone) { + logger.info("Failed to find Bone %s for actor %08x\n", b, actor->formID); + } else { + logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); + things.emplace(b, Thing(bone, cs)); + } + } + updateConfig(config); + return true; + } + return false; } bool SimObj::actorValid(Actor *actor) { - if (actor->flags & TESForm::kFlag_IsDeleted) - return false; - if (actor && actor->unkF0 && actor->unkF0->rootNode) - return true; - return false; + if (actor->flags & TESForm::kFlag_IsDeleted) + return false; + if (actor && actor->unkF0 && actor->unkF0->rootNode) + return true; + return false; } void SimObj::update(Actor *actor) { - if (!bound) - return; - //logger.error("update\n"); - for (auto &t : things) { - t.second.update(actor); - } - //logger.error("end SimObj update\n"); + if (!bound) + return; + //logger.error("update\n"); + for (auto &t : things) { + t.second.update(actor); + } + //logger.error("end SimObj update\n"); } bool SimObj::updateConfig(config_t & config) { - for (auto &thing : things) { - std::string &boneName = std::string(thing.first); - logger.info("updateConfig in SimObj: %s, %s\n", boneName, thing.first); - //auto ¢ry = config[section]; - thing.second.updateConfig(config[boneName]); - } - return true; + for (auto &thing : things) { + std::string &boneName = std::string(thing.first); + //logger.info("updateConfig in SimObj: %s, %s\n", boneName, thing.first); + thing.second.updateConfig(config[boneName]); + } + return true; } diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index d6e72dd..0c2e97f 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -7,21 +7,21 @@ #include "config.h" class SimObj { - UInt32 id = 0; - bool bound = false; + UInt32 id = 0; + bool bound = false; public: - std::unordered_map things; - SimObj(Actor *actor, config_t &config); - SimObj() {} - ~SimObj(); - bool bind(Actor *actor, std::vector &boneNames, config_t &config); - bool actorValid(Actor *actor); - void update(Actor *actor); - bool updateConfig(config_t &config); - bool isBound() { return bound; } + std::unordered_map things; + SimObj(Actor *actor, config_t &config, int num_things); + SimObj() {} + ~SimObj(); + bool bind(Actor *actor, std::vector &boneNames, config_t &config); + bool actorValid(Actor *actor); + void update(Actor *actor); + bool updateConfig(config_t &config); + bool isBound() { return bound; } }; -extern std::vector femaleBones; +extern std::vector boneNames; //extern std::unordered_map configMap; \ No newline at end of file diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 3e4e5ac..a52f96b 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -6,49 +6,46 @@ #include "config.h" class Thing { - BSFixedString boneName; - NiPoint3 oldWorldPos; - NiPoint3 velocity; - //NiPoint3 origLocalPos; - //NiMatrix43 origLocalRot; - clock_t time; + BSFixedString boneName; + NiPoint3 oldWorldPos; + NiPoint3 velocity; + clock_t time; public: - float stiffness = 0.5f; - float stiffness2 = 0.0f; - float damping = 0.2f; - float maxOffsetX = 5.0f; - float maxOffsetY = 5.0f; - float maxOffsetZ = 5.0f; - float cogOffsetX = 0.0f; - float cogOffsetY = 0.0f; - float cogOffsetZ = 0.0f; - - float gravityBias = 0.0f; - float gravityCorrection = 0.0f; - //float zOffset = 0.0f; // Computed based on GravityBias value - float timeTick = 4.0f; - float linearX = 0; - float linearY = 0; - float linearZ = 0; - float rotationalX = 0.1; - float rotationalY = 0.1; - float rotationalZ = 0.1; - - float rotationX = 0.0; - float rotationY = 0.0; - float rotationZ = 0.0; - - float timeStep = 1.0f; - bool fusionGirlEnabled; - - Thing(NiAVObject *obj, BSFixedString &name); - ~Thing(); - - void updateConfig(configEntry_t ¢ry); - void dump(); - - void update(Actor *actor); - void reset(); + float stiffness = 0.5f; + float stiffness2 = 0.0f; + float damping = 0.2f; + float maxOffsetX = 5.0f; + float maxOffsetY = 5.0f; + float maxOffsetZ = 5.0f; + float cogOffsetX = 0.0f; + float cogOffsetY = 0.0f; + float cogOffsetZ = 0.0f; + + float gravityBias = 0.0f; + float gravityCorrection = 0.0f; + float timeTick = 4.0f; + float linearX = 0; + float linearY = 0; + float linearZ = 0; + float rotationalX = 0.1; + float rotationalY = 0.1; + float rotationalZ = 0.1; + + float rotationX = 0.0; + float rotationY = 0.0; + float rotationZ = 0.0; + + float timeStep = 1.0f; + bool fusionGirlEnabled; + + Thing(NiAVObject *obj, BSFixedString &name); + ~Thing(); + + void updateConfig(configEntry_t ¢ry); + void dump(); + + void update(Actor *actor); + void reset(); }; \ No newline at end of file diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 542cd19..aed9535 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -5,86 +5,83 @@ #include #include "config.h" #include + +#include "f4se/GameObjects.h" +#include "f4se/GameRTTI.h" +#include "f4se_common/Utilities.h" + #pragma warning(disable : 4996) int configReloadCount = 60; - +bool playerOnly = false; +bool femaleOnly = false; +bool maleOnly = false; config_t config; -const char* leftBreastName_FG = "Breast_CBP_L_02"; -const char* rightBreastName_FG = "Breast_CBP_R_02"; -const char* leftButtName_FG = "Butt_CBP_L_01"; -const char* rightButtName_FG = "Butt_CBP_R_01"; - -const char* leftBreastName_CBBE = "LBreast_skin"; -const char* rightBreastName_CBBE = "RBreast_skin"; -const char* leftButtName_CBBE = "LButtFat_skin"; -const char* rightButtName_CBBE = "RButtFat_skin"; +//const char* leftBreastName_FG = "Breast_CBP_L_02"; +//const char* rightBreastName_FG = "Breast_CBP_R_02"; +//const char* leftButtName_FG = "Butt_CBP_L_01"; +//const char* rightButtName_FG = "Butt_CBP_R_01"; +// +//const char* leftBreastName_CBBE = "LBreast_skin"; +//const char* rightBreastName_CBBE = "RBreast_skin"; +//const char* leftButtName_CBBE = "LButtFat_skin"; +//const char* rightButtName_CBBE = "RButtFat_skin"; void loadConfig() { - char buffer[1024]; - std::set femaleBonesSet; - //logger.info("loadConfig\n"); - FILE *fh = fopen("Data\\F4SE\\Plugins\\CBPConfig.txt", "r"); - if (!fh) { - logger.error("Failed to open config file CBPConfig.txt\n"); - //Console_Print("Failed to open config file CBPConfig.txt"); - configReloadCount = 0; - return; - } + char buffer[1024]; + std::set bonesSet; + //logger.info("loadConfig\n"); + FILE *fh = fopen("Data\\F4SE\\Plugins\\CBPConfig.txt", "r"); + if (!fh) { + logger.error("Failed to open config file CBPConfig.txt\n"); + //Console_Print("Failed to open config file CBPConfig.txt"); + configReloadCount = 0; + return; + } - //Console_Print("Reading CBP Config"); - // Rewrite this eventually - config.clear(); - do { - auto str = fgets(buffer, 1023, fh); - //logger.error("str %s\n", str); - if (str && strlen(str) > 1) { - if (str[0] != '#') { - char *tok0 = strtok(str, "."); - char *tok1 = strtok(NULL, " "); - char *tok2 = strtok(NULL, " "); + //Console_Print("Reading CBP Config"); + // Rewrite this eventually + config.clear(); + do { + auto str = fgets(buffer, 1023, fh); + //logger.error("str %s\n", str); + if (str && strlen(str) > 1) { + if (str[0] != '#') { + char *tok0 = strtok(str, "."); + char *tok1 = strtok(NULL, " "); + char *tok2 = strtok(NULL, " "); - if (tok0 && tok1 && tok2) { - config[std::string(tok0)][std::string(tok1)] = atof(tok2); - } - if (std::string(tok0) != "Tuning") { - femaleBones.push_back(std::string(tok0)); - } - } - } - } while (!feof(fh)); - fclose(fh); - femaleBonesSet = std::set(femaleBones.begin(), femaleBones.end()); - femaleBones.assign(femaleBonesSet.begin(), femaleBonesSet.end()); - //// temporary config setup until ini format - //if (config["Breast"]["FG"] == 1.0) { - // femaleBones.push_back(leftBreastName_FG); - // femaleBones.push_back(rightBreastName_FG); - // configMap.insert({ leftBreastName_FG, "Breast" }); - // configMap.insert({ rightBreastName_FG, "Breast" }); - //} - //else { - // // Default is CBBE - // femaleBones.push_back(leftBreastName_CBBE); - // femaleBones.push_back(rightBreastName_CBBE); - // configMap.insert({ leftBreastName_CBBE, "Breast" }); - // configMap.insert({ rightBreastName_CBBE, "Breast" }); - //} - //if (config["Butt"]["FG"] == 1.0) { - // femaleBones.push_back(leftButtName_FG); - // femaleBones.push_back(rightButtName_FG); - // configMap.insert({ leftButtName_FG, "Butt" }); - // configMap.insert({ rightButtName_FG, "Butt" }); - //} - //else { - // // Default is CBBE - // femaleBones.push_back(leftButtName_CBBE); - // femaleBones.push_back(rightButtName_CBBE); - // configMap.insert({ leftButtName_CBBE, "Butt" }); - // configMap.insert({ rightButtName_CBBE, "Butt" }); - //} - configReloadCount = config["Tuning"]["rate"]; + if (tok0 && tok1 && tok2) { + config[std::string(tok0)][std::string(tok1)] = atof(tok2); + } + if (std::string(tok0) != "Tuning" && std::string(tok0) != "General") { + boneNames.push_back(std::string(tok0)); + } + } + } + } while (!feof(fh)); + fclose(fh); + bonesSet = std::set(boneNames.begin(), boneNames.end()); + boneNames.assign(bonesSet.begin(), bonesSet.end()); + + playerOnly = config["General"]["playerOnly"] == 1; + femaleOnly = config["General"]["femaleOnly"] == 1; + maleOnly = config["General"]["maleOnly"] == 1; + configReloadCount = config["Tuning"]["rate"]; + } + +bool IsActorMale(Actor* actor) +{ + TESNPC* actorNPC = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); + + auto npcSex = actorNPC ? CALL_MEMBER_FN(actorNPC, GetSex)() : 1; + + if (npcSex == 0) //Actor is male + return true; + else + return false; +} \ No newline at end of file diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 71b7cf8..121266f 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -1,8 +1,17 @@ #pragma once #include +class Configuration { +}; + typedef std::unordered_map configEntry_t; typedef std::unordered_map config_t; + +bool IsActorMale(Actor* actor); + +extern bool playerOnly; +extern bool femaleOnly; +extern bool maleOnly; extern int configReloadCount; extern config_t config; diff --git a/CBPSSE/hookD3D.cpp b/CBPSSE/hookD3D.cpp index c993d75..099555e 100644 --- a/CBPSSE/hookD3D.cpp +++ b/CBPSSE/hookD3D.cpp @@ -147,11 +147,11 @@ renderHook orender = nullptr; void scaleTest(); UINT64 __cdecl Render(void *This, UINT64 arg) { - //logger.error("This is called\n"); - //logger.error("orender = %016llx\n", orender); - scaleTest(); - return orender(This, arg); - //return 0; + //logger.error("This is called\n"); + //logger.error("orender = %016llx\n", orender); + scaleTest(); + return orender(This, arg); + //return 0; } //RelocPtr render(0xD69720); @@ -170,11 +170,11 @@ RelocPtr ProcessEventQueue_Internal(0x0211CF80); DetourXS renderDetour; void DoHook() { - logger.info("Attempting Game Hook\n"); - // Useful for finding the addresses - //CreateThread(NULL, 0, HookCreateFn, NULL, 0, NULL); + logger.info("Attempting Game Hook\n"); + // Useful for finding the addresses + //CreateThread(NULL, 0, HookCreateFn, NULL, 0, NULL); - //renderDetour.Create(render.GetPtr(), Render, &(LPVOID)orender); - renderDetour.Create((LPVOID)ProcessEventQueue_Internal.GetPtr(), Render, &(LPVOID)orender); - //orender = (renderHook)renderDetour.GetTrampoline(); + //renderDetour.Create(render.GetPtr(), Render, &(LPVOID)orender); + renderDetour.Create((LPVOID)ProcessEventQueue_Internal.GetPtr(), Render, &(LPVOID)orender); + //orender = (renderHook)renderDetour.GetTrampoline(); } \ No newline at end of file diff --git a/CBPSSE/log.h b/CBPSSE/log.h index e09f433..0f62938 100644 --- a/CBPSSE/log.h +++ b/CBPSSE/log.h @@ -3,11 +3,11 @@ class Logger { public: - Logger(const char* fname); - void info(const char* args...); - void error(const char* args...); + Logger(const char* fname); + void info(const char* args...); + void error(const char* args...); - FILE *handle; + FILE *handle; }; extern Logger logger; \ No newline at end of file diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index 7ac8752..eea9298 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -26,130 +26,130 @@ void DoHook(); void MessageHandler(F4SEMessagingInterface::Message * msg) { - switch (msg->type) - { - case F4SEMessagingInterface::kMessage_GameDataReady: - { - logger.info("kMessage_GameDataReady\n"); - } - break; - case F4SEMessagingInterface::kMessage_GameLoaded: - { - logger.info("kMessage_GameLoaded\n"); - } - break; - case F4SEMessagingInterface::kMessage_NewGame: - { - logger.info("kMessage_NewGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PreLoadGame: - { - logger.info("kMessage_PreLoadGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostLoad: - { - logger.info("kMessage_PostLoad\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostPostLoad: - { - logger.info("kMessage_PostPostLoad\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostLoadGame: - { - logger.info("kMessage_PostLoadGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PreSaveGame: - { - logger.info("kMessage_PreSaveGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostSaveGame: - { - logger.info("kMessage_PostSaveGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_DeleteGame: - { - logger.info("kMessage_DeleteGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_InputLoaded: - { - logger.info("kMessage_InputLoaded\n"); - } - break; - - } + switch (msg->type) + { + case F4SEMessagingInterface::kMessage_GameDataReady: + { + logger.info("kMessage_GameDataReady\n"); + } + break; + case F4SEMessagingInterface::kMessage_GameLoaded: + { + logger.info("kMessage_GameLoaded\n"); + } + break; + case F4SEMessagingInterface::kMessage_NewGame: + { + logger.info("kMessage_NewGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PreLoadGame: + { + logger.info("kMessage_PreLoadGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostLoad: + { + logger.info("kMessage_PostLoad\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostPostLoad: + { + logger.info("kMessage_PostPostLoad\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostLoadGame: + { + logger.info("kMessage_PostLoadGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PreSaveGame: + { + logger.info("kMessage_PreSaveGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostSaveGame: + { + logger.info("kMessage_PostSaveGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_DeleteGame: + { + logger.info("kMessage_DeleteGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_InputLoaded: + { + logger.info("kMessage_InputLoaded\n"); + } + break; + + } } extern "C" { - bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) - { - logger.info("CBP Physics F4SE Plugin\n"); - logger.error("Query called\n"); - - - // populate info structure - info->infoVersion = PluginInfo::kInfoVersion; - info->name = "CBP plugin"; - info->version = 24; - - // store plugin handle so we can identify ourselves later - g_pluginHandle = f4se->GetPluginHandle(); - - if (f4se->isEditor) - { - logger.error("loaded in editor, marking as incompatible\n"); - return false; - } - else if (f4se->runtimeVersion != RUNTIME_VERSION) - { - logger.error("unsupported runtime version %08X", f4se->runtimeVersion); - return false; - } - // supported runtime version - - logger.error("Query complete\n"); - return true; - } - - bool F4SEPlugin_Load(const F4SEInterface * f4se) - { - logger.error("CBP Loading\n"); - - g_task = (F4SETaskInterface *)f4se->QueryInterface(kInterface_Task); - if (!g_task) - { - logger.error("Couldn't get Task interface\n"); - return false; - } - - // Load initial config before the hook. - logger.error("Loading Config\n"); - loadConfig(); - //g_messagingInterface->RegisterListener(0, "F4SE", MessageHandler); - logger.error("Hooking Game\n"); - DoHook(); - logger.error("CBP Load Complete\n"); - return true; - } + bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) + { + logger.info("CBP Physics F4SE Plugin\n"); + logger.error("Query called\n"); + + + // populate info structure + info->infoVersion = PluginInfo::kInfoVersion; + info->name = "CBP plugin"; + info->version = 24; + + // store plugin handle so we can identify ourselves later + g_pluginHandle = f4se->GetPluginHandle(); + + if (f4se->isEditor) + { + logger.error("loaded in editor, marking as incompatible\n"); + return false; + } + else if (f4se->runtimeVersion != RUNTIME_VERSION) + { + logger.error("unsupported runtime version %08X", f4se->runtimeVersion); + return false; + } + // supported runtime version + + logger.error("Query complete\n"); + return true; + } + + bool F4SEPlugin_Load(const F4SEInterface * f4se) + { + logger.error("CBP Loading\n"); + + g_task = (F4SETaskInterface *)f4se->QueryInterface(kInterface_Task); + if (!g_task) + { + logger.error("Couldn't get Task interface\n"); + return false; + } + + // Load initial config before the hook. + logger.error("Loading Config\n"); + loadConfig(); + //g_messagingInterface->RegisterListener(0, "F4SE", MessageHandler); + logger.error("Hooking Game\n"); + DoHook(); + logger.error("CBP Load Complete\n"); + return true; + } }; BOOL WINAPI DllMain( - _In_ HINSTANCE hinstDLL, - _In_ DWORD fdwReason, - _In_ LPVOID lpvReserved + _In_ HINSTANCE hinstDLL, + _In_ DWORD fdwReason, + _In_ LPVOID lpvReserved ) { - return true; + return true; } \ No newline at end of file diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 9200251..1697ecc 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -45,56 +45,56 @@ extern F4SETaskInterface *g_task; //void UpdateWorldDataToChild(NiAVObject) void dumpTransform(NiTransform t) { - Console_Print("%8.2f %8.2f %8.2f", t.rot.data[0][0], t.rot.data[0][1], t.rot.data[0][2]); - Console_Print("%8.2f %8.2f %8.2f", t.rot.data[1][0], t.rot.data[1][1], t.rot.data[1][2]); - Console_Print("%8.2f %8.2f %8.2f", t.rot.data[2][0], t.rot.data[2][1], t.rot.data[2][2]); + Console_Print("%8.2f %8.2f %8.2f", t.rot.data[0][0], t.rot.data[0][1], t.rot.data[0][2]); + Console_Print("%8.2f %8.2f %8.2f", t.rot.data[1][0], t.rot.data[1][1], t.rot.data[1][2]); + Console_Print("%8.2f %8.2f %8.2f", t.rot.data[2][0], t.rot.data[2][1], t.rot.data[2][2]); - Console_Print("%8.2f %8.2f %8.2f", t.pos.x, t.pos.y, t.pos.z); - Console_Print("%8.2f", t.scale); + Console_Print("%8.2f %8.2f %8.2f", t.pos.x, t.pos.y, t.pos.z); + Console_Print("%8.2f", t.scale); } bool visitObjects(NiAVObject *parent, std::function functor, int depth = 0) { - if (!parent) return false; - NiNode * node = parent->GetAsNiNode(); - if (node) { - if (functor(parent, depth)) - return true; - - for (UInt32 i = 0; i < node->m_children.m_emptyRunStart; i++) { - NiAVObject * object = node->m_children.m_data[i]; - if (object) { - if (visitObjects(object, functor, depth+1)) - return true; - } - } - } - else if (functor(parent, depth)) - return true; - - return false; + if (!parent) return false; + NiNode * node = parent->GetAsNiNode(); + if (node) { + if (functor(parent, depth)) + return true; + + for (UInt32 i = 0; i < node->m_children.m_emptyRunStart; i++) { + NiAVObject * object = node->m_children.m_data[i]; + if (object) { + if (visitObjects(object, functor, depth+1)) + return true; + } + } + } + else if (functor(parent, depth)) + return true; + + return false; } std::string spaces(int n) { - auto s = std::string(n , ' '); - return s; + auto s = std::string(n , ' '); + return s; } bool printStuff(NiAVObject *avObj, int depth) { - std::string sss = spaces(depth); - const char *ss = sss.c_str(); - //logger.info("%savObj Name = %s, RTTI = %s\n", ss, avObj->m_name, avObj->GetRTTI()->name); - - //NiNode *node = avObj->GetAsNiNode(); - //if (node) { - // logger.info("%snode %s, RTTI %s\n", ss, node->m_name, node->GetRTTI()->name); - //} - //return false; + std::string sss = spaces(depth); + const char *ss = sss.c_str(); + //logger.info("%savObj Name = %s, RTTI = %s\n", ss, avObj->m_name, avObj->GetRTTI()->name); + + //NiNode *node = avObj->GetAsNiNode(); + //if (node) { + // logger.info("%snode %s, RTTI %s\n", ss, node->m_name, node->GetRTTI()->name); + //} + //return false; } void dumpVec(NiPoint3 p) { - logger.info("%8.2f %8.2f %8.2f\n", p.x, p.y, p.z); + logger.info("%8.2f %8.2f %8.2f\n", p.x, p.y, p.z); } @@ -102,10 +102,10 @@ void dumpVec(NiPoint3 p) { template inline void safe_delete(T*& in) { - if (in) { - delete in; - in = NULL; - } + if (in) { + delete in; + in = NULL; + } } @@ -116,117 +116,124 @@ TESObjectCELL *curCell = nullptr; struct ActorEntry { - UInt32 id; - Actor *actor; + UInt32 id; + Actor *actor; }; void updateActors() { - //LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; - //LARGE_INTEGER frequency; - - //QueryPerformanceFrequency(&frequency); - //QueryPerformanceCounter(&startingTime); - - // We scan the cell and build the list every time - only look up things by ID once - // we retain all state by actor ID, in a map - it's cleared on cell change - std::vector actorEntries; - - //logger.error("scan Cell\n"); - auto player = DYNAMIC_CAST(LookupFormByID(0x14), TESForm, Actor); - if (!player || !player->unkF0) goto FAILED; - - auto cell = player->parentCell; - if (!cell) goto FAILED; - - if (cell != curCell) { - logger.error("cell change %d\n", cell); - curCell = cell; - actors.clear(); - } else { - // Attempt to get cell's objects - for (int i = 0; i < cell->objectList.count; i++) { - auto ref = cell->objectList[i]; - if (ref) { - // Attempt to get actors - auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); - if (actor && actor->unkF0) { - auto soIt = actors.find(actor->formID); - if (soIt == actors.end() && actor->formID == 0x14) { - logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); - auto obj = SimObj(actor, config); - if (obj.actorValid(actor)) { - actors.emplace(actor->formID, obj); - actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); - } - } else if (soIt->second.actorValid(actor)) { - actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); - } - } - } - } - } - - //static bool done = false; - //if (!done && player->loadedState->node) { - // visitObjects(player->loadedState->node, printStuff); - // BSFixedString cs("UUNP"); - // auto bodyAV = player->loadedState->node->GetObjectByName(&cs.data); - // BSTriShape *body = bodyAV->GetAsBSTriShape(); - // logger.info("GetAsBSTriShape returned %lld\n", body); - // auto geometryData = body->geometryData; - // //logger.info("Num verts = %d\n", geometryData->m_usVertices); - - - // done = true; - //} - - // Reload config - static int count = 0; - if (configReloadCount && count++ > configReloadCount) { - count = 0; - loadConfig(); - for (auto &a : actors) { - a.second.updateConfig(config); - } - } - - //logger.error("Updating %d entities\n", actorEntries.size()); - for (auto &a : actorEntries) { - auto objIterator = actors.find(a.id); - if (objIterator == actors.end()) { - //logger.error("Sim Object not found in tracked actors\n"); - } else { - auto &obj = objIterator->second; - if (obj.isBound()) { - obj.update(a.actor); - } else { - obj.bind(a.actor, femaleBones, config); - } - } - } + //LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; + //LARGE_INTEGER frequency; + + //QueryPerformanceFrequency(&frequency); + //QueryPerformanceCounter(&startingTime); + + // We scan the cell and build the list every time - only look up things by ID once + // we retain all state by actor ID, in a map - it's cleared on cell change + std::vector actorEntries; + + //logger.error("scan Cell\n"); + auto player = DYNAMIC_CAST(LookupFormByID(0x14), TESForm, Actor); + if (!player || !player->unkF0) goto FAILED; + + auto cell = player->parentCell; + if (!cell) goto FAILED; + + if (cell != curCell) { + logger.error("cell change %d\n", cell); + curCell = cell; + actors.clear(); + } else { + // Attempt to get cell's objects + for (int i = 0; i < cell->objectList.count; i++) { + auto ref = cell->objectList[i]; + if (ref) { + // Attempt to get actors + auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); + if (actor && actor->unkF0) { + auto soIt = actors.find(actor->formID); + if ((!playerOnly || (actor->formID == 0x14 && playerOnly)) && + (!maleOnly || (IsActorMale(actor) && maleOnly)) && + (!femaleOnly || (!IsActorMale(actor) && femaleOnly))) { // design needs to be changed to allow config changes on the fly + if (soIt == actors.end()) { + logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); + //logger.info("%s\n", actor->unk08-> & 1); + //logger.info("%s\n", actor->race->textureModel[1].GetModelName()); + auto obj = SimObj(actor, config, boneNames.size()); + if (obj.actorValid(actor)) { + actors.emplace(actor->formID, obj); + actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); + } + } + else if (soIt->second.actorValid(actor)) { + actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); + } + } + } + } + } + } + + //static bool done = false; + //if (!done && player->loadedState->node) { + // visitObjects(player->loadedState->node, printStuff); + // BSFixedString cs("UUNP"); + // auto bodyAV = player->loadedState->node->GetObjectByName(&cs.data); + // BSTriShape *body = bodyAV->GetAsBSTriShape(); + // logger.info("GetAsBSTriShape returned %lld\n", body); + // auto geometryData = body->geometryData; + // //logger.info("Num verts = %d\n", geometryData->m_usVertices); + + + // done = true; + //} + + // Reload config + static int count = 0; + if (configReloadCount && count++ > configReloadCount) { + count = 0; + loadConfig(); + for (auto &a : actors) { + a.second.updateConfig(config); + } + } + + //logger.error("Updating %d entities\n", actorEntries.size()); + for (auto &a : actorEntries) { + auto objIterator = actors.find(a.id); + if (objIterator == actors.end()) { + //logger.error("Sim Object not found in tracked actors\n"); + } else { + auto &obj = objIterator->second; + if (obj.isBound()) { + obj.update(a.actor); + } else { + obj.bind(a.actor, boneNames, config); + } + } + } FAILED: - return; - //QueryPerformanceCounter(&endingTime); - //elapsedMicroseconds.QuadPart = endingTime.QuadPart - startingTime.QuadPart; - //elapsedMicroseconds.QuadPart *= 1000000000LL; - //elapsedMicroseconds.QuadPart /= frequency.QuadPart; - //logger.info("Update Time = %lld ns\n", elapsedMicroseconds.QuadPart); + return; + //QueryPerformanceCounter(&endingTime); + //elapsedMicroseconds.QuadPart = endingTime.QuadPart - startingTime.QuadPart; + //elapsedMicroseconds.QuadPart *= 1000000000LL; + //elapsedMicroseconds.QuadPart /= frequency.QuadPart; + //logger.info("Update Time = %lld ns\n", elapsedMicroseconds.QuadPart); } class ScanDelegate : public ITaskDelegate { public: - virtual void Run() { - updateActors(); - } - virtual void Dispose() { - delete this; - } + virtual void Run() { + updateActors(); + } + virtual void Dispose() { + delete this; + } }; void scaleTest() { - g_task->AddTask(new ScanDelegate()); - return; + g_task->AddTask(new ScanDelegate()); + return; } \ No newline at end of file From 4b55028e0365d0cfecfb4c07bc3d2c15b607135e Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sat, 1 Feb 2020 02:27:55 -0800 Subject: [PATCH 12/66] Configuration is now through an ini file. Cleanup Thing object. Added armor detection and bone override settings. --- CBPSSE/ActorEntry.cpp | 17 ++ CBPSSE/ActorEntry.h | 11 + CBPSSE/CBPSSE.vcxproj | 5 + CBPSSE/CBPSSE.vcxproj.filters | 15 ++ CBPSSE/INIReader.h | 461 ++++++++++++++++++++++++++++++++++ CBPSSE/SimObj.cpp | 11 +- CBPSSE/SimObj.h | 5 +- CBPSSE/Thing.cpp | 97 +++---- CBPSSE/Thing.h | 10 +- CBPSSE/config.cpp | 146 +++++++---- CBPSSE/config.h | 2 + CBPSSE/scan.cpp | 44 ++-- 12 files changed, 695 insertions(+), 129 deletions(-) create mode 100644 CBPSSE/ActorEntry.cpp create mode 100644 CBPSSE/ActorEntry.h create mode 100644 CBPSSE/INIReader.h diff --git a/CBPSSE/ActorEntry.cpp b/CBPSSE/ActorEntry.cpp new file mode 100644 index 0000000..b651f82 --- /dev/null +++ b/CBPSSE/ActorEntry.cpp @@ -0,0 +1,17 @@ +#include "ActorEntry.h" +#include "f4se/GameExtraData.h" + +bool ActorEntry::isInPowerArmor() { + if (!this->actor) + return false; + return !this->actor->extraDataList->HasType(kExtraData_PowerArmor); +} + +bool ActorEntry::IsTorsoArmorEquipped() { + bool isEquipped = false; + if (!this->actor) + return false; + // 11 IS ARMOR TORSO SLOT (41 minus 30??) + isEquipped = this->actor->equipData->slots[11].item; + return isEquipped; +} \ No newline at end of file diff --git a/CBPSSE/ActorEntry.h b/CBPSSE/ActorEntry.h new file mode 100644 index 0000000..8a68708 --- /dev/null +++ b/CBPSSE/ActorEntry.h @@ -0,0 +1,11 @@ +#pragma once +#include "f4se/GameReferences.h" + +class ActorEntry { + public: + UInt32 id; + Actor* actor; + + bool isInPowerArmor(); + bool IsTorsoArmorEquipped(); +}; \ No newline at end of file diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index a57b394..78ac553 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -155,12 +155,15 @@ + + + @@ -169,12 +172,14 @@ + + diff --git a/CBPSSE/CBPSSE.vcxproj.filters b/CBPSSE/CBPSSE.vcxproj.filters index 567fa75..82ac052 100644 --- a/CBPSSE/CBPSSE.vcxproj.filters +++ b/CBPSSE/CBPSSE.vcxproj.filters @@ -60,6 +60,15 @@ Header Files\api + + Header Files\api + + + Header Files + + + Header Files + @@ -107,6 +116,12 @@ Source Files\api + + Source Files\api + + + Source Files + diff --git a/CBPSSE/INIReader.h b/CBPSSE/INIReader.h new file mode 100644 index 0000000..c5c8547 --- /dev/null +++ b/CBPSSE/INIReader.h @@ -0,0 +1,461 @@ +// Read an INI file into easy-to-access name/value pairs. + +// inih and INIReader are released under the New BSD license (see LICENSE.txt). +// Go to the project home page for more info: +// +// https://github.com/benhoyt/inih +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include +/* Typedef for prototype of handler function. */ +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value); + +/* Typedef for prototype of fgets-style reader function. */ +typedef char* (*ini_reader)(char* str, int num, void* stream); + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(const char* filename, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O. */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +/* Maximum line length for any line in INI file. */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +#ifdef __cplusplus +} +#endif + +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 80 +#define MAX_NAME 80 + +/* Strip whitespace chars off end of given string, in place. Return s. */ +inline static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +inline static char* lskip(const char* s) +{ + while (*s && isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to null at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +inline static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +inline static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy_s(dest, size, src, size); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +inline int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; +#else + char* line; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_MAX_LINE); + if (!line) { + return -2; + } +#endif + + /* Scan through stream line by line */ + while (reader(line, INI_MAX_LINE, stream) != NULL) { + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python configparser, allow both ; and # comments at the + start of a line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(start, NULL); + if (*end) + *end = '\0'; + rstrip(start); +#endif + + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!handler(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + + if (!handler(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +inline int ini_parse_file(FILE* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)fgets, file, handler, user); +} + +/* See documentation in header file. */ +inline int ini_parse(const char* filename, ini_handler handler, void* user) +{ + FILE* file; + int error; + + fopen_s(&file, filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} + +#endif /* __INI_H__ */ + + +#ifndef __INIREADER_H__ +#define __INIREADER_H__ + +#include +#include +#include + +// Read an INI file into easy-to-access name/value pairs. (Note that I've gone +// for simplicity here rather than speed, but it should be pretty decent.) +class INIReader +{ +public: + // Empty Constructor + INIReader() {}; + + // Construct INIReader and parse given filename. See ini.h for more info + // about the parsing. + INIReader(std::string filename); + + // Construct INIReader and parse given file. See ini.h for more info + // about the parsing. + INIReader(FILE *file); + + // Return the result of ini_parse(), i.e., 0 on success, line number of + // first error on parse error, or -1 on file open error. + int ParseError() const; + + // Return the list of sections found in ini file + const std::set& Sections() const; + + const std::unordered_map & Section(std::string section) const; + + // Get a string value from INI file, returning default_value if not found. + std::string Get(std::string section, std::string name, + std::string default_value) const; + + // Get an integer (long) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + long GetInteger(std::string section, std::string name, long default_value) const; + + // Get a real (floating point double) value from INI file, returning + // default_value if not found or not a valid floating point value + // according to strtod(). + double GetReal(std::string section, std::string name, double default_value) const; + + // Get a single precision floating point number value from INI file, returning + // default_value if not found or not a valid floating point value + // according to strtof(). + float GetFloat(std::string section, std::string name, float default_value) const; + + // Get a boolean value from INI file, returning default_value if not found or if + // not a valid true/false value. Valid true values are "true", "yes", "on", "1", + // and valid false values are "false", "no", "off", "0" (not case sensitive). + bool GetBoolean(std::string section, std::string name, bool default_value) const; + +protected: + int _error; + std::unordered_map> _values; + std::set _sections; + static int ValueHandler(void* user, const char* section, const char* name, + const char* value); +}; + +#endif // __INIREADER_H__ + + +#ifndef __INIREADER__ +#define __INIREADER__ + +#include +#include +#include + +inline INIReader::INIReader(std::string filename) +{ + _error = ini_parse(filename.c_str(), ValueHandler, this); +} + +inline INIReader::INIReader(FILE *file) +{ + _error = ini_parse_file(file, ValueHandler, this); +} + +inline int INIReader::ParseError() const +{ + return _error; +} + +inline const std::set& INIReader::Sections() const +{ + return _sections; +} + +inline const std::unordered_map& INIReader::Section(std::string section) const +{ + return _values.at(section); +} + +inline std::string INIReader::Get(std::string section, std::string name, std::string default_value) const +{ + try { + return _values.at(section).count(name) ? _values.at(section).at(name) : default_value; + } + catch (...) { + return default_value; + } +} + +inline long INIReader::GetInteger(std::string section, std::string name, long default_value) const +{ + std::string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + long n = strtol(value, &end, 0); + return end > value ? n : default_value; +} + +inline double INIReader::GetReal(std::string section, std::string name, double default_value) const +{ + std::string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + double n = strtod(value, &end); + return end > value ? n : default_value; +} + +inline float INIReader::GetFloat(std::string section, std::string name, float default_value) const +{ + std::string valstr = Get(section, name, ""); + const char* value = valstr.c_str(); + char* end; + float n = strtof(value, &end); + return end > value ? n : default_value; +} + +inline bool INIReader::GetBoolean(std::string section, std::string name, bool default_value) const +{ + std::string valstr = Get(section, name, ""); + // Convert to lower case to make string comparisons case-insensitive + std::transform(valstr.begin(), valstr.end(), valstr.begin(), ::tolower); + if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") + return true; + else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") + return false; + else + return default_value; +} + +inline int INIReader::ValueHandler(void* user, const char* section, const char* name, + const char* value) +{ + INIReader* reader = (INIReader*)user; + reader->_values[section][name] = value; + reader->_sections.insert(section); + return 1; +} + +#endif // __INIREADER__ diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 1cffaf4..21c6238 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -10,9 +10,8 @@ const char *rightScrotumName_BT2 = "Penis_Balls_CBP_02"; std::vector boneNames; -SimObj::SimObj(Actor *actor, config_t &config, int num_things) { -// : things(num_things){ - +SimObj::SimObj(Actor *actor, config_t &config) + : things(4) { id = actor->formID; } @@ -37,7 +36,7 @@ bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &c if (!bone) { logger.info("Failed to find Bone %s for actor %08x\n", b, actor->formID); } else { - logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); + //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); things.emplace(b, Thing(bone, cs)); } } @@ -68,9 +67,7 @@ void SimObj::update(Actor *actor) { bool SimObj::updateConfig(config_t & config) { for (auto &thing : things) { - std::string &boneName = std::string(thing.first); - //logger.info("updateConfig in SimObj: %s, %s\n", boneName, thing.first); - thing.second.updateConfig(config[boneName]); + thing.second.updateConfig(config[std::string(thing.first)]); } return true; } diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index 0c2e97f..c8569ff 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -11,7 +11,7 @@ class SimObj { bool bound = false; public: std::unordered_map things; - SimObj(Actor *actor, config_t &config, int num_things); + SimObj(Actor *actor, config_t &config); SimObj() {} ~SimObj(); bool bind(Actor *actor, std::vector &boneNames, config_t &config); @@ -24,4 +24,5 @@ class SimObj { extern std::vector boneNames; -//extern std::unordered_map configMap; \ No newline at end of file +extern std::unordered_map configMap; +extern std::unordered_map configMapArmor; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 9bac70b..2bf3519 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -10,9 +10,23 @@ constexpr auto DEG_TO_RAD = 3.14159265 / 180; std::unordered_map origLocalPos; std::unordered_map origLocalRot; + +std::unordered_map::const_iterator origLocalPos_iter; +std::unordered_map::const_iterator origLocalRot_iter; + const char * skeletonNif_boneName = "skeleton.nif"; const char* COM_boneName = "COM"; +void Thing::showPos(NiPoint3& p) { + logger.info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); +} + +void Thing::showRot(NiMatrix43& r) { + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); + logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); +} + Thing::Thing(NiAVObject* obj, BSFixedString& name) : boneName(name) , velocity(NiPoint3(0, 0, 0)) @@ -20,22 +34,28 @@ Thing::Thing(NiAVObject* obj, BSFixedString& name) oldWorldPos = obj->m_worldTransform.pos; time = clock(); -} -Thing::~Thing() { -} + // Save the bones original local values if it already hasn't + origLocalPos_iter = origLocalPos.find(boneName.c_str()); + origLocalRot_iter = origLocalRot.find(boneName.c_str()); -void showPos(NiPoint3 &p) { - logger.info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); + if (origLocalPos_iter == origLocalPos.end()) { + logger.error("for bone %s: ", boneName.c_str()); + logger.error("firstRun pos Set: "); + origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); + showPos(obj->m_localTransform.pos); + } + if (origLocalRot_iter == origLocalRot.end()) { + logger.error("for bone %s: ", boneName.c_str()); + logger.error("firstRun rot Set:\n"); + origLocalRot.emplace(boneName.c_str(), obj->m_localTransform.rot); + showRot(obj->m_localTransform.rot); + } } -void showRot(NiMatrix43 &r) { - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); +Thing::~Thing() { } - float solveQuad(float a, float b, float c) { float k1 = (-b + sqrtf(b*b - 4*a*c)) / (2 * a); //float k2 = (-b - sqrtf(b*b - 4*a*c)) / (2 * a); @@ -43,9 +63,6 @@ float solveQuad(float a, float b, float c) { return k1; } - - - void Thing::updateConfig(configEntry_t & centry) { stiffness = centry["stiffness"]; stiffness2 = centry["stiffness2"]; @@ -60,9 +77,9 @@ void Thing::updateConfig(configEntry_t & centry) { rotationalX = centry["rotationalX"]; rotationalY = centry["rotationalY"]; rotationalZ = centry["rotationalZ"]; - rotationX = centry["rotationX"]; - rotationY = centry["rotationY"]; - rotationZ = centry["rotationZ"]; + rotateLinearX = centry["rotateLinearX"]; + rotateLinearY = centry["rotateLinearY"]; + rotateLinearZ = centry["rotateLinearZ"]; // Optional entries for backwards compatability if (centry.find("timeStep") != centry.end()) timeStep = centry["timeStep"]; @@ -78,16 +95,9 @@ void Thing::updateConfig(configEntry_t & centry) { timeTick = 1; //zOffset = solveQuad(stiffness2, stiffness, -gravityBias); - //logger.error("z offset = %f\n", solveQuad(stiffness2, stiffness, -gravityBias)); } -void Thing::dump() { - //showPos(obj->m_worldTransform.pos); - //showPos(obj->m_localTransform.pos); -} - - static float clamp(float val, float min, float max) { if (val < min) return min; else if (val > max) return max; @@ -104,6 +114,12 @@ template int sgn(T val) { } void Thing::update(Actor *actor) { + /*LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; + LARGE_INTEGER frequency; + + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&startingTime);*/ + auto newTime = clock(); auto deltaT = newTime - time; @@ -154,22 +170,6 @@ void Thing::update(Actor *actor) { } #endif - // TODO rewrite these for performance - // Save the bone's original local position if it already hasn't - if (origLocalPos.find(boneName.c_str()) == origLocalPos.end()) { - logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); - logger.error("firstRun pos Set: "); - origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); - showPos(obj->m_localTransform.pos); - } - // Save the bone's original local rotation if it already hasn't - if (origLocalRot.find(boneName.c_str()) == origLocalRot.end()) { - logger.error("for actor %08x, bone %s: ", actor->formID, boneName.c_str()); - logger.error("firstRun rot Set:\n"); - origLocalRot.emplace(boneName.c_str(), obj->m_localTransform.rot); - showRot(obj->m_localTransform.rot); - } - auto skeletonObj = obj; NiAVObject * comObj; bool skeletonFound = false; @@ -303,17 +303,17 @@ void Thing::update(Actor *actor) { if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { NiMatrix43 standardRot; - standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, - rotationY * DEG_TO_RAD, - rotationZ * DEG_TO_RAD); + standardRot.SetEulerAngles(rotateLinearX * DEG_TO_RAD, + rotateLinearY * DEG_TO_RAD, + rotateLinearZ * DEG_TO_RAD); invRot = standardRot * obj->m_parent->m_worldTransform.rot; } else { //invRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose(); NiMatrix43 standardRot; - standardRot.SetEulerAngles(rotationX * DEG_TO_RAD, - rotationY * DEG_TO_RAD, - rotationZ * DEG_TO_RAD); + standardRot.SetEulerAngles(rotateLinearX * DEG_TO_RAD, + rotateLinearY * DEG_TO_RAD, + rotateLinearZ * DEG_TO_RAD); invRot = standardRot * obj->m_parent->m_worldTransform.rot; } @@ -370,6 +370,11 @@ void Thing::update(Actor *actor) { logger.error("end update()\n"); #endif - + //logger.error("end update()\n"); + /*QueryPerformanceCounter(&endingTime); + elapsedMicroseconds.QuadPart = endingTime.QuadPart - startingTime.QuadPart; + elapsedMicroseconds.QuadPart *= 1000000000LL; + elapsedMicroseconds.QuadPart /= frequency.QuadPart; + _MESSAGE("Thing.update() Update Time = %lld ns\n", elapsedMicroseconds.QuadPart);*/ } \ No newline at end of file diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index a52f96b..348aad0 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -32,9 +32,9 @@ class Thing { float rotationalY = 0.1; float rotationalZ = 0.1; - float rotationX = 0.0; - float rotationY = 0.0; - float rotationZ = 0.0; + float rotateLinearX = 0.0; + float rotateLinearY = 0.0; + float rotateLinearZ = 0.0; float timeStep = 1.0f; bool fusionGirlEnabled; @@ -43,9 +43,9 @@ class Thing { ~Thing(); void updateConfig(configEntry_t ¢ry); - void dump(); - void update(Actor *actor); void reset(); + void showPos(NiPoint3& p); + void showRot(NiMatrix43& r); }; \ No newline at end of file diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index aed9535..354e906 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -1,77 +1,131 @@ -#include "Thing.h" +#include "config.h" +#include "INIReader.h" #include "log.h" #include "SimObj.h" -#include -#include -#include "config.h" +#include "Thing.h" + +#include #include +#include +#include #include "f4se/GameObjects.h" #include "f4se/GameRTTI.h" #include "f4se_common/Utilities.h" +#define DEBUG 0 #pragma warning(disable : 4996) int configReloadCount = 60; bool playerOnly = false; bool femaleOnly = false; bool maleOnly = false; +bool detectArmor = false; config_t config; - - -//const char* leftBreastName_FG = "Breast_CBP_L_02"; -//const char* rightBreastName_FG = "Breast_CBP_R_02"; -//const char* leftButtName_FG = "Butt_CBP_L_01"; -//const char* rightButtName_FG = "Butt_CBP_R_01"; -// -//const char* leftBreastName_CBBE = "LBreast_skin"; -//const char* rightBreastName_CBBE = "RBreast_skin"; -//const char* leftButtName_CBBE = "LButtFat_skin"; -//const char* rightButtName_CBBE = "RButtFat_skin"; +config_t configArmor; +configOverrides_t configOverrides; void loadConfig() { - char buffer[1024]; + logger.info("loadConfig\n"); + boneNames.clear(); std::set bonesSet; - //logger.info("loadConfig\n"); - FILE *fh = fopen("Data\\F4SE\\Plugins\\CBPConfig.txt", "r"); - if (!fh) { - logger.error("Failed to open config file CBPConfig.txt\n"); - //Console_Print("Failed to open config file CBPConfig.txt"); - configReloadCount = 0; - return; - } - //Console_Print("Reading CBP Config"); - // Rewrite this eventually config.clear(); - do { - auto str = fgets(buffer, 1023, fh); - //logger.error("str %s\n", str); - if (str && strlen(str) > 1) { - if (str[0] != '#') { - char *tok0 = strtok(str, "."); - char *tok1 = strtok(NULL, " "); - char *tok2 = strtok(NULL, " "); - - if (tok0 && tok1 && tok2) { - config[std::string(tok0)][std::string(tok1)] = atof(tok2); + + // Note: Using INIReader results in a slight double read + INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); + if (configReader.ParseError() < 0) { + logger.error("Can't load 'ocbp.ini'\n"); + } + logger.error("Reading CBP Config\n"); + + // Read general settings + playerOnly = configReader.GetBoolean("General", "playerOnly", false); + femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); + maleOnly = configReader.GetBoolean("General", "maleOnly", false); + detectArmor = configReader.GetBoolean("General", "detectArmor", false); + configReloadCount = configReader.GetInteger("Tuning", "rate", 0); + + // Read sections + auto sections = configReader.Sections(); + for (auto sectionIt = sections.begin(); sectionIt != sections.end(); ++sectionIt) { + + // Split for override section check + auto overrideStr = std::string("Override:"); + auto splitStr = std::mismatch(overrideStr.begin(), overrideStr.end(), sectionIt->begin()); + + if (*sectionIt == std::string("Attach")) { + // Get section contents + auto sectionMap = configReader.Section(*sectionIt); + for (auto& valuesIter : sectionMap) { + auto& boneName = valuesIter.first; + auto& attachName = valuesIter.second; + boneNames.push_back(boneName); + // Find specified bone section and insert map values into config + if (sections.find(attachName) != sections.end()) { + auto attachMapSection = configReader.Section(attachName); + for (auto& attachIter : attachMapSection) { + auto& keyName = attachIter.first; + config[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); + } } - if (std::string(tok0) != "Tuning" && std::string(tok0) != "General") { - boneNames.push_back(std::string(tok0)); + } + } + else if (*sectionIt == std::string("Attach.A") && detectArmor) { + // Get section contents + auto sectionMap = configReader.Section(*sectionIt); + for (auto &valuesIter : sectionMap) { + auto &boneName = valuesIter.first; + auto &attachName = valuesIter.second; + boneNames.push_back(boneName); + // Find specified bone section and insert map values into configArmor + if (sections.find(attachName) != sections.end()) { + auto attachMapSection = configReader.Section(attachName); + for (auto &attachIter : attachMapSection) { + auto& keyName = attachIter.first; + configArmor[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); + } } } } - } while (!feof(fh)); - fclose(fh); + else if (splitStr.first == overrideStr.end()) { + // If section name is prefixed with "Override:", grab other half of name for bone + auto boneName = std::string(splitStr.second, sectionIt->end()); + + // Get section contents + auto sectionMap = configReader.Section(*sectionIt); + for (auto &valuesIt : sectionMap) { + configOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionIt, valuesIt.first, 0.0); + } + } + } + + // replace configs with override settings (if any) + for (auto &boneIter : configOverrides) { + if (config.count(boneIter.first) > 0) { + for (auto settingIter : boneIter.second) { + config[boneIter.first][settingIter.first] = settingIter.second; + } + } + } + + // Remove duplicate entries bonesSet = std::set(boneNames.begin(), boneNames.end()); boneNames.assign(bonesSet.begin(), bonesSet.end()); - playerOnly = config["General"]["playerOnly"] == 1; - femaleOnly = config["General"]["femaleOnly"] == 1; - maleOnly = config["General"]["maleOnly"] == 1; - configReloadCount = config["Tuning"]["rate"]; - + logger.error("Finished CBP Config\n"); +} + +void dumpConfigtoLog() +{ + // Log contents of config + for (auto section : config) { + logger.info("[%s]\n", section.first.c_str()); + for (auto setting : section.second) { + logger.info("%s=%f\n", setting.first.c_str(), setting.second); + } + } } bool IsActorMale(Actor* actor) diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 121266f..cdeb91c 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -1,11 +1,13 @@ #pragma once #include +#include "f4se/GameReferences.h" class Configuration { }; typedef std::unordered_map configEntry_t; typedef std::unordered_map config_t; +typedef std::unordered_map configOverrides_t; bool IsActorMale(Actor* actor); diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 1697ecc..34de728 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -1,5 +1,5 @@ #pragma once - +#define DEBUG #include #include @@ -22,6 +22,7 @@ #include #include +#include "ActorEntry.h" #include "log.h" #include "Thing.h" #include "config.h" @@ -35,7 +36,7 @@ #include "f4se/GameThreads.h" #include "f4se/PluginAPI.h" #include "f4se/GameStreams.h" - +#include "f4se/GameExtraData.h" #pragma warning(disable : 4996) @@ -115,11 +116,6 @@ std::unordered_map actors; TESObjectCELL *curCell = nullptr; -struct ActorEntry { - UInt32 id; - Actor *actor; -}; - void updateActors() { //LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; //LARGE_INTEGER frequency; @@ -151,23 +147,19 @@ void updateActors() { auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); if (actor && actor->unkF0) { auto soIt = actors.find(actor->formID); - if ((!playerOnly || (actor->formID == 0x14 && playerOnly)) && - (!maleOnly || (IsActorMale(actor) && maleOnly)) && - (!femaleOnly || (!IsActorMale(actor) && femaleOnly))) { // design needs to be changed to allow config changes on the fly - if (soIt == actors.end()) { - logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); - //logger.info("%s\n", actor->unk08-> & 1); - //logger.info("%s\n", actor->race->textureModel[1].GetModelName()); - auto obj = SimObj(actor, config, boneNames.size()); - if (obj.actorValid(actor)) { - actors.emplace(actor->formID, obj); - actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); - } - } - else if (soIt->second.actorValid(actor)) { + if (soIt == actors.end()) { + logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); + //logger.info("%s\n", actor->unk08-> & 1); + //logger.info("%s\n", actor->race->textureModel[1].GetModelName()); + auto obj = SimObj(actor, config); + if (obj.actorValid(actor)) { + actors.emplace(actor->formID, obj); actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } + else if (soIt->second.actorValid(actor)) { + actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); + } } } } @@ -202,9 +194,15 @@ void updateActors() { auto objIterator = actors.find(a.id); if (objIterator == actors.end()) { //logger.error("Sim Object not found in tracked actors\n"); - } else { + } + else { auto &obj = objIterator->second; - if (obj.isBound()) { + if (obj.isBound() && + a.isInPowerArmor() && + (!playerOnly || (a.actor->formID == 0x14 && playerOnly)) && + (!maleOnly || (IsActorMale(a.actor) && maleOnly)) && + (!femaleOnly || (!IsActorMale(a.actor) && femaleOnly)) + ) { obj.update(a.actor); } else { obj.bind(a.actor, boneNames, config); From 1a1cf816af65d70a19da361012487d65ae254507 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sat, 1 Feb 2020 02:41:04 -0800 Subject: [PATCH 13/66] Cleanup, cleanup --- CBPSSE/ActorEntry.cpp | 16 +++++++++++++++- CBPSSE/ActorEntry.h | 3 ++- CBPSSE/SimObj.cpp | 5 ----- CBPSSE/SimObj.h | 1 - CBPSSE/Thing.cpp | 1 + CBPSSE/config.cpp | 12 ------------ CBPSSE/config.h | 3 +-- CBPSSE/main.cpp | 3 --- CBPSSE/scan.cpp | 16 +++++++++++----- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/CBPSSE/ActorEntry.cpp b/CBPSSE/ActorEntry.cpp index b651f82..68fe40f 100644 --- a/CBPSSE/ActorEntry.cpp +++ b/CBPSSE/ActorEntry.cpp @@ -1,7 +1,21 @@ #include "ActorEntry.h" #include "f4se/GameExtraData.h" +#include "f4se/GameObjects.h" +#include "f4se/GameRTTI.h" -bool ActorEntry::isInPowerArmor() { +bool ActorEntry::IsMale() +{ + TESNPC* actorNPC = DYNAMIC_CAST(this->actor->baseForm, TESForm, TESNPC); + + auto npcSex = actorNPC ? CALL_MEMBER_FN(actorNPC, GetSex)() : 1; + + if (npcSex == 0) //Actor is male + return true; + else + return false; +} + +bool ActorEntry::IsInPowerArmor() { if (!this->actor) return false; return !this->actor->extraDataList->HasType(kExtraData_PowerArmor); diff --git a/CBPSSE/ActorEntry.h b/CBPSSE/ActorEntry.h index 8a68708..3a646a0 100644 --- a/CBPSSE/ActorEntry.h +++ b/CBPSSE/ActorEntry.h @@ -6,6 +6,7 @@ class ActorEntry { UInt32 id; Actor* actor; - bool isInPowerArmor(); + bool IsInPowerArmor(); bool IsTorsoArmorEquipped(); + bool IsMale(); }; \ No newline at end of file diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 21c6238..7ebc198 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -5,9 +5,6 @@ #include "log.h" // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory -const char *leftScrotumName_BT2 = "Penis_Balls_CBP_01"; -const char *rightScrotumName_BT2 = "Penis_Balls_CBP_02"; - std::vector boneNames; SimObj::SimObj(Actor *actor, config_t &config) @@ -23,7 +20,6 @@ bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &c { // logger.error("bind\n"); - auto loadedData = actor->unkF0; if (loadedData && loadedData->rootNode) { bound = true; @@ -54,7 +50,6 @@ bool SimObj::actorValid(Actor *actor) { return false; } - void SimObj::update(Actor *actor) { if (!bound) return; diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index c8569ff..8b943f7 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -22,7 +22,6 @@ class SimObj { }; - extern std::vector boneNames; extern std::unordered_map configMap; extern std::unordered_map configMapArmor; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 2bf3519..06a6b90 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -5,6 +5,7 @@ constexpr auto DEG_TO_RAD = 3.14159265 / 180; +// TODO Make these logger macros #define DEBUG 0 #define TRANSFORM_DEBUG 0 diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 354e906..de4ae28 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -126,16 +126,4 @@ void dumpConfigtoLog() logger.info("%s=%f\n", setting.first.c_str(), setting.second); } } -} - -bool IsActorMale(Actor* actor) -{ - TESNPC* actorNPC = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); - - auto npcSex = actorNPC ? CALL_MEMBER_FN(actorNPC, GetSex)() : 1; - - if (npcSex == 0) //Actor is male - return true; - else - return false; } \ No newline at end of file diff --git a/CBPSSE/config.h b/CBPSSE/config.h index cdeb91c..507e191 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -9,12 +9,11 @@ typedef std::unordered_map configEntry_t; typedef std::unordered_map config_t; typedef std::unordered_map configOverrides_t; -bool IsActorMale(Actor* actor); - extern bool playerOnly; extern bool femaleOnly; extern bool maleOnly; extern int configReloadCount; extern config_t config; +extern config_t configArmor; void loadConfig(); \ No newline at end of file diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index eea9298..ccd9409 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -141,9 +141,6 @@ extern "C" logger.error("CBP Load Complete\n"); return true; } - - - }; BOOL WINAPI DllMain( diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 34de728..f2df5ca 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -198,14 +198,20 @@ void updateActors() { else { auto &obj = objIterator->second; if (obj.isBound() && - a.isInPowerArmor() && + a.IsInPowerArmor() && (!playerOnly || (a.actor->formID == 0x14 && playerOnly)) && - (!maleOnly || (IsActorMale(a.actor) && maleOnly)) && - (!femaleOnly || (!IsActorMale(a.actor) && femaleOnly)) + (!maleOnly || (a.IsMale() && maleOnly)) && + (!femaleOnly || (!a.IsMale() && femaleOnly)) ) { obj.update(a.actor); - } else { - obj.bind(a.actor, boneNames, config); + } + else { + if (a.IsTorsoArmorEquipped()) { + obj.bind(a.actor, boneNames, configArmor); + } + else { + obj.bind(a.actor, boneNames, config); + } } } } From 0df733c301ffb40c177633f649a0e62892ea5208 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sun, 2 Feb 2020 19:09:55 -0800 Subject: [PATCH 14/66] Mostly refactoring. Moved actor checks to tracking code. --- CBPSSE/ActorEntry.h | 4 ---- CBPSSE/{ActorEntry.cpp => ActorUtils.cpp} | 18 +++++++-------- CBPSSE/ActorUtils.h | 9 ++++++++ CBPSSE/CBPSSE.vcxproj | 3 ++- CBPSSE/CBPSSE.vcxproj.filters | 5 +++- CBPSSE/Thing.cpp | 14 +++++------- CBPSSE/Thing.h | 25 ++++++++++---------- CBPSSE/config.cpp | 13 +++++++++-- CBPSSE/config.h | 2 +- CBPSSE/log.cpp | 9 ++++++++ CBPSSE/scan.cpp | 28 +++++++++++++++-------- 11 files changed, 81 insertions(+), 49 deletions(-) rename CBPSSE/{ActorEntry.cpp => ActorUtils.cpp} (50%) create mode 100644 CBPSSE/ActorUtils.h diff --git a/CBPSSE/ActorEntry.h b/CBPSSE/ActorEntry.h index 3a646a0..b75963d 100644 --- a/CBPSSE/ActorEntry.h +++ b/CBPSSE/ActorEntry.h @@ -5,8 +5,4 @@ class ActorEntry { public: UInt32 id; Actor* actor; - - bool IsInPowerArmor(); - bool IsTorsoArmorEquipped(); - bool IsMale(); }; \ No newline at end of file diff --git a/CBPSSE/ActorEntry.cpp b/CBPSSE/ActorUtils.cpp similarity index 50% rename from CBPSSE/ActorEntry.cpp rename to CBPSSE/ActorUtils.cpp index 68fe40f..6535951 100644 --- a/CBPSSE/ActorEntry.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -1,11 +1,11 @@ -#include "ActorEntry.h" +#include "ActorUtils.h" #include "f4se/GameExtraData.h" #include "f4se/GameObjects.h" #include "f4se/GameRTTI.h" -bool ActorEntry::IsMale() +bool actorUtils::IsActorMale(Actor *actor) { - TESNPC* actorNPC = DYNAMIC_CAST(this->actor->baseForm, TESForm, TESNPC); + TESNPC* actorNPC = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); auto npcSex = actorNPC ? CALL_MEMBER_FN(actorNPC, GetSex)() : 1; @@ -15,17 +15,17 @@ bool ActorEntry::IsMale() return false; } -bool ActorEntry::IsInPowerArmor() { - if (!this->actor) +bool actorUtils::IsActorInPowerArmor(Actor* actor) { + if (!actor) return false; - return !this->actor->extraDataList->HasType(kExtraData_PowerArmor); + return !actor->extraDataList->HasType(kExtraData_PowerArmor); } -bool ActorEntry::IsTorsoArmorEquipped() { +bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { bool isEquipped = false; - if (!this->actor) + if (!actor) return false; // 11 IS ARMOR TORSO SLOT (41 minus 30??) - isEquipped = this->actor->equipData->slots[11].item; + isEquipped = actor->equipData->slots[11].item; return isEquipped; } \ No newline at end of file diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h new file mode 100644 index 0000000..c2dfdef --- /dev/null +++ b/CBPSSE/ActorUtils.h @@ -0,0 +1,9 @@ +#pragma once +#include "f4se/GameReferences.h" + +namespace actorUtils { + // The statics needs to not be here + bool IsActorInPowerArmor(Actor* actor); + bool IsActorTorsoArmorEquipped(Actor* actor); + bool IsActorMale(Actor* actor); +} \ No newline at end of file diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index 78ac553..c2acf20 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -163,6 +163,7 @@ + @@ -179,7 +180,7 @@ - + diff --git a/CBPSSE/CBPSSE.vcxproj.filters b/CBPSSE/CBPSSE.vcxproj.filters index 82ac052..58e39d0 100644 --- a/CBPSSE/CBPSSE.vcxproj.filters +++ b/CBPSSE/CBPSSE.vcxproj.filters @@ -69,6 +69,9 @@ Header Files + + Header Files + @@ -119,7 +122,7 @@ Source Files\api - + Source Files diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 06a6b90..2ad362d 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -71,7 +71,6 @@ void Thing::updateConfig(configEntry_t & centry) { maxOffsetX = centry["maxoffsetX"]; maxOffsetY = centry["maxoffsetY"]; maxOffsetZ = centry["maxoffsetZ"]; - timeTick = centry["timetick"]; linearX = centry["linearX"]; linearY = centry["linearY"]; linearZ = centry["linearZ"]; @@ -81,17 +80,16 @@ void Thing::updateConfig(configEntry_t & centry) { rotateLinearX = centry["rotateLinearX"]; rotateLinearY = centry["rotateLinearY"]; rotateLinearZ = centry["rotateLinearZ"]; - // Optional entries for backwards compatability + timeTick = centry["timetick"]; if (centry.find("timeStep") != centry.end()) timeStep = centry["timeStep"]; else - timeStep = 1.0f; + timeStep = 0.016f; gravityBias = centry["gravityBias"]; gravityCorrection = centry["gravityCorrection"]; cogOffsetY = centry["cogOffsetY"]; cogOffsetX = centry["cogOffsetX"]; cogOffsetZ = centry["cogOffsetZ"]; - fusionGirlEnabled = centry["FG"] == 1.0; if (timeTick <= 1) timeTick = 1; @@ -318,11 +316,11 @@ void Thing::update(Actor *actor) { invRot = standardRot * obj->m_parent->m_worldTransform.rot; } - auto localDiff = NiPoint3(diff.x * linearX, - diff.y * linearY, - diff.z * linearZ); + auto localDiff = invRot * diff; + localDiff.x *= linearX; + localDiff.y *= linearY; + localDiff.z *= linearZ; auto rotDiff = localDiff; - localDiff = invRot * localDiff; oldWorldPos = diff + target; #if DEBUG diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 348aad0..71c78ce 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -25,19 +25,18 @@ class Thing { float gravityBias = 0.0f; float gravityCorrection = 0.0f; float timeTick = 4.0f; - float linearX = 0; - float linearY = 0; - float linearZ = 0; - float rotationalX = 0.1; - float rotationalY = 0.1; - float rotationalZ = 0.1; - - float rotateLinearX = 0.0; - float rotateLinearY = 0.0; - float rotateLinearZ = 0.0; - - float timeStep = 1.0f; - bool fusionGirlEnabled; + float linearX = 0.0f; + float linearY = 0.0f; + float linearZ = 0.0f; + float rotationalX = 0.0f; + float rotationalY = 0.0f; + float rotationalZ = 0.0f; + + float rotateLinearX = 0.0f; + float rotateLinearY = 0.0f; + float rotateLinearZ = 0.0f; + + float timeStep = 0.016f; Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index de4ae28..c623927 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -26,11 +26,15 @@ config_t config; config_t configArmor; configOverrides_t configOverrides; -void loadConfig() { +bool loadConfig() { + bool reloadActors = false; + auto playerOnlyOld = playerOnly; + auto femaleOnlyOld = femaleOnly; + auto maleOnlyOld = maleOnly; + logger.info("loadConfig\n"); boneNames.clear(); std::set bonesSet; - config.clear(); // Note: Using INIReader results in a slight double read @@ -41,9 +45,13 @@ void loadConfig() { logger.error("Reading CBP Config\n"); // Read general settings + playerOnly = configReader.GetBoolean("General", "playerOnly", false); femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); maleOnly = configReader.GetBoolean("General", "maleOnly", false); + reloadActors = (playerOnly ^ playerOnlyOld) || + (femaleOnly ^ femaleOnlyOld) || + (maleOnly ^ maleOnlyOld); detectArmor = configReader.GetBoolean("General", "detectArmor", false); configReloadCount = configReader.GetInteger("Tuning", "rate", 0); @@ -115,6 +123,7 @@ void loadConfig() { boneNames.assign(bonesSet.begin(), bonesSet.end()); logger.error("Finished CBP Config\n"); + return reloadActors; } void dumpConfigtoLog() diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 507e191..a135378 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -16,4 +16,4 @@ extern int configReloadCount; extern config_t config; extern config_t configArmor; -void loadConfig(); \ No newline at end of file +bool loadConfig(); \ No newline at end of file diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index 61ffa4c..09abaa7 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -3,14 +3,20 @@ #pragma warning(disable : 4996) +// TODO make better macro +#define LOG_ON 1 + Logger::Logger(const char *fname) { +#ifdef LOG_ON handle = fopen(fname, "a"); if (handle) { fprintf(handle, "CBP Log initialized\n"); } +#endif } void Logger::info(const char *fmt...) { +#ifdef LOG_ON if (handle) { va_list argptr; va_start(argptr, fmt); @@ -18,9 +24,11 @@ void Logger::info(const char *fmt...) { va_end(argptr); fflush(handle); } +#endif } void Logger::error(const char *fmt...) { +#ifdef LOG_ON if (handle) { va_list argptr; va_start(argptr, fmt); @@ -28,6 +36,7 @@ void Logger::error(const char *fmt...) { va_end(argptr); fflush(handle); } +#endif } Logger logger("Data\\F4SE\\Plugins\\cbp.log"); diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index f2df5ca..3081ef3 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -23,6 +23,7 @@ #include #include "ActorEntry.h" +#include "ActorUtils.h" #include "log.h" #include "Thing.h" #include "config.h" @@ -40,6 +41,9 @@ #pragma warning(disable : 4996) +using actorUtils::IsActorMale; +using actorUtils::IsActorTorsoArmorEquipped; +using actorUtils::IsActorInPowerArmor; extern F4SETaskInterface *g_task; @@ -112,7 +116,6 @@ inline void safe_delete(T*& in) { std::unordered_map actors; - TESObjectCELL *curCell = nullptr; @@ -146,8 +149,13 @@ void updateActors() { // Attempt to get actors auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); if (actor && actor->unkF0) { + // Find if actors is already being tracked auto soIt = actors.find(actor->formID); - if (soIt == actors.end()) { + if (soIt == actors.end() && + IsActorInPowerArmor(actor) && + (!playerOnly || (actor->formID == 0x14 && playerOnly)) && + (!maleOnly || (IsActorMale(actor) && maleOnly)) && + (!femaleOnly || (!IsActorMale(actor) && femaleOnly))) { logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); //logger.info("%s\n", actor->unk08-> & 1); //logger.info("%s\n", actor->race->textureModel[1].GetModelName()); @@ -183,10 +191,15 @@ void updateActors() { static int count = 0; if (configReloadCount && count++ > configReloadCount) { count = 0; - loadConfig(); + auto reloadActors = loadConfig(); for (auto &a : actors) { a.second.updateConfig(config); } + + // Clear actors + if (reloadActors) { + actors.clear(); + } } //logger.error("Updating %d entities\n", actorEntries.size()); @@ -197,16 +210,11 @@ void updateActors() { } else { auto &obj = objIterator->second; - if (obj.isBound() && - a.IsInPowerArmor() && - (!playerOnly || (a.actor->formID == 0x14 && playerOnly)) && - (!maleOnly || (a.IsMale() && maleOnly)) && - (!femaleOnly || (!a.IsMale() && femaleOnly)) - ) { + if (obj.isBound()) { obj.update(a.actor); } else { - if (a.IsTorsoArmorEquipped()) { + if (IsActorTorsoArmorEquipped(a.actor)) { obj.bind(a.actor, boneNames, configArmor); } else { From 031e98ff97254acc5d4ff17725659c0e21c4c268 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Thu, 6 Feb 2020 02:04:38 -0800 Subject: [PATCH 15/66] Added rotateRotation and code cleanup --- CBPSSE/SimObj.cpp | 2 +- CBPSSE/Thing.cpp | 33 ++++++++++++++++++++++----------- CBPSSE/Thing.h | 4 ++++ CBPSSE/config.cpp | 8 +++++--- CBPSSE/scan.cpp | 1 + 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 7ebc198..266e5f5 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -30,7 +30,7 @@ bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &c BSFixedString cs(bone_c_str); auto bone = loadedData->rootNode->GetObjectByName(&cs); if (!bone) { - logger.info("Failed to find Bone %s for actor %08x\n", b, actor->formID); + logger.info("Failed to find Bone %s for actor %08x\n", b.c_str(), actor->formID); } else { //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); things.emplace(b, Thing(bone, cs)); diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 2ad362d..81de5e9 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -80,6 +80,11 @@ void Thing::updateConfig(configEntry_t & centry) { rotateLinearX = centry["rotateLinearX"]; rotateLinearY = centry["rotateLinearY"]; rotateLinearZ = centry["rotateLinearZ"]; + + rotateRotationX = centry["rotateRotationX"]; + rotateRotationY = centry["rotateRotationY"]; + rotateRotationZ = centry["rotateRotationZ"]; + timeTick = centry["timetick"]; if (centry.find("timeStep") != centry.end()) timeStep = centry["timeStep"]; @@ -299,29 +304,28 @@ void Thing::update(Actor *actor) { // Convert the world translations into local coordinates NiMatrix43 invRot; + NiMatrix43 rotateLinear; + rotateLinear.SetEulerAngles(rotateLinearX * DEG_TO_RAD, + rotateLinearY * DEG_TO_RAD, + rotateLinearZ * DEG_TO_RAD); if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { - NiMatrix43 standardRot; - standardRot.SetEulerAngles(rotateLinearX * DEG_TO_RAD, - rotateLinearY * DEG_TO_RAD, - rotateLinearZ * DEG_TO_RAD); - invRot = standardRot * obj->m_parent->m_worldTransform.rot; + invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; } else { //invRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose(); - NiMatrix43 standardRot; - standardRot.SetEulerAngles(rotateLinearX * DEG_TO_RAD, - rotateLinearY * DEG_TO_RAD, - rotateLinearZ * DEG_TO_RAD); - invRot = standardRot * obj->m_parent->m_worldTransform.rot; + invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; } - auto localDiff = invRot * diff; + auto localDiff = diff; + localDiff = skeletonObj->m_localTransform.rot * localDiff; localDiff.x *= linearX; localDiff.y *= linearY; localDiff.z *= linearZ; auto rotDiff = localDiff; + localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; + localDiff = invRot * localDiff; oldWorldPos = diff + target; #if DEBUG logger.error("invRot x=10 Transformation:"); @@ -361,7 +365,14 @@ void Thing::update(Actor *actor) { #endif // Do rotation. + NiMatrix43 rotateRotation; + rotateRotation.SetEulerAngles(rotateRotationX * DEG_TO_RAD, + rotateRotationY * DEG_TO_RAD, + rotateRotationZ * DEG_TO_RAD); + NiMatrix43 standardRot; + + rotDiff = rotateRotation * rotDiff; standardRot.SetEulerAngles(rotDiff.x, rotDiff.y, rotDiff.z); obj->m_localTransform.rot = standardRot * origLocalRot.at(boneName.c_str()); } diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 71c78ce..cb3f10a 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -36,6 +36,10 @@ class Thing { float rotateLinearY = 0.0f; float rotateLinearZ = 0.0f; + float rotateRotationX = 0.0f; + float rotateRotationY = 0.0f; + float rotateRotationZ = 0.0f; + float timeStep = 0.016f; Thing(NiAVObject *obj, BSFixedString &name); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index c623927..b1ad39d 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -27,15 +27,18 @@ config_t configArmor; configOverrides_t configOverrides; bool loadConfig() { + logger.info("loadConfig\n"); + bool reloadActors = false; auto playerOnlyOld = playerOnly; auto femaleOnlyOld = femaleOnly; auto maleOnlyOld = maleOnly; + std::set bonesSet; - logger.info("loadConfig\n"); boneNames.clear(); - std::set bonesSet; config.clear(); + configArmor.clear(); + configOverrides.clear(); // Note: Using INIReader results in a slight double read INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); @@ -45,7 +48,6 @@ bool loadConfig() { logger.error("Reading CBP Config\n"); // Read general settings - playerOnly = configReader.GetBoolean("General", "playerOnly", false); femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); maleOnly = configReader.GetBoolean("General", "maleOnly", false); diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 3081ef3..cfc0f24 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -215,6 +215,7 @@ void updateActors() { } else { if (IsActorTorsoArmorEquipped(a.actor)) { + logger.info("torso armor detected on actor %x\n", a.actor->formID); obj.bind(a.actor, boneNames, configArmor); } else { From e9f61afbd3d9a0edf926ad36715928e90a20a5ca Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sun, 9 Feb 2020 03:36:57 -0800 Subject: [PATCH 16/66] detectArmor Overrides --- CBPSSE/Thing.cpp | 4 ++-- CBPSSE/config.cpp | 24 ++++++++++++++++++++++++ CBPSSE/log.cpp | 2 +- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 81de5e9..02d4a0e 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -6,8 +6,8 @@ constexpr auto DEG_TO_RAD = 3.14159265 / 180; // TODO Make these logger macros -#define DEBUG 0 -#define TRANSFORM_DEBUG 0 +//#define DEBUG 0 +//#define TRANSFORM_DEBUG 0 std::unordered_map origLocalPos; std::unordered_map origLocalRot; diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index b1ad39d..442eccc 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -25,6 +25,7 @@ bool detectArmor = false; config_t config; config_t configArmor; configOverrides_t configOverrides; +configOverrides_t configArmorOverrides; bool loadConfig() { logger.info("loadConfig\n"); @@ -39,6 +40,7 @@ bool loadConfig() { config.clear(); configArmor.clear(); configOverrides.clear(); + configArmorOverrides.clear(); // Note: Using INIReader results in a slight double read INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); @@ -65,6 +67,9 @@ bool loadConfig() { auto overrideStr = std::string("Override:"); auto splitStr = std::mismatch(overrideStr.begin(), overrideStr.end(), sectionIt->begin()); + auto overrideAStr = std::string("Override.A:"); + auto splitAStr = std::mismatch(overrideAStr.begin(), overrideAStr.end(), sectionIt->begin()); + if (*sectionIt == std::string("Attach")) { // Get section contents auto sectionMap = configReader.Section(*sectionIt); @@ -109,6 +114,16 @@ bool loadConfig() { configOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionIt, valuesIt.first, 0.0); } } + else if (splitAStr.first == overrideAStr.end()) { + // If section name is prefixed with "Override:", grab other half of name for bone + auto boneName = std::string(splitAStr.second, sectionIt->end()); + + // Get section contents + auto sectionMap = configReader.Section(*sectionIt); + for (auto& valuesIt : sectionMap) { + configArmorOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionIt, valuesIt.first, 0.0); + } + } } // replace configs with override settings (if any) @@ -120,6 +135,15 @@ bool loadConfig() { } } + // replace armor configs with override settings (if any) + for (auto& boneIter : configArmorOverrides) { + if (configArmor.count(boneIter.first) > 0) { + for (auto settingIter : boneIter.second) { + configArmor[boneIter.first][settingIter.first] = settingIter.second; + } + } + } + // Remove duplicate entries bonesSet = std::set(boneNames.begin(), boneNames.end()); boneNames.assign(bonesSet.begin(), bonesSet.end()); diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index 09abaa7..f79e93c 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -4,7 +4,7 @@ #pragma warning(disable : 4996) // TODO make better macro -#define LOG_ON 1 +//#define LOG_ON Logger::Logger(const char *fname) { #ifdef LOG_ON From 930940ca0a66abf67f0ef3be0f11ae29ca00c7bf Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sun, 9 Feb 2020 04:08:31 -0800 Subject: [PATCH 17/66] Remove old code --- CBPSSE/Thing.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 02d4a0e..4dbf0ce 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -198,17 +198,7 @@ void Thing::update(Actor *actor) { showRot(skeletonObj->m_worldTransform.rot); //showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif - NiMatrix43 targetRot; - if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { - targetRot = skeletonObj->m_localTransform.rot.Transpose(); - } - else { - //targetRot = skeletonObj->m_localTransform.rot.Transpose() * - // comObj->m_localTransform.rot * - // skeletonObj->m_localTransform.rot * - // obj->m_parent->m_worldTransform.rot.Transpose(); - targetRot = skeletonObj->m_localTransform.rot.Transpose(); - } + NiMatrix43 targetRot = skeletonObj->m_localTransform.rot.Transpose(); NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; // Offset to move Center of Mass make rotational motion more significant @@ -309,13 +299,7 @@ void Thing::update(Actor *actor) { rotateLinearY * DEG_TO_RAD, rotateLinearZ * DEG_TO_RAD); - if (obj->m_name == BSFixedString("Breast_CBP_R_02") || obj->m_name == BSFixedString("Breast_CBP_L_02")) { - invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; - } - else { - //invRot = obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * comObj->m_localTransform.rot.Transpose(); - invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; - } + invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; auto localDiff = diff; localDiff = skeletonObj->m_localTransform.rot * localDiff; From 43e3e705f2b28b9f63dc823405dad6dc89d51eb2 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Mon, 10 Feb 2020 01:19:44 -0800 Subject: [PATCH 18/66] Hotfix for detectArmor not working & initial Papyrus code. Misc. refactoring --- CBPSSE/CBPSSE.vcxproj | 23 +++++++++++++++++-- CBPSSE/CBPSSE.vcxproj.filters | 42 ++++++++++++++++++++++++++++++++++ CBPSSE/PapyrusOCBP.cpp | 43 +++++++++++++++++++++++++++++++++++ CBPSSE/PapyrusOCBP.h | 16 +++++++++++++ CBPSSE/SimObj.cpp | 24 +++++++++++++------ CBPSSE/SimObj.h | 14 ++++++------ CBPSSE/Thing.cpp | 15 +++++++++++- CBPSSE/Thing.h | 2 +- CBPSSE/config.h | 2 ++ CBPSSE/log.cpp | 8 +++---- CBPSSE/log.h | 6 ++--- CBPSSE/main.cpp | 19 +++++++++++++++- CBPSSE/scan.cpp | 26 +++++++++++++-------- 13 files changed, 205 insertions(+), 35 deletions(-) create mode 100644 CBPSSE/PapyrusOCBP.cpp create mode 100644 CBPSSE/PapyrusOCBP.h diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index c2acf20..4c1d9cf 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -78,7 +78,7 @@ .dll cbp false - $(LibraryPath) + $(ProjectDir)x64_v141\Release\;$(OutDir);$(LibraryPath) $(IncludePath) @@ -151,6 +151,9 @@ Installing DLL to FO4 Plugins... + + true + @@ -159,12 +162,19 @@ + + + + + + + @@ -177,10 +187,17 @@ + + + + + + + @@ -195,10 +212,12 @@ {472e19ab-def0-42df-819b-18722e8dc822} + true {a236f69d-8ff9-4491-ac5f-45bf49448bbe} - true + false + true {20c6411c-596f-4b85-be4e-8bc91f59d8a6} diff --git a/CBPSSE/CBPSSE.vcxproj.filters b/CBPSSE/CBPSSE.vcxproj.filters index 58e39d0..17564cc 100644 --- a/CBPSSE/CBPSSE.vcxproj.filters +++ b/CBPSSE/CBPSSE.vcxproj.filters @@ -72,6 +72,27 @@ Header Files + + Header Files + + + Header Files\api + + + Header Files\api + + + Header Files\api + + + Header Files\api + + + Header Files\api + + + Header Files\api + @@ -125,6 +146,27 @@ Source Files + + Source Files + + + Source Files\api + + + Source Files\api + + + Source Files\api + + + Source Files\api + + + Source Files\api + + + Source Files\api + diff --git a/CBPSSE/PapyrusOCBP.cpp b/CBPSSE/PapyrusOCBP.cpp new file mode 100644 index 0000000..9f931d0 --- /dev/null +++ b/CBPSSE/PapyrusOCBP.cpp @@ -0,0 +1,43 @@ +#include "PapyrusOCBP.h" + +//#include "f4se/PapyrusVM.h" +#include "f4se/PapyrusNativeFunctions.h" + +#include "f4se/GameReferences.h" +#include "f4se/GameRTTI.h" + +#include +#include + +#include "f4se/NiNodes.h" +#include "f4se/NiExtraData.h" +#include "f4se/BSGeometry.h" + +#include "f4se/GameObjects.h" + +#include "SimObj.h" + +std::unordered_map> boneIgnores; + +namespace papyrusOCBP +{ + void ToggleBone(StaticFunctionTag*, Actor* actor, bool toggle, BSFixedString boneName) + { + boneIgnores[actor->formID][std::string(boneName.c_str())] = toggle; + } + + void ClearBoneToggles(StaticFunctionTag*) + { + boneIgnores.clear(); + } + +} + +void papyrusOCBP::RegisterFuncs(VirtualMachine* vm) +{ + vm->RegisterFunction( + new NativeFunction3("ToggleBone", "OCBP_API", papyrusOCBP::ToggleBone, vm)); + + vm->RegisterFunction( + new NativeFunction0("ClearBoneToggles", "OCBP_API", papyrusOCBP::ClearBoneToggles, vm)); +} \ No newline at end of file diff --git a/CBPSSE/PapyrusOCBP.h b/CBPSSE/PapyrusOCBP.h new file mode 100644 index 0000000..2ae30f7 --- /dev/null +++ b/CBPSSE/PapyrusOCBP.h @@ -0,0 +1,16 @@ +#pragma once + +#include "f4se/GameTypes.h" +#include "f4se/PapyrusVM.h" +#include +#include + +class VirtualMachine; +struct StaticFunctionTag; + +extern std::unordered_map> boneIgnores; // probably should be moved somewhere else + +namespace papyrusOCBP +{ + void RegisterFuncs(VirtualMachine* vm); +}; \ No newline at end of file diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 266e5f5..38bd301 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -3,6 +3,7 @@ #include "f4se/GameForms.h" #include "f4se/GameRTTI.h" #include "log.h" +#include "PapyrusOCBP.h" // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory std::vector boneNames; @@ -16,7 +17,7 @@ SimObj::~SimObj() { } -bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &config) +bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &config) { // logger.error("bind\n"); @@ -36,13 +37,13 @@ bool SimObj::bind(Actor *actor, std::vector& boneNames, config_t &c things.emplace(b, Thing(bone, cs)); } } - updateConfig(config); + UpdateConfig(config); return true; } return false; } -bool SimObj::actorValid(Actor *actor) { +bool SimObj::ActorValid(Actor *actor) { if (actor->flags & TESForm::kFlag_IsDeleted) return false; if (actor && actor->unkF0 && actor->unkF0->rootNode) @@ -50,20 +51,29 @@ bool SimObj::actorValid(Actor *actor) { return false; } -void SimObj::update(Actor *actor) { +void SimObj::Update(Actor *actor) { if (!bound) return; //logger.error("update\n"); for (auto &t : things) { + + // TODO bad way to do it + //if (boneIgnores.find(actor->formID) != boneIgnores.end()) { + // auto actorBoneMap = boneIgnores.at(actor->formID); + // if (actorBoneMap.find(t.first) != actorBoneMap.end()) { + // if (actorBoneMap.at(t.first)) { + // continue; + // } + // } + //} t.second.update(actor); } //logger.error("end SimObj update\n"); } -bool SimObj::updateConfig(config_t & config) { +bool SimObj::UpdateConfig(config_t & config) { for (auto &thing : things) { thing.second.updateConfig(config[std::string(thing.first)]); } return true; -} - +} \ No newline at end of file diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index 8b943f7..17d3816 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -14,14 +14,14 @@ class SimObj { SimObj(Actor *actor, config_t &config); SimObj() {} ~SimObj(); - bool bind(Actor *actor, std::vector &boneNames, config_t &config); - bool actorValid(Actor *actor); - void update(Actor *actor); - bool updateConfig(config_t &config); - bool isBound() { return bound; } + bool Bind(Actor *actor, std::vector &boneNames, config_t &config); + bool ActorValid(Actor *actor); + void Update(Actor *actor); + bool UpdateConfig(config_t &config); + bool IsBound() { return bound; } }; extern std::vector boneNames; -extern std::unordered_map configMap; -extern std::unordered_map configMapArmor; \ No newline at end of file +//extern std::unordered_map configMap; +//extern std::unordered_map configMapArmor; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 4dbf0ce..f877350 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -108,8 +108,21 @@ static float clamp(float val, float min, float max) { return val; } -void Thing::reset() { +void Thing::reset(Actor *actor) { + auto loadedState = actor->unkF0; + if (!loadedState || !loadedState->rootNode) { + logger.error("No loaded state for actor %08x\n", actor->formID); + return; + } + auto obj = loadedState->rootNode->GetObjectByName(&boneName); + + if (!obj) { + logger.error("Couldn't get name for loaded state for actor %08x\n", actor->formID); + return; + } + obj->m_localTransform.pos = origLocalPos.at(boneName.c_str()); + obj->m_localTransform.rot = origLocalRot.at(boneName.c_str()); } // Returns diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index cb3f10a..9b9d7c8 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -47,7 +47,7 @@ class Thing { void updateConfig(configEntry_t ¢ry); void update(Actor *actor); - void reset(); + void reset(Actor * actor); void showPos(NiPoint3& p); void showRot(NiMatrix43& r); diff --git a/CBPSSE/config.h b/CBPSSE/config.h index a135378..c671e3c 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -12,6 +12,8 @@ typedef std::unordered_map configOverrides_t; extern bool playerOnly; extern bool femaleOnly; extern bool maleOnly; +extern bool detectArmor; + extern int configReloadCount; extern config_t config; extern config_t configArmor; diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index f79e93c..1500dbb 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -6,7 +6,7 @@ // TODO make better macro //#define LOG_ON -Logger::Logger(const char *fname) { +CbpLogger::CbpLogger(const char *fname) { #ifdef LOG_ON handle = fopen(fname, "a"); if (handle) { @@ -15,7 +15,7 @@ Logger::Logger(const char *fname) { #endif } -void Logger::info(const char *fmt...) { +void CbpLogger::info(const char *fmt...) { #ifdef LOG_ON if (handle) { va_list argptr; @@ -27,7 +27,7 @@ void Logger::info(const char *fmt...) { #endif } -void Logger::error(const char *fmt...) { +void CbpLogger::error(const char *fmt...) { #ifdef LOG_ON if (handle) { va_list argptr; @@ -39,4 +39,4 @@ void Logger::error(const char *fmt...) { #endif } -Logger logger("Data\\F4SE\\Plugins\\cbp.log"); +CbpLogger logger("Data\\F4SE\\Plugins\\cbp.log"); diff --git a/CBPSSE/log.h b/CBPSSE/log.h index 0f62938..0450be3 100644 --- a/CBPSSE/log.h +++ b/CBPSSE/log.h @@ -1,13 +1,13 @@ #pragma once #include -class Logger { +class CbpLogger { public: - Logger(const char* fname); + CbpLogger(const char* fname); void info(const char* args...); void error(const char* args...); FILE *handle; }; -extern Logger logger; \ No newline at end of file +extern CbpLogger logger; \ No newline at end of file diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index ccd9409..2b79c7c 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -7,9 +7,10 @@ #include "f4se/GameEvents.h" #include "log.h" #include "config.h" +#include "PapyrusOCBP.h" - +bool RegisterFuncs(VirtualMachine* vm); PluginHandle g_pluginHandle = kPluginHandle_Invalid; //F4SEMessagingInterface * g_messagingInterface = NULL; @@ -17,6 +18,7 @@ PluginHandle g_pluginHandle = kPluginHandle_Invalid; //F4SEScaleformInterface * g_scaleform = NULL; //F4SESerializationInterface * g_serialization = NULL; F4SETaskInterface * g_task = nullptr; +F4SEPapyrusInterface * g_papyrus = nullptr; //IDebugLog gLog("Data\\F4SE\\Plugins\\hook.log"); @@ -117,6 +119,12 @@ extern "C" } // supported runtime version + g_papyrus = (F4SEPapyrusInterface*)f4se->QueryInterface(kInterface_Papyrus); + if (!g_papyrus) + { + _WARNING("couldn't get papyrus interface"); + } + logger.error("Query complete\n"); return true; } @@ -132,6 +140,9 @@ extern "C" return false; } + if (g_papyrus) + g_papyrus->Register(RegisterFuncs); + // Load initial config before the hook. logger.error("Loading Config\n"); loadConfig(); @@ -143,6 +154,12 @@ extern "C" } }; +bool RegisterFuncs(VirtualMachine* vm) +{ + papyrusOCBP::RegisterFuncs(vm); + return true; +} + BOOL WINAPI DllMain( _In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index cfc0f24..e9f0e76 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -27,6 +27,7 @@ #include "log.h" #include "Thing.h" #include "config.h" +#include "PapyrusOCBP.h" #include "SimObj.h" #include "f4se/GameRTTI.h" #include "f4se/GameForms.h" @@ -160,12 +161,12 @@ void updateActors() { //logger.info("%s\n", actor->unk08-> & 1); //logger.info("%s\n", actor->race->textureModel[1].GetModelName()); auto obj = SimObj(actor, config); - if (obj.actorValid(actor)) { + if (obj.ActorValid(actor)) { actors.emplace(actor->formID, obj); actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } - else if (soIt->second.actorValid(actor)) { + else if (soIt->second.ActorValid(actor)) { actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } @@ -193,7 +194,7 @@ void updateActors() { count = 0; auto reloadActors = loadConfig(); for (auto &a : actors) { - a.second.updateConfig(config); + a.second.UpdateConfig(config); } // Clear actors @@ -209,17 +210,24 @@ void updateActors() { //logger.error("Sim Object not found in tracked actors\n"); } else { - auto &obj = objIterator->second; - if (obj.isBound()) { - obj.update(a.actor); + auto &simObj = objIterator->second; + if (simObj.IsBound()) { + if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { + logger.info("torso armor detected on actor %x\n", a.actor->formID); + simObj.UpdateConfig(configArmor); + } + else { + simObj.UpdateConfig(config); + } + simObj.Update(a.actor); } else { - if (IsActorTorsoArmorEquipped(a.actor)) { + if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { logger.info("torso armor detected on actor %x\n", a.actor->formID); - obj.bind(a.actor, boneNames, configArmor); + simObj.Bind(a.actor, boneNames, configArmor); } else { - obj.bind(a.actor, boneNames, config); + simObj.Bind(a.actor, boneNames, config); } } } From 194da9091c90ee3343a54601ad6494cb3dee96e1 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Tue, 11 Feb 2020 01:47:31 -0800 Subject: [PATCH 19/66] Moved maxOffset into relevant spot. Papyrus functions added. Linker configuration updated. --- CBPSSE/ActorUtils.cpp | 1 + CBPSSE/CBPSSE.vcxproj | 9 ++++----- CBPSSE/PapyrusOCBP.cpp | 19 +++++++++++++++++-- CBPSSE/SimObj.cpp | 18 +++++++++--------- CBPSSE/Thing.cpp | 9 +++++---- CBPSSE/config.cpp | 13 +++++++++++-- CBPSSE/config.h | 2 +- CBPSSE/main.cpp | 2 +- CBPSSE/scan.cpp | 3 ++- 9 files changed, 51 insertions(+), 25 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 6535951..547d38d 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -21,6 +21,7 @@ bool actorUtils::IsActorInPowerArmor(Actor* actor) { return !actor->extraDataList->HasType(kExtraData_PowerArmor); } +// should change this for any armor bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { bool isEquipped = false; if (!actor) diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index 4c1d9cf..f01bbb7 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -143,7 +143,7 @@ Windows true %(AdditionalLibraryDirectories) - + $(OutDir)f4se_1_10_163.lib;$(OutDir)f4se_common.lib;$(SolutionDir)x64_v141\Release\common_vc14.lib;%(AdditionalDependencies) copy "$(TargetPath)" "$(Fallout4Path)\$(TargetFileName)" @@ -151,9 +151,7 @@ Installing DLL to FO4 Plugins... - - true - + @@ -212,7 +210,8 @@ {472e19ab-def0-42df-819b-18722e8dc822} - true + false + true {a236f69d-8ff9-4491-ac5f-45bf49448bbe} diff --git a/CBPSSE/PapyrusOCBP.cpp b/CBPSSE/PapyrusOCBP.cpp index 9f931d0..c69166b 100644 --- a/CBPSSE/PapyrusOCBP.cpp +++ b/CBPSSE/PapyrusOCBP.cpp @@ -21,11 +21,23 @@ std::unordered_map> boneIgnores; namespace papyrusOCBP { - void ToggleBone(StaticFunctionTag*, Actor* actor, bool toggle, BSFixedString boneName) + void SetBoneToggle(StaticFunctionTag*, Actor* actor, bool toggle, BSFixedString boneName) { boneIgnores[actor->formID][std::string(boneName.c_str())] = toggle; } + bool GetBoneToggle(StaticFunctionTag*, Actor* actor, BSFixedString boneName) + { + if (boneIgnores.find(actor->formID) != boneIgnores.end()) { + auto actorsBoneIgns = boneIgnores.at(actor->formID); + if (actorsBoneIgns.find(std::string(boneName.c_str())) != actorsBoneIgns.end()) { + return actorsBoneIgns.at(std::string(boneName.c_str())); + } + } + + return false; + } + void ClearBoneToggles(StaticFunctionTag*) { boneIgnores.clear(); @@ -36,7 +48,10 @@ namespace papyrusOCBP void papyrusOCBP::RegisterFuncs(VirtualMachine* vm) { vm->RegisterFunction( - new NativeFunction3("ToggleBone", "OCBP_API", papyrusOCBP::ToggleBone, vm)); + new NativeFunction3("SetBoneToggle", "OCBP_API", papyrusOCBP::SetBoneToggle, vm)); + + vm->RegisterFunction( + new NativeFunction2("GetBoneToggle", "OCBP_API", papyrusOCBP::GetBoneToggle, vm)); vm->RegisterFunction( new NativeFunction0("ClearBoneToggles", "OCBP_API", papyrusOCBP::ClearBoneToggles, vm)); diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 38bd301..47f9ec1 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -57,15 +57,15 @@ void SimObj::Update(Actor *actor) { //logger.error("update\n"); for (auto &t : things) { - // TODO bad way to do it - //if (boneIgnores.find(actor->formID) != boneIgnores.end()) { - // auto actorBoneMap = boneIgnores.at(actor->formID); - // if (actorBoneMap.find(t.first) != actorBoneMap.end()) { - // if (actorBoneMap.at(t.first)) { - // continue; - // } - // } - //} + // Might be a better way to do this + if (boneIgnores.find(actor->formID) != boneIgnores.end()) { + auto actorBoneMap = boneIgnores.at(actor->formID); + if (actorBoneMap.find(t.first) != actorBoneMap.end()) { + if (actorBoneMap.at(t.first)) { + continue; + } + } + } t.second.update(actor); } //logger.error("end SimObj update\n"); diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index f877350..69e2965 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -290,10 +290,6 @@ void Thing::update(Actor *actor) { // clamp the difference to stop the breast severely lagging at low framerates diff = newPos - target; - diff.x = clamp(diff.x, -maxOffsetX, maxOffsetX); - diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); - diff.z = clamp(diff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; - //oldWorldPos = target + diff; #if DEBUG @@ -319,6 +315,11 @@ void Thing::update(Actor *actor) { localDiff.x *= linearX; localDiff.y *= linearY; localDiff.z *= linearZ; + + localDiff.x = clamp(localDiff.x, -maxOffsetX, maxOffsetX); + localDiff.y = clamp(localDiff.y, -maxOffsetY, maxOffsetY); + localDiff.z = clamp(localDiff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; + auto rotDiff = localDiff; localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 442eccc..3992d78 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -27,7 +27,7 @@ config_t configArmor; configOverrides_t configOverrides; configOverrides_t configArmorOverrides; -bool loadConfig() { +bool LoadConfig() { logger.info("loadConfig\n"); bool reloadActors = false; @@ -152,13 +152,22 @@ bool loadConfig() { return reloadActors; } -void dumpConfigtoLog() +void DumpConfigtoLog() { // Log contents of config + logger.info("***** Config Dump *****"); for (auto section : config) { logger.info("[%s]\n", section.first.c_str()); for (auto setting : section.second) { logger.info("%s=%f\n", setting.first.c_str(), setting.second); } } + + logger.info("***** ConfigArmor Dump *****"); + for (auto section : configArmor) { + logger.info("[%s]\n", section.first.c_str()); + for (auto setting : section.second) { + logger.info("%s=%f\n", setting.first.c_str(), setting.second); + } + } } \ No newline at end of file diff --git a/CBPSSE/config.h b/CBPSSE/config.h index c671e3c..a6003cc 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -18,4 +18,4 @@ extern int configReloadCount; extern config_t config; extern config_t configArmor; -bool loadConfig(); \ No newline at end of file +bool LoadConfig(); \ No newline at end of file diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index 2b79c7c..022657d 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -145,7 +145,7 @@ extern "C" // Load initial config before the hook. logger.error("Loading Config\n"); - loadConfig(); + LoadConfig(); //g_messagingInterface->RegisterListener(0, "F4SE", MessageHandler); logger.error("Hooking Game\n"); DoHook(); diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index e9f0e76..9417625 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -192,7 +192,7 @@ void updateActors() { static int count = 0; if (configReloadCount && count++ > configReloadCount) { count = 0; - auto reloadActors = loadConfig(); + auto reloadActors = LoadConfig(); for (auto &a : actors) { a.second.UpdateConfig(config); } @@ -212,6 +212,7 @@ void updateActors() { else { auto &simObj = objIterator->second; if (simObj.IsBound()) { + // need better system for update config if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { logger.info("torso armor detected on actor %x\n", a.actor->formID); simObj.UpdateConfig(configArmor); From 2a96f5ad51eb7f1bdd2b17acd16b09ff0d75278a Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Thu, 13 Feb 2020 01:52:23 -0800 Subject: [PATCH 20/66] Added npcOnly option. Added experimental absRot feature. --- CBPSSE/ActorUtils.cpp | 2 +- CBPSSE/SimObj.h | 4 +--- CBPSSE/Thing.cpp | 17 +++++++++++++---- CBPSSE/Thing.h | 7 ++++++- CBPSSE/config.cpp | 8 +++++++- CBPSSE/config.h | 1 + CBPSSE/scan.cpp | 3 ++- 7 files changed, 31 insertions(+), 11 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 547d38d..3edf658 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -21,7 +21,7 @@ bool actorUtils::IsActorInPowerArmor(Actor* actor) { return !actor->extraDataList->HasType(kExtraData_PowerArmor); } -// should change this for any armor +// May want to change this for any armor bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { bool isEquipped = false; if (!actor) diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index 17d3816..b32ae66 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -22,6 +22,4 @@ class SimObj { }; -extern std::vector boneNames; -//extern std::unordered_map configMap; -//extern std::unordered_map configMapArmor; \ No newline at end of file +extern std::vector boneNames; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 69e2965..de80f71 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -98,6 +98,10 @@ void Thing::updateConfig(configEntry_t & centry) { if (timeTick <= 1) timeTick = 1; + absRotX = centry["absRotX"] != 0.0; + absRotY = centry["absRotY"] != 0.0; + absRotZ = centry["absRotZ"] != 0.0; + //zOffset = solveQuad(stiffness2, stiffness, -gravityBias); //logger.error("z offset = %f\n", solveQuad(stiffness2, stiffness, -gravityBias)); } @@ -312,13 +316,14 @@ void Thing::update(Actor *actor) { auto localDiff = diff; localDiff = skeletonObj->m_localTransform.rot * localDiff; - localDiff.x *= linearX; - localDiff.y *= linearY; - localDiff.z *= linearZ; localDiff.x = clamp(localDiff.x, -maxOffsetX, maxOffsetX); localDiff.y = clamp(localDiff.y, -maxOffsetY, maxOffsetY); - localDiff.z = clamp(localDiff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; + localDiff.z = clamp(localDiff.z, -maxOffsetZ, maxOffsetZ); + + localDiff.x *= linearX; + localDiff.y *= linearY; + localDiff.z *= linearZ; auto rotDiff = localDiff; localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; @@ -353,6 +358,10 @@ void Thing::update(Actor *actor) { rotDiff.y *= rotationalY; rotDiff.z *= rotationalZ; + if (absRotX) rotDiff.x = fabs(rotDiff.x); + if (absRotY) rotDiff.y = fabs(rotDiff.y); + if (absRotZ) rotDiff.z = fabs(rotDiff.z); + #if DEBUG logger.error("localTransform.pos after: "); showPos(obj->m_localTransform.pos); diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 9b9d7c8..ff085c8 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -8,6 +8,7 @@ class Thing { BSFixedString boneName; NiPoint3 oldWorldPos; + float oldRotZ; NiPoint3 velocity; clock_t time; @@ -42,11 +43,15 @@ class Thing { float timeStep = 0.016f; + bool absRotX = 0; + bool absRotY = 0; + bool absRotZ = 0; + Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); void updateConfig(configEntry_t ¢ry); - void update(Actor *actor); + void update(Actor * actor); void reset(Actor * actor); void showPos(NiPoint3& p); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 3992d78..d549436 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -20,6 +20,7 @@ int configReloadCount = 60; bool playerOnly = false; bool femaleOnly = false; bool maleOnly = false; +bool npcOnly = false; bool detectArmor = false; config_t config; @@ -34,6 +35,7 @@ bool LoadConfig() { auto playerOnlyOld = playerOnly; auto femaleOnlyOld = femaleOnly; auto maleOnlyOld = maleOnly; + auto npcOnlyOld = npcOnly; std::set bonesSet; boneNames.clear(); @@ -53,9 +55,13 @@ bool LoadConfig() { playerOnly = configReader.GetBoolean("General", "playerOnly", false); femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); maleOnly = configReader.GetBoolean("General", "maleOnly", false); + npcOnly = configReader.GetBoolean("General", "npcOnly", false); + reloadActors = (playerOnly ^ playerOnlyOld) || (femaleOnly ^ femaleOnlyOld) || - (maleOnly ^ maleOnlyOld); + (maleOnly ^ maleOnlyOld) || + (npcOnly ^ npcOnlyOld); + detectArmor = configReader.GetBoolean("General", "detectArmor", false); configReloadCount = configReader.GetInteger("Tuning", "rate", 0); diff --git a/CBPSSE/config.h b/CBPSSE/config.h index a6003cc..ed410d3 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -12,6 +12,7 @@ typedef std::unordered_map configOverrides_t; extern bool playerOnly; extern bool femaleOnly; extern bool maleOnly; +extern bool npcOnly; extern bool detectArmor; extern int configReloadCount; diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 9417625..655ed50 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -156,7 +156,8 @@ void updateActors() { IsActorInPowerArmor(actor) && (!playerOnly || (actor->formID == 0x14 && playerOnly)) && (!maleOnly || (IsActorMale(actor) && maleOnly)) && - (!femaleOnly || (!IsActorMale(actor) && femaleOnly))) { + (!femaleOnly || (!IsActorMale(actor) && femaleOnly)) && + (!npcOnly || (actor->formID != 0x14 && npcOnly))) { logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); //logger.info("%s\n", actor->unk08-> & 1); //logger.info("%s\n", actor->race->textureModel[1].GetModelName()); From bd98aa3e5c0f12a0b581f188adabe13bdca3e024 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Sun, 16 Feb 2020 05:12:58 -0800 Subject: [PATCH 21/66] Whitelist for race and gender added. Some configuration code moved around. --- CBPSSE/ActorUtils.cpp | 32 +++++++++++ CBPSSE/ActorUtils.h | 5 +- CBPSSE/SimObj.cpp | 16 ++++-- CBPSSE/Thing.cpp | 101 +++++++++++++++++----------------- CBPSSE/Thing.h | 15 +++--- CBPSSE/config.cpp | 123 ++++++++++++++++++++++++++++++++---------- CBPSSE/config.h | 15 +++++- CBPSSE/hookD3D.cpp | 2 +- CBPSSE/log.cpp | 6 +-- CBPSSE/log.h | 4 +- CBPSSE/main.cpp | 42 +++++++-------- CBPSSE/scan.cpp | 39 +++++--------- 12 files changed, 254 insertions(+), 146 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 3edf658..4e9d50d 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -1,8 +1,16 @@ +#include + #include "ActorUtils.h" +#include "log.h" + #include "f4se/GameExtraData.h" #include "f4se/GameObjects.h" #include "f4se/GameRTTI.h" +std::string actorUtils::GetActorRaceEID(Actor* actor) { + return std::string(actor->race->editorId.c_str()); +} + bool actorUtils::IsActorMale(Actor *actor) { TESNPC* actorNPC = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); @@ -29,4 +37,28 @@ bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { // 11 IS ARMOR TORSO SLOT (41 minus 30??) isEquipped = actor->equipData->slots[11].item; return isEquipped; +} + +bool actorUtils::IsActorTrackable(Actor* actor) { + bool inRaceWhitelist = find(raceWhitelist.begin(), raceWhitelist.end(), actorUtils::GetActorRaceEID(actor)) != raceWhitelist.end(); + return IsActorInPowerArmor(actor) && + (!playerOnly || (actor->formID == 0x14 && playerOnly)) && + (!maleOnly || (IsActorMale(actor) && maleOnly)) && + (!femaleOnly || (!IsActorMale(actor) && femaleOnly)) && + (!npcOnly || (actor->formID != 0x14 && npcOnly)) && + (!useWhitelist || (inRaceWhitelist && useWhitelist)); +} + +bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { + auto raceEID = actorUtils::GetActorRaceEID(actor); + if (whitelist.find(boneName) != whitelist.end()) { + auto racesMap = whitelist.at(boneName); + if (racesMap.find(raceEID) != racesMap.end()) { + if (IsActorMale(actor)) + return racesMap.at(raceEID).male; + else + return racesMap.at(raceEID).female; + } + } + return false; } \ No newline at end of file diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index c2dfdef..f6d6907 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -1,9 +1,12 @@ #pragma once #include "f4se/GameReferences.h" +#include "config.h" namespace actorUtils { - // The statics needs to not be here + std::string GetActorRaceEID(Actor* actor); bool IsActorInPowerArmor(Actor* actor); bool IsActorTorsoArmorEquipped(Actor* actor); bool IsActorMale(Actor* actor); + bool IsActorTrackable(Actor* actor); + bool IsBoneInWhitelist(Actor* actor, std::string boneName); } \ No newline at end of file diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 47f9ec1..84bd29d 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -1,9 +1,14 @@ -#include "SimObj.h" #include "f4se/NiNodes.h" #include "f4se/GameForms.h" #include "f4se/GameRTTI.h" + +#include "ActorUtils.h" +#include "config.h" #include "log.h" #include "PapyrusOCBP.h" +#include "SimObj.h" + +using actorUtils::IsBoneInWhitelist; // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory std::vector boneNames; @@ -31,7 +36,7 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c BSFixedString cs(bone_c_str); auto bone = loadedData->rootNode->GetObjectByName(&cs); if (!bone) { - logger.info("Failed to find Bone %s for actor %08x\n", b.c_str(), actor->formID); + logger.Info("Failed to find Bone %s for actor %08x\n", b.c_str(), actor->formID); } else { //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); things.emplace(b, Thing(bone, cs)); @@ -66,14 +71,17 @@ void SimObj::Update(Actor *actor) { } } } - t.second.update(actor); + + if (!useWhitelist || (IsBoneInWhitelist(actor, t.first) && useWhitelist)) { + t.second.Update(actor); + } } //logger.error("end SimObj update\n"); } bool SimObj::UpdateConfig(config_t & config) { for (auto &thing : things) { - thing.second.updateConfig(config[std::string(thing.first)]); + thing.second.UpdateConfig(config[std::string(thing.first)]); } return true; } \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index de80f71..451be5d 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -18,65 +18,62 @@ std::unordered_map::const_iterator origLocalRot_iter; const char * skeletonNif_boneName = "skeleton.nif"; const char* COM_boneName = "COM"; -void Thing::showPos(NiPoint3& p) { - logger.info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); +void Thing::ShowPos(NiPoint3& p) { + logger.Info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); } -void Thing::showRot(NiMatrix43& r) { - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); - logger.info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); +void Thing::ShowRot(NiMatrix43& r) { + logger.Info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); + logger.Info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); + logger.Info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); } Thing::Thing(NiAVObject* obj, BSFixedString& name) : boneName(name) - , velocity(NiPoint3(0, 0, 0)) -{ + , velocity(NiPoint3(0, 0, 0)) { + + // Set initial positions oldWorldPos = obj->m_worldTransform.pos; - time = clock(); - // Save the bones original local values if it already hasn't + // Save the bones' original local values if they already haven't origLocalPos_iter = origLocalPos.find(boneName.c_str()); origLocalRot_iter = origLocalRot.find(boneName.c_str()); if (origLocalPos_iter == origLocalPos.end()) { - logger.error("for bone %s: ", boneName.c_str()); - logger.error("firstRun pos Set: "); + logger.Error("for bone %s: ", boneName.c_str()); + logger.Error("firstRun pos Set: "); origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); - showPos(obj->m_localTransform.pos); + ShowPos(obj->m_localTransform.pos); } if (origLocalRot_iter == origLocalRot.end()) { - logger.error("for bone %s: ", boneName.c_str()); - logger.error("firstRun rot Set:\n"); + logger.Error("for bone %s: ", boneName.c_str()); + logger.Error("firstRun rot Set:\n"); origLocalRot.emplace(boneName.c_str(), obj->m_localTransform.rot); - showRot(obj->m_localTransform.rot); + ShowRot(obj->m_localTransform.rot); } } Thing::~Thing() { } -float solveQuad(float a, float b, float c) { - float k1 = (-b + sqrtf(b*b - 4*a*c)) / (2 * a); - //float k2 = (-b - sqrtf(b*b - 4*a*c)) / (2 * a); - //logger.error("k2 = %f\n", k2); - return k1; -} - -void Thing::updateConfig(configEntry_t & centry) { +void Thing::UpdateConfig(configEntry_t & centry) { stiffness = centry["stiffness"]; stiffness2 = centry["stiffness2"]; damping = centry["damping"]; + maxOffsetX = centry["maxoffsetX"]; maxOffsetY = centry["maxoffsetY"]; maxOffsetZ = centry["maxoffsetZ"]; + linearX = centry["linearX"]; linearY = centry["linearY"]; linearZ = centry["linearZ"]; + rotationalX = centry["rotationalX"]; rotationalY = centry["rotationalY"]; rotationalZ = centry["rotationalZ"]; + rotateLinearX = centry["rotateLinearX"]; rotateLinearY = centry["rotateLinearY"]; rotateLinearZ = centry["rotateLinearZ"]; @@ -86,10 +83,12 @@ void Thing::updateConfig(configEntry_t & centry) { rotateRotationZ = centry["rotateRotationZ"]; timeTick = centry["timetick"]; + if (centry.find("timeStep") != centry.end()) timeStep = centry["timeStep"]; else timeStep = 0.016f; + gravityBias = centry["gravityBias"]; gravityCorrection = centry["gravityCorrection"]; cogOffsetY = centry["cogOffsetY"]; @@ -99,11 +98,6 @@ void Thing::updateConfig(configEntry_t & centry) { timeTick = 1; absRotX = centry["absRotX"] != 0.0; - absRotY = centry["absRotY"] != 0.0; - absRotZ = centry["absRotZ"] != 0.0; - - //zOffset = solveQuad(stiffness2, stiffness, -gravityBias); - //logger.error("z offset = %f\n", solveQuad(stiffness2, stiffness, -gravityBias)); } static float clamp(float val, float min, float max) { @@ -112,16 +106,16 @@ static float clamp(float val, float min, float max) { return val; } -void Thing::reset(Actor *actor) { +void Thing::Reset(Actor *actor) { auto loadedState = actor->unkF0; if (!loadedState || !loadedState->rootNode) { - logger.error("No loaded state for actor %08x\n", actor->formID); + logger.Error("No loaded state for actor %08x\n", actor->formID); return; } auto obj = loadedState->rootNode->GetObjectByName(&boneName); if (!obj) { - logger.error("Couldn't get name for loaded state for actor %08x\n", actor->formID); + logger.Error("Couldn't get name for loaded state for actor %08x\n", actor->formID); return; } @@ -134,7 +128,28 @@ template int sgn(T val) { return (T(0) < val) - (val < T(0)); } -void Thing::update(Actor *actor) { +NiAVObject* Thing::IsActorValid(Actor* actor) { + auto loadedState = actor->unkF0; + if (!loadedState || !loadedState->rootNode) { + logger.Error("No loaded state for actor %08x\n", actor->formID); + return NULL; + } + auto obj = loadedState->rootNode->GetObjectByName(&boneName); + + if (!obj) { + logger.Error("Couldn't get name for loaded state for actor %08x\n", actor->formID); + return NULL; + } + + if (!obj->m_parent) { + logger.Error("Couldn't get bone %s parent for actor %08x\n", boneName.c_str(), actor->formID); + return NULL; + } + + return obj; +} + +void Thing::Update(Actor *actor) { /*LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; LARGE_INTEGER frequency; @@ -148,20 +163,8 @@ void Thing::update(Actor *actor) { if (deltaT > 64) deltaT = 64; if (deltaT < 8) deltaT = 8; - auto loadedState = actor->unkF0; - if (!loadedState || !loadedState->rootNode) { - logger.error("No loaded state for actor %08x\n", actor->formID); - return; - } - auto obj = loadedState->rootNode->GetObjectByName(&boneName); - + auto obj = IsActorValid(actor); if (!obj) { - logger.error("Couldn't get name for loaded state for actor %08x\n", actor->formID); - return; - } - - if (!obj->m_parent) { - logger.error("Couldn't get bone %s parent for actor %08x\n", boneName.c_str() , actor->formID); return; } @@ -207,7 +210,7 @@ void Thing::update(Actor *actor) { skeletonObj = skeletonObj->m_parent; } if (skeletonFound == false) { - logger.error("Couldn't find skeleton for actor %08x\n", actor->formID); + logger.Error("Couldn't find skeleton for actor %08x\n", actor->formID); return; } #if DEBUG @@ -250,7 +253,7 @@ void Thing::update(Actor *actor) { #endif if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { - logger.error("transform reset\n"); + logger.Error("transform reset\n"); obj->m_localTransform.pos = origLocalPos.at(boneName.c_str()); oldWorldPos = target; velocity = NiPoint3(0, 0, 0); @@ -359,8 +362,6 @@ void Thing::update(Actor *actor) { rotDiff.z *= rotationalZ; if (absRotX) rotDiff.x = fabs(rotDiff.x); - if (absRotY) rotDiff.y = fabs(rotDiff.y); - if (absRotZ) rotDiff.z = fabs(rotDiff.z); #if DEBUG logger.error("localTransform.pos after: "); diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index ff085c8..7a038f3 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -44,16 +44,15 @@ class Thing { float timeStep = 0.016f; bool absRotX = 0; - bool absRotY = 0; - bool absRotZ = 0; Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); - void updateConfig(configEntry_t ¢ry); - void update(Actor * actor); - void reset(Actor * actor); - - void showPos(NiPoint3& p); - void showRot(NiMatrix43& r); + NiAVObject* IsActorValid(Actor* actor); + void Reset(Actor* actor); + void Update(Actor* actor); + void UpdateConfig(configEntry_t& centry); + + void ShowPos(NiPoint3& p); + void ShowRot(NiMatrix43& r); }; \ No newline at end of file diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index d549436..d1103ed 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -5,6 +5,7 @@ #include "Thing.h" #include +#include #include #include #include @@ -22,21 +23,28 @@ bool femaleOnly = false; bool maleOnly = false; bool npcOnly = false; bool detectArmor = false; +bool useWhitelist = false; config_t config; config_t configArmor; configOverrides_t configOverrides; configOverrides_t configArmorOverrides; +// TODO data structure these +whitelist_t whitelist; +std::vector raceWhitelist; + bool LoadConfig() { - logger.info("loadConfig\n"); + logger.Info("loadConfig\n"); + + std::set bonesSet; bool reloadActors = false; auto playerOnlyOld = playerOnly; auto femaleOnlyOld = femaleOnly; auto maleOnlyOld = maleOnly; auto npcOnlyOld = npcOnly; - std::set bonesSet; + auto useWhitelistOld = useWhitelist; boneNames.clear(); config.clear(); @@ -47,38 +55,48 @@ bool LoadConfig() { // Note: Using INIReader results in a slight double read INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); if (configReader.ParseError() < 0) { - logger.error("Can't load 'ocbp.ini'\n"); + logger.Error("Can't load 'ocbp.ini'\n"); } - logger.error("Reading CBP Config\n"); + logger.Error("Reading CBP Config\n"); // Read general settings playerOnly = configReader.GetBoolean("General", "playerOnly", false); - femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); - maleOnly = configReader.GetBoolean("General", "maleOnly", false); - npcOnly = configReader.GetBoolean("General", "npcOnly", false); + npcOnly = configReader.GetBoolean("General", "npcOnly", false); + useWhitelist = configReader.GetBoolean("General", "useWhitelist", false); + + if (useWhitelist) { + maleOnly = false; + femaleOnly = false; + } + else { + femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); + maleOnly = configReader.GetBoolean("General", "maleOnly", false); + + } reloadActors = (playerOnly ^ playerOnlyOld) || (femaleOnly ^ femaleOnlyOld) || (maleOnly ^ maleOnlyOld) || - (npcOnly ^ npcOnlyOld); + (npcOnly ^ npcOnlyOld) || + (useWhitelist ^ useWhitelistOld); detectArmor = configReader.GetBoolean("General", "detectArmor", false); configReloadCount = configReader.GetInteger("Tuning", "rate", 0); // Read sections auto sections = configReader.Sections(); - for (auto sectionIt = sections.begin(); sectionIt != sections.end(); ++sectionIt) { + for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) { // Split for override section check auto overrideStr = std::string("Override:"); - auto splitStr = std::mismatch(overrideStr.begin(), overrideStr.end(), sectionIt->begin()); + auto splitStr = std::mismatch(overrideStr.begin(), overrideStr.end(), sectionsIter->begin()); auto overrideAStr = std::string("Override.A:"); - auto splitAStr = std::mismatch(overrideAStr.begin(), overrideAStr.end(), sectionIt->begin()); + auto splitAStr = std::mismatch(overrideAStr.begin(), overrideAStr.end(), sectionsIter->begin()); - if (*sectionIt == std::string("Attach")) { + if (*sectionsIter == std::string("Attach")) { // Get section contents - auto sectionMap = configReader.Section(*sectionIt); + auto sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& boneName = valuesIter.first; auto& attachName = valuesIter.second; @@ -93,9 +111,9 @@ bool LoadConfig() { } } } - else if (*sectionIt == std::string("Attach.A") && detectArmor) { + else if (*sectionsIter == std::string("Attach.A") && detectArmor) { // Get section contents - auto sectionMap = configReader.Section(*sectionIt); + auto sectionMap = configReader.Section(*sectionsIter); for (auto &valuesIter : sectionMap) { auto &boneName = valuesIter.first; auto &attachName = valuesIter.second; @@ -110,24 +128,61 @@ bool LoadConfig() { } } } + else if (*sectionsIter == std::string("Whitelist") && useWhitelist) { + whitelist.clear(); + raceWhitelist.clear(); + + // Get section contents + auto sectionMap = configReader.Section(*sectionsIter); + for (auto& valuesIter : sectionMap) { + auto& boneName = valuesIter.first; + auto& whitelistName = valuesIter.second; + + size_t commaPos; + do { + commaPos = whitelistName.find_first_of(","); + auto token = whitelistName.substr(0, commaPos); + size_t colonPos = token.find_last_of(":"); + auto raceName = token.substr(0, colonPos); + auto genderStr = token.substr(colonPos + 1); + + if (colonPos == -1) { + whitelist[boneName][token].male = true; + whitelist[boneName][token].female = true; + raceWhitelist.push_back(token); + } + else if (genderStr == "male") { + whitelist[boneName][raceName].male = true; + raceWhitelist.push_back(raceName); + } + else if (genderStr == "female") { + whitelist[boneName][raceName].female = true; + raceWhitelist.push_back(raceName); + } + whitelistName = whitelistName.substr(commaPos + 1); + + //logger.Info(" %s, %s, %d, %d\n", token.c_str(), whitelistName.c_str(), commaPos >= 0, colonPos < 0); + } while (commaPos != -1); + } + } else if (splitStr.first == overrideStr.end()) { // If section name is prefixed with "Override:", grab other half of name for bone - auto boneName = std::string(splitStr.second, sectionIt->end()); + auto boneName = std::string(splitStr.second, sectionsIter->end()); // Get section contents - auto sectionMap = configReader.Section(*sectionIt); + auto sectionMap = configReader.Section(*sectionsIter); for (auto &valuesIt : sectionMap) { - configOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionIt, valuesIt.first, 0.0); + configOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); } } else if (splitAStr.first == overrideAStr.end()) { // If section name is prefixed with "Override:", grab other half of name for bone - auto boneName = std::string(splitAStr.second, sectionIt->end()); + auto boneName = std::string(splitAStr.second, sectionsIter->end()); // Get section contents - auto sectionMap = configReader.Section(*sectionIt); + auto sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIt : sectionMap) { - configArmorOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionIt, valuesIt.first, 0.0); + configArmorOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); } } } @@ -154,26 +209,36 @@ bool LoadConfig() { bonesSet = std::set(boneNames.begin(), boneNames.end()); boneNames.assign(bonesSet.begin(), bonesSet.end()); - logger.error("Finished CBP Config\n"); + logger.Error("Finished CBP Config\n"); return reloadActors; } -void DumpConfigtoLog() +void DumpConfigToLog() { // Log contents of config - logger.info("***** Config Dump *****"); + logger.Info("***** Config Dump *****\n"); for (auto section : config) { - logger.info("[%s]\n", section.first.c_str()); + logger.Info("[%s]\n", section.first.c_str()); for (auto setting : section.second) { - logger.info("%s=%f\n", setting.first.c_str(), setting.second); + logger.Info("%s=%f\n", setting.first.c_str(), setting.second); } } - logger.info("***** ConfigArmor Dump *****"); + logger.Info("***** ConfigArmor Dump *****\n"); for (auto section : configArmor) { - logger.info("[%s]\n", section.first.c_str()); + logger.Info("[%s]\n", section.first.c_str()); + for (auto setting : section.second) { + logger.Info("%s=%f\n", setting.first.c_str(), setting.second); + } + } +} + +void DumpWhitelistToLog() { + logger.Info("***** Whitelist Dump *****\n"); + for (auto section : whitelist) { + logger.Info("[%s]\n", section.first.c_str()); for (auto setting : section.second) { - logger.info("%s=%f\n", setting.first.c_str(), setting.second); + logger.Info("%s= female: %d, male: %d\n", setting.first.c_str(), setting.second.female, setting.second.male); } } } \ No newline at end of file diff --git a/CBPSSE/config.h b/CBPSSE/config.h index ed410d3..1bb8b7a 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -1,22 +1,33 @@ #pragma once #include +#include + #include "f4se/GameReferences.h" class Configuration { }; +struct whitelistSex { + bool male; + bool female; +}; + typedef std::unordered_map configEntry_t; typedef std::unordered_map config_t; typedef std::unordered_map configOverrides_t; +typedef std::unordered_map> whitelist_t; extern bool playerOnly; extern bool femaleOnly; extern bool maleOnly; extern bool npcOnly; extern bool detectArmor; +extern bool useWhitelist; extern int configReloadCount; extern config_t config; extern config_t configArmor; - -bool LoadConfig(); \ No newline at end of file +extern whitelist_t whitelist; +extern std::vector raceWhitelist; +bool LoadConfig(); +void DumpWhitelistToLog(); \ No newline at end of file diff --git a/CBPSSE/hookD3D.cpp b/CBPSSE/hookD3D.cpp index 099555e..81f9471 100644 --- a/CBPSSE/hookD3D.cpp +++ b/CBPSSE/hookD3D.cpp @@ -170,7 +170,7 @@ RelocPtr ProcessEventQueue_Internal(0x0211CF80); DetourXS renderDetour; void DoHook() { - logger.info("Attempting Game Hook\n"); + logger.Info("Attempting Game Hook\n"); // Useful for finding the addresses //CreateThread(NULL, 0, HookCreateFn, NULL, 0, NULL); diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index 1500dbb..af28fb5 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -4,7 +4,7 @@ #pragma warning(disable : 4996) // TODO make better macro -//#define LOG_ON +#define LOG_ON CbpLogger::CbpLogger(const char *fname) { #ifdef LOG_ON @@ -15,7 +15,7 @@ CbpLogger::CbpLogger(const char *fname) { #endif } -void CbpLogger::info(const char *fmt...) { +void CbpLogger::Info(const char *fmt...) { #ifdef LOG_ON if (handle) { va_list argptr; @@ -27,7 +27,7 @@ void CbpLogger::info(const char *fmt...) { #endif } -void CbpLogger::error(const char *fmt...) { +void CbpLogger::Error(const char *fmt...) { #ifdef LOG_ON if (handle) { va_list argptr; diff --git a/CBPSSE/log.h b/CBPSSE/log.h index 0450be3..9dabeda 100644 --- a/CBPSSE/log.h +++ b/CBPSSE/log.h @@ -4,8 +4,8 @@ class CbpLogger { public: CbpLogger(const char* fname); - void info(const char* args...); - void error(const char* args...); + void Info(const char* args...); + void Error(const char* args...); FILE *handle; }; diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index 022657d..0f7e324 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -32,57 +32,57 @@ void MessageHandler(F4SEMessagingInterface::Message * msg) { case F4SEMessagingInterface::kMessage_GameDataReady: { - logger.info("kMessage_GameDataReady\n"); + logger.Info("kMessage_GameDataReady\n"); } break; case F4SEMessagingInterface::kMessage_GameLoaded: { - logger.info("kMessage_GameLoaded\n"); + logger.Info("kMessage_GameLoaded\n"); } break; case F4SEMessagingInterface::kMessage_NewGame: { - logger.info("kMessage_NewGame\n"); + logger.Info("kMessage_NewGame\n"); } break; case F4SEMessagingInterface::kMessage_PreLoadGame: { - logger.info("kMessage_PreLoadGame\n"); + logger.Info("kMessage_PreLoadGame\n"); } break; case F4SEMessagingInterface::kMessage_PostLoad: { - logger.info("kMessage_PostLoad\n"); + logger.Info("kMessage_PostLoad\n"); } break; case F4SEMessagingInterface::kMessage_PostPostLoad: { - logger.info("kMessage_PostPostLoad\n"); + logger.Info("kMessage_PostPostLoad\n"); } break; case F4SEMessagingInterface::kMessage_PostLoadGame: { - logger.info("kMessage_PostLoadGame\n"); + logger.Info("kMessage_PostLoadGame\n"); } break; case F4SEMessagingInterface::kMessage_PreSaveGame: { - logger.info("kMessage_PreSaveGame\n"); + logger.Info("kMessage_PreSaveGame\n"); } break; case F4SEMessagingInterface::kMessage_PostSaveGame: { - logger.info("kMessage_PostSaveGame\n"); + logger.Info("kMessage_PostSaveGame\n"); } break; case F4SEMessagingInterface::kMessage_DeleteGame: { - logger.info("kMessage_DeleteGame\n"); + logger.Info("kMessage_DeleteGame\n"); } break; case F4SEMessagingInterface::kMessage_InputLoaded: { - logger.info("kMessage_InputLoaded\n"); + logger.Info("kMessage_InputLoaded\n"); } break; @@ -95,8 +95,8 @@ extern "C" bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) { - logger.info("CBP Physics F4SE Plugin\n"); - logger.error("Query called\n"); + logger.Info("CBP Physics F4SE Plugin\n"); + logger.Error("Query called\n"); // populate info structure @@ -109,12 +109,12 @@ extern "C" if (f4se->isEditor) { - logger.error("loaded in editor, marking as incompatible\n"); + logger.Error("loaded in editor, marking as incompatible\n"); return false; } else if (f4se->runtimeVersion != RUNTIME_VERSION) { - logger.error("unsupported runtime version %08X", f4se->runtimeVersion); + logger.Error("unsupported runtime version %08X", f4se->runtimeVersion); return false; } // supported runtime version @@ -125,18 +125,18 @@ extern "C" _WARNING("couldn't get papyrus interface"); } - logger.error("Query complete\n"); + logger.Error("Query complete\n"); return true; } bool F4SEPlugin_Load(const F4SEInterface * f4se) { - logger.error("CBP Loading\n"); + logger.Error("CBP Loading\n"); g_task = (F4SETaskInterface *)f4se->QueryInterface(kInterface_Task); if (!g_task) { - logger.error("Couldn't get Task interface\n"); + logger.Error("Couldn't get Task interface\n"); return false; } @@ -144,12 +144,12 @@ extern "C" g_papyrus->Register(RegisterFuncs); // Load initial config before the hook. - logger.error("Loading Config\n"); + logger.Error("Loading Config\n"); LoadConfig(); //g_messagingInterface->RegisterListener(0, "F4SE", MessageHandler); - logger.error("Hooking Game\n"); + logger.Error("Hooking Game\n"); DoHook(); - logger.error("CBP Load Complete\n"); + logger.Error("CBP Load Complete\n"); return true; } }; diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 655ed50..e89b8fc 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -44,13 +44,12 @@ using actorUtils::IsActorMale; using actorUtils::IsActorTorsoArmorEquipped; -using actorUtils::IsActorInPowerArmor; +using actorUtils::IsActorTrackable; extern F4SETaskInterface *g_task; - //void UpdateWorldDataToChild(NiAVObject) -void dumpTransform(NiTransform t) { +void DumpTransform(NiTransform t) { Console_Print("%8.2f %8.2f %8.2f", t.rot.data[0][0], t.rot.data[0][1], t.rot.data[0][2]); Console_Print("%8.2f %8.2f %8.2f", t.rot.data[1][0], t.rot.data[1][1], t.rot.data[1][2]); Console_Print("%8.2f %8.2f %8.2f", t.rot.data[2][0], t.rot.data[2][1], t.rot.data[2][2]); @@ -98,14 +97,6 @@ bool printStuff(NiAVObject *avObj, int depth) { //return false; } - -void dumpVec(NiPoint3 p) { - logger.info("%8.2f %8.2f %8.2f\n", p.x, p.y, p.z); -} - - - - template inline void safe_delete(T*& in) { if (in) { @@ -120,7 +111,7 @@ std::unordered_map actors; TESObjectCELL *curCell = nullptr; -void updateActors() { +void UpdateActors() { //LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; //LARGE_INTEGER frequency; @@ -139,9 +130,10 @@ void updateActors() { if (!cell) goto FAILED; if (cell != curCell) { - logger.error("cell change %d\n", cell); + logger.Error("cell change %d\n", cell); curCell = cell; actors.clear(); + actorEntries.clear(); } else { // Attempt to get cell's objects for (int i = 0; i < cell->objectList.count; i++) { @@ -152,15 +144,11 @@ void updateActors() { if (actor && actor->unkF0) { // Find if actors is already being tracked auto soIt = actors.find(actor->formID); - if (soIt == actors.end() && - IsActorInPowerArmor(actor) && - (!playerOnly || (actor->formID == 0x14 && playerOnly)) && - (!maleOnly || (IsActorMale(actor) && maleOnly)) && - (!femaleOnly || (!IsActorMale(actor) && femaleOnly)) && - (!npcOnly || (actor->formID != 0x14 && npcOnly))) { - logger.info("Tracking Actor with form ID %08x in cell %ld\n", actor->formID, actor->parentCell); - //logger.info("%s\n", actor->unk08-> & 1); - //logger.info("%s\n", actor->race->textureModel[1].GetModelName()); + if (soIt == actors.end() && IsActorTrackable(actor)) { + logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", + actor->formID, actor->parentCell, + actor->race->editorId.c_str(), + IsActorMale(actor)); auto obj = SimObj(actor, config); if (obj.ActorValid(actor)) { actors.emplace(actor->formID, obj); @@ -201,6 +189,7 @@ void updateActors() { // Clear actors if (reloadActors) { actors.clear(); + actorEntries.clear(); } } @@ -215,7 +204,7 @@ void updateActors() { if (simObj.IsBound()) { // need better system for update config if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { - logger.info("torso armor detected on actor %x\n", a.actor->formID); + logger.Info("torso armor detected on actor %x\n", a.actor->formID); simObj.UpdateConfig(configArmor); } else { @@ -225,7 +214,7 @@ void updateActors() { } else { if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { - logger.info("torso armor detected on actor %x\n", a.actor->formID); + logger.Info("torso armor detected on actor %x\n", a.actor->formID); simObj.Bind(a.actor, boneNames, configArmor); } else { @@ -248,7 +237,7 @@ void updateActors() { class ScanDelegate : public ITaskDelegate { public: virtual void Run() { - updateActors(); + UpdateActors(); } virtual void Dispose() { delete this; From de9e321397040253cb2314f807e35380e6aacfd4 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Mon, 17 Feb 2020 03:01:22 -0800 Subject: [PATCH 22/66] Slight change to absRotX calculation. --- CBPSSE/Thing.cpp | 3 ++- CBPSSE/log.cpp | 2 +- CBPSSE/scan.cpp | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 451be5d..13d2457 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -357,11 +357,12 @@ void Thing::Update(Actor *actor) { ); obj->m_localTransform.pos = newLocalPos; + if (absRotX) rotDiff.x = fabs(rotDiff.x); + rotDiff.x *= rotationalX; rotDiff.y *= rotationalY; rotDiff.z *= rotationalZ; - if (absRotX) rotDiff.x = fabs(rotDiff.x); #if DEBUG logger.error("localTransform.pos after: "); diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index af28fb5..21a7a4e 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -4,7 +4,7 @@ #pragma warning(disable : 4996) // TODO make better macro -#define LOG_ON +//#define LOG_ON CbpLogger::CbpLogger(const char *fname) { #ifdef LOG_ON diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index e89b8fc..566195a 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -149,6 +149,7 @@ void UpdateActors() { actor->formID, actor->parentCell, actor->race->editorId.c_str(), IsActorMale(actor)); + // Make SimObj and place new element in Things auto obj = SimObj(actor, config); if (obj.ActorValid(actor)) { actors.emplace(actor->formID, obj); From 6495fe71535a653eb6616f9be62d4f49b26529c4 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Thu, 20 Feb 2020 02:31:20 -0800 Subject: [PATCH 23/66] Scan now is driven by CBPC's method. Reverted maxOffset changes --- CBPSSE/Thing.cpp | 9 ++++---- CBPSSE/hookD3D.cpp | 57 ++++++++++++++++++++++++++++++++-------------- CBPSSE/scan.cpp | 30 ++++++++++++------------ 3 files changed, 59 insertions(+), 37 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 13d2457..804e5ae 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -297,6 +297,10 @@ void Thing::Update(Actor *actor) { // clamp the difference to stop the breast severely lagging at low framerates diff = newPos - target; + diff.x = clamp(diff.x, -maxOffsetX, maxOffsetX); + diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); + diff.z = clamp(diff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; + //oldWorldPos = target + diff; #if DEBUG @@ -319,11 +323,6 @@ void Thing::Update(Actor *actor) { auto localDiff = diff; localDiff = skeletonObj->m_localTransform.rot * localDiff; - - localDiff.x = clamp(localDiff.x, -maxOffsetX, maxOffsetX); - localDiff.y = clamp(localDiff.y, -maxOffsetY, maxOffsetY); - localDiff.z = clamp(localDiff.z, -maxOffsetZ, maxOffsetZ); - localDiff.x *= linearX; localDiff.y *= linearY; localDiff.z *= linearZ; diff --git a/CBPSSE/hookD3D.cpp b/CBPSSE/hookD3D.cpp index 81f9471..9dd7b35 100644 --- a/CBPSSE/hookD3D.cpp +++ b/CBPSSE/hookD3D.cpp @@ -140,6 +140,9 @@ // + +/*//OLD METHOD + typedef UINT64 (__cdecl *renderHook) (void* This, UINT64 arg); renderHook orender = nullptr; @@ -147,34 +150,54 @@ renderHook orender = nullptr; void scaleTest(); UINT64 __cdecl Render(void *This, UINT64 arg) { - //logger.error("This is called\n"); - //logger.error("orender = %016llx\n", orender); - scaleTest(); - return orender(This, arg); - //return 0; +//logger.error("This is called\n"); +//logger.error("orender = %016llx\n", orender); +scaleTest(); +return orender(This, arg); +//return 0; } //RelocPtr render(0xD69720); //RelocPtr main(0x640BC0); // Address copied out of SKSE -//RelocPtr ProcessTasks_HookTarget_Enter(0x005B2EF0); -//RelocPtr ProcessTasks_HookTarget_Enter(0x005B34A0); -//RelocPtr ProcessTasks_HookTarget_Enter(0x005B31E0); -//RelocPtr ProcessTasks_HookTarget_Enter(0x005B31E0); -//RelocPtr ProcessTasks_HookTarget_Enter(0x005B2FF0); -RelocPtr ProcessEventQueue_Internal(0x0211CF80); +//RelocPtr ProcessTasks_HookTarget_Enter(0x005B2EF0); //For SSE 1.5.23 +//RelocPtr ProcessTasks_HookTarget_Enter(0x005B34A0); //For SSE 1.5.39 +//RelocPtr ProcessTasks_HookTarget_Enter(0x005B31E0); //For SSE 1.5.53 +RelocPtr ProcessTasks_HookTarget_Enter(0x005BAB10); //For VR 1.4.15 + +DetourXS renderDetour; +void DoHook() { +logger.info("Attempting Game Hook\n"); +// Useful for finding the addresses +//CreateThread(NULL, 0, HookCreateFn, NULL, 0, NULL); + +//renderDetour.Create(render.GetPtr(), Render, &(LPVOID)orender); +renderDetour.Create(ProcessTasks_HookTarget_Enter.GetPtr(), Render, &(LPVOID)orender); +//orender = (renderHook)renderDetour.GetTrampoline(); +} +*/ +typedef void(*_ProcessEventQueue_Internal) (void * thisPtr); + +_ProcessEventQueue_Internal orig_ProcessEventQueue_Internal = nullptr; + +void UpdateActors(); + +void hk_ProcessEventQueue_Internal(void *thisPtr) +{ + orig_ProcessEventQueue_Internal(thisPtr); + UpdateActors(); +} + +RelocPtr ProcessEventQueue_Internal(0x0211CF80); DetourXS renderDetour; + void DoHook() { - logger.Info("Attempting Game Hook\n"); - // Useful for finding the addresses - //CreateThread(NULL, 0, HookCreateFn, NULL, 0, NULL); + logger.Info("Attempting Game Hook\n"); - //renderDetour.Create(render.GetPtr(), Render, &(LPVOID)orender); - renderDetour.Create((LPVOID)ProcessEventQueue_Internal.GetPtr(), Render, &(LPVOID)orender); - //orender = (renderHook)renderDetour.GetTrampoline(); + renderDetour.Create((LPVOID)ProcessEventQueue_Internal.GetPtr(), hk_ProcessEventQueue_Internal, (LPVOID*)(&orig_ProcessEventQueue_Internal)); } \ No newline at end of file diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 566195a..10a4dd7 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -235,18 +235,18 @@ void UpdateActors() { } -class ScanDelegate : public ITaskDelegate { -public: - virtual void Run() { - UpdateActors(); - } - virtual void Dispose() { - delete this; - } -}; - - -void scaleTest() { - g_task->AddTask(new ScanDelegate()); - return; -} \ No newline at end of file +//class ScanDelegate : public ITaskDelegate { +//public: +// virtual void Run() { +// UpdateActors(); +// } +// virtual void Dispose() { +// delete this; +// } +//}; +// +// +//void scaleTest() { +// g_task->AddTask(new ScanDelegate()); +// return; +//} \ No newline at end of file From 87fa1d380367569e4a0db6d2f38d265975f542b3 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Mon, 2 Mar 2020 02:13:35 -0800 Subject: [PATCH 24/66] Fix bug for actors with unexpected skeletons, added armorIgnore. Some cleanup. --- CBPSSE/ActorUtils.cpp | 14 ++- CBPSSE/Thing.cpp | 240 +++++++++++++++++++++++------------------- CBPSSE/Thing.h | 6 ++ CBPSSE/config.cpp | 21 +++- CBPSSE/config.h | 1 + 5 files changed, 169 insertions(+), 113 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 4e9d50d..6f12488 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -32,11 +32,23 @@ bool actorUtils::IsActorInPowerArmor(Actor* actor) { // May want to change this for any armor bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { bool isEquipped = false; + bool isArmorIgnored = false; + if (!actor) return false; + // 11 IS ARMOR TORSO SLOT (41 minus 30??) isEquipped = actor->equipData->slots[11].item; - return isEquipped; + + // Check if armor is ignored + if (isEquipped) { + auto itemFormID = actor->equipData->slots[11].item->formID; + auto idIter = armorIgnore.find(itemFormID); + if (idIter != armorIgnore.end()) + isArmorIgnored = true; + } + + return isEquipped && !isArmorIgnored; } bool actorUtils::IsActorTrackable(Actor* actor) { diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 804e5ae..312a79a 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -4,19 +4,19 @@ #include constexpr auto DEG_TO_RAD = 3.14159265 / 180; +const char* skeletonNif_boneName = "skeleton.nif"; +const char* COM_boneName = "COM"; // TODO Make these logger macros -//#define DEBUG 0 -//#define TRANSFORM_DEBUG 0 - -std::unordered_map origLocalPos; -std::unordered_map origLocalRot; +//#define DEBUG 1 +//#define TRANSFORM_DEBUG 1 -std::unordered_map::const_iterator origLocalPos_iter; -std::unordered_map::const_iterator origLocalRot_iter; +// > +//pos_map origLocalPos; +//rot_map origLocalRot; -const char * skeletonNif_boneName = "skeleton.nif"; -const char* COM_boneName = "COM"; +pos_map Thing::origLocalPos; +rot_map Thing::origLocalRot; void Thing::ShowPos(NiPoint3& p) { logger.Info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); @@ -35,23 +35,6 @@ Thing::Thing(NiAVObject* obj, BSFixedString& name) // Set initial positions oldWorldPos = obj->m_worldTransform.pos; time = clock(); - - // Save the bones' original local values if they already haven't - origLocalPos_iter = origLocalPos.find(boneName.c_str()); - origLocalRot_iter = origLocalRot.find(boneName.c_str()); - - if (origLocalPos_iter == origLocalPos.end()) { - logger.Error("for bone %s: ", boneName.c_str()); - logger.Error("firstRun pos Set: "); - origLocalPos.emplace(boneName.c_str(), obj->m_localTransform.pos); - ShowPos(obj->m_localTransform.pos); - } - if (origLocalRot_iter == origLocalRot.end()) { - logger.Error("for bone %s: ", boneName.c_str()); - logger.Error("firstRun rot Set:\n"); - origLocalRot.emplace(boneName.c_str(), obj->m_localTransform.rot); - ShowRot(obj->m_localTransform.rot); - } } Thing::~Thing() { @@ -119,8 +102,8 @@ void Thing::Reset(Actor *actor) { return; } - obj->m_localTransform.pos = origLocalPos.at(boneName.c_str()); - obj->m_localTransform.rot = origLocalRot.at(boneName.c_str()); + obj->m_localTransform.pos = origLocalPos[boneName.c_str()][actor->formID]; + obj->m_localTransform.rot = origLocalRot[boneName.c_str()][actor->formID]; } // Returns @@ -172,28 +155,65 @@ void Thing::Update(Actor *actor) { auto sceneObj = obj; while (sceneObj->m_parent && sceneObj->m_name != "skeleton.nif") { - logger.info(sceneObj->m_name); - logger.info("\n---\n"); - logger.error("Actual m_localTransform.pos: "); - showPos(sceneObj->m_localTransform.pos); - logger.error("Actual m_worldTransform.pos: "); - showPos(sceneObj->m_worldTransform.pos); - logger.info("---\n"); - //logger.error("Actual m_localTransform.rot Matrix:\n"); - showRot(sceneObj->m_localTransform.rot); - //logger.error("Actual m_worldTransform.rot Matrix:\n"); - showRot(sceneObj->m_worldTransform.rot); - logger.info("---\n"); + logger.Info(sceneObj->m_name); + logger.Info("\n---\n"); + logger.Error("Actual m_localTransform.pos: "); + ShowPos(sceneObj->m_localTransform.pos); + logger.Error("Actual m_worldTransform.pos: "); + ShowPos(sceneObj->m_worldTransform.pos); + logger.Info("---\n"); + //logger.Error("Actual m_localTransform.rot Matrix:\n"); + ShowRot(sceneObj->m_localTransform.rot); + //logger.Error("Actual m_worldTransform.rot Matrix:\n"); + ShowRot(sceneObj->m_worldTransform.rot); + logger.Info("---\n"); //if (sceneObj->m_parent) { - // logger.error("Calculated m_worldTransform.pos: "); - // showPos((sceneObj->m_parent->m_worldTransform.rot.Transpose() * sceneObj->m_localTransform.pos) + sceneObj->m_parent->m_worldTransform.pos); - // logger.error("Calculated m_worldTransform.rot Matrix:\n"); - // showRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); + // logger.Error("Calculated m_worldTransform.pos: "); + // ShowPos((sceneObj->m_parent->m_worldTransform.rot.Transpose() * sceneObj->m_localTransform.pos) + sceneObj->m_parent->m_worldTransform.pos); + // logger.Error("Calculated m_worldTransform.rot Matrix:\n"); + // ShowRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); //} sceneObj = sceneObj->m_parent; } #endif + // Save the bones' original local values if they already haven't + auto origLocalPos_iter = origLocalPos.find(boneName.c_str()); + auto origLocalRot_iter = origLocalRot.find(boneName.c_str()); + + if (origLocalPos_iter == origLocalPos.end()) { + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun pos Set: \n"); + origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; + ShowPos(obj->m_localTransform.pos); + } + else { + auto actorPosMap = origLocalPos.at(boneName.c_str()); + auto actor_iter = actorPosMap.find(actor->formID); + if (actor_iter == actorPosMap.end()) { + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun pos Set: \n"); + origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; + ShowPos(obj->m_localTransform.pos); + } + } + if (origLocalRot_iter == origLocalRot.end()) { + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun rot Set:\n"); + origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; + ShowRot(obj->m_localTransform.rot); + } + else { + auto actorRotMap = origLocalRot.at(boneName.c_str()); + auto actor_iter = actorRotMap.find(actor->formID); + if (actor_iter == actorRotMap.end()) { + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun rot Set: \n"); + origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; + ShowRot(obj->m_localTransform.rot); + } + } + auto skeletonObj = obj; NiAVObject * comObj; bool skeletonFound = false; @@ -213,32 +233,33 @@ void Thing::Update(Actor *actor) { logger.Error("Couldn't find skeleton for actor %08x\n", actor->formID); return; } + #if DEBUG - logger.error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); - showRot(skeletonObj->m_worldTransform.rot); - //showPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); + logger.Error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); + ShowRot(skeletonObj->m_worldTransform.rot); + //ShowPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif NiMatrix43 targetRot = skeletonObj->m_localTransform.rot.Transpose(); - NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos.at(boneName.c_str())) + obj->m_parent->m_worldTransform.pos; + NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos[boneName.c_str()][actor->formID]) + obj->m_parent->m_worldTransform.pos; // Offset to move Center of Mass make rotational motion more significant NiPoint3 target = (targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; #if DEBUG - logger.error("World Position: "); - showPos(obj->m_worldTransform.pos); - //logger.error("Parent World Position difference: "); - //showPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); - logger.error("Target Rotation * cogOffsetY %8.4f: ", cogOffsetY); - showPos(targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)); - //logger.error("Target Rotation:\n"); - //showRot(targetRot); - logger.error("cogOffset x Transformation:"); - showPos(targetRot * NiPoint3(cogOffsetX, 0, 0)); - logger.error("cogOffset y Transformation:"); - showPos(targetRot * NiPoint3(0, cogOffsetY, 0)); - logger.error("cogOffset z Transformation:"); - showPos(targetRot * NiPoint3(0, 0, cogOffsetZ)); + logger.Error("World Position: "); + ShowPos(obj->m_worldTransform.pos); + //logger.Error("Parent World Position difference: "); + //ShowPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); + logger.Error("Target Rotation * cogOffsetY %8.4f: ", cogOffsetY); + ShowPos(targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)); + //logger.Error("Target Rotation:\n"); + //ShowRot(targetRot); + logger.Error("cogOffset x Transformation:"); + ShowPos(targetRot * NiPoint3(cogOffsetX, 0, 0)); + logger.Error("cogOffset y Transformation:"); + ShowPos(targetRot * NiPoint3(0, cogOffsetY, 0)); + logger.Error("cogOffset z Transformation:"); + ShowPos(targetRot * NiPoint3(0, 0, cogOffsetZ)); #endif // diff is Difference in position between old and new world position @@ -248,13 +269,13 @@ void Thing::Update(Actor *actor) { diff += targetRot * NiPoint3(0, 0, gravityCorrection); #if DEBUG - logger.error("Diff after gravity correction %f: ", gravityCorrection); - showPos(diff); + logger.Error("Diff after gravity correction %f: ", gravityCorrection); + ShowPos(diff); #endif if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { logger.Error("transform reset\n"); - obj->m_localTransform.pos = origLocalPos.at(boneName.c_str()); + obj->m_localTransform.pos = origLocalPos[boneName.c_str()][actor->formID]; oldWorldPos = target; velocity = NiPoint3(0, 0, 0); time = clock(); @@ -268,10 +289,10 @@ void Thing::Update(Actor *actor) { NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - (targetRot * NiPoint3(0, 0, gravityBias)); #if DEBUG - logger.error("Diff2: "); - showPos(diff2); - logger.error("Force with stiffness %f, stiffness2 %f, gravity bias %f: ", stiffness, stiffness2, gravityBias); - showPos(force); + logger.Error("Diff2: "); + ShowPos(diff2); + logger.Error("Force with stiffness %f, stiffness2 %f, gravity bias %f: ", stiffness, stiffness2, gravityBias); + ShowPos(force); #endif do { @@ -284,15 +305,15 @@ void Thing::Update(Actor *actor) { deltaT -= timeTick; } while (deltaT >= timeTick); - NiPoint3 newPos = oldWorldPos +posDelta; + NiPoint3 newPos = oldWorldPos + posDelta; oldWorldPos = diff + target; #if DEBUG - //logger.error("posDelta: "); - //showPos(posDelta); - logger.error("newPos: "); - showPos(newPos); + //logger.Error("posDelta: "); + //ShowPos(posDelta); + logger.Error("newPos: "); + ShowPos(newPos); #endif // clamp the difference to stop the breast severely lagging at low framerates diff = newPos - target; @@ -301,25 +322,22 @@ void Thing::Update(Actor *actor) { diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); diff.z = clamp(diff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; - //oldWorldPos = target + diff; - #if DEBUG - logger.error("diff from newPos: "); - showPos(diff); - //logger.error("oldWorldPos: "); - //showPos(oldWorldPos); + logger.Error("diff from newPos: "); + ShowPos(diff); + //logger.Error("oldWorldPos: "); + //ShowPos(oldWorldPos); #endif // move the bones based on the supplied weightings // Convert the world translations into local coordinates - NiMatrix43 invRot; NiMatrix43 rotateLinear; - rotateLinear.SetEulerAngles(rotateLinearX * DEG_TO_RAD, - rotateLinearY * DEG_TO_RAD, - rotateLinearZ * DEG_TO_RAD); + rotateLinear.SetEulerAngles(rotateLinearX* DEG_TO_RAD, + rotateLinearY* DEG_TO_RAD, + rotateLinearZ* DEG_TO_RAD); - invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; + NiMatrix43 invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; auto localDiff = diff; localDiff = skeletonObj->m_localTransform.rot * localDiff; @@ -333,26 +351,26 @@ void Thing::Update(Actor *actor) { localDiff = invRot * localDiff; oldWorldPos = diff + target; #if DEBUG - logger.error("invRot x=10 Transformation:"); - showPos(invRot * NiPoint3(10, 0, 0)); - logger.error("invRot y=10 Transformation:"); - showPos(invRot * NiPoint3(0, 10, 0)); - logger.error("invRot z=10 Transformation:"); - showPos(invRot * NiPoint3(0, 0, 10)); - logger.error("oldWorldPos: "); - showPos(oldWorldPos); - logger.error("localTransform.pos: "); - showPos(obj->m_localTransform.pos); - logger.error("localDiff: "); - showPos(localDiff); - logger.error("rotDiff: "); - showPos(rotDiff); + logger.Error("invRot x=10 Transformation:"); + ShowPos(invRot * NiPoint3(10, 0, 0)); + logger.Error("invRot y=10 Transformation:"); + ShowPos(invRot * NiPoint3(0, 10, 0)); + logger.Error("invRot z=10 Transformation:"); + ShowPos(invRot * NiPoint3(0, 0, 10)); + logger.Error("oldWorldPos: "); + ShowPos(oldWorldPos); + logger.Error("localTransform.pos: "); + ShowPos(obj->m_localTransform.pos); + logger.Error("localDiff: "); + ShowPos(localDiff); + logger.Error("rotDiff: "); + ShowPos(rotDiff); #endif // scale positions from config - NiPoint3 newLocalPos = NiPoint3((localDiff.x) + origLocalPos.at(boneName.c_str()).x, - (localDiff.y) + origLocalPos.at(boneName.c_str()).y, - (localDiff.z) + origLocalPos.at(boneName.c_str()).z + NiPoint3 newLocalPos = NiPoint3((localDiff.x) + origLocalPos[boneName.c_str()][actor->formID].x, + (localDiff.y) + origLocalPos[boneName.c_str()][actor->formID].y, + (localDiff.z) + origLocalPos[boneName.c_str()][actor->formID].z ); obj->m_localTransform.pos = newLocalPos; @@ -364,12 +382,12 @@ void Thing::Update(Actor *actor) { #if DEBUG - logger.error("localTransform.pos after: "); - showPos(obj->m_localTransform.pos); - logger.error("origLocalPos:"); - showPos(origLocalPos.at(boneName.c_str())); - logger.error("origLocalRot:"); - showRot(origLocalRot.at(boneName.c_str())); + logger.Error("localTransform.pos after: "); + ShowPos(obj->m_localTransform.pos); + logger.Error("origLocalPos:"); + ShowPos(origLocalPos[boneName.c_str()][actor->formID]); + logger.Error("origLocalRot:"); + ShowRot(origLocalRot[boneName.c_str()][actor->formID]); #endif // Do rotation. @@ -382,13 +400,13 @@ void Thing::Update(Actor *actor) { rotDiff = rotateRotation * rotDiff; standardRot.SetEulerAngles(rotDiff.x, rotDiff.y, rotDiff.z); - obj->m_localTransform.rot = standardRot * origLocalRot.at(boneName.c_str()); + obj->m_localTransform.rot = standardRot * origLocalRot[boneName.c_str()][actor->formID]; } #if DEBUG - logger.error("end update()\n"); + logger.Error("end update()\n"); #endif - //logger.error("end update()\n"); + //logger.Error("end update()\n"); /*QueryPerformanceCounter(&endingTime); elapsedMicroseconds.QuadPart = endingTime.QuadPart - startingTime.QuadPart; elapsedMicroseconds.QuadPart *= 1000000000LL; diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 7a038f3..aed9f9c 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -5,6 +5,9 @@ #include #include "config.h" +typedef std::unordered_map> pos_map; +typedef std::unordered_map> rot_map; + class Thing { BSFixedString boneName; NiPoint3 oldWorldPos; @@ -45,6 +48,9 @@ class Thing { bool absRotX = 0; + static pos_map origLocalPos; + static rot_map origLocalRot; + Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index d1103ed..f25ea7a 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -33,6 +34,7 @@ configOverrides_t configArmorOverrides; // TODO data structure these whitelist_t whitelist; std::vector raceWhitelist; +std::unordered_map armorIgnore; bool LoadConfig() { logger.Info("loadConfig\n"); @@ -71,7 +73,6 @@ bool LoadConfig() { else { femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); maleOnly = configReader.GetBoolean("General", "maleOnly", false); - } reloadActors = (playerOnly ^ playerOnlyOld) || @@ -83,6 +84,24 @@ bool LoadConfig() { detectArmor = configReader.GetBoolean("General", "detectArmor", false); configReloadCount = configReader.GetInteger("Tuning", "rate", 0); + //Read armorIgnore + auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); + { + size_t commaPos; + do { + commaPos = armorIgnoreStr.find_first_of(","); + auto token = armorIgnoreStr.substr(0, commaPos); + UInt32 formID; + std::stringstream ss; + ss << std::hex << token; + ss >> formID; + armorIgnore[formID] = true; + armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); + + //logger.Info(" %s, %s, %d, %d\n", token.c_str(), whitelistName.c_str(), commaPos >= 0, colonPos < 0); + } while (commaPos != -1); + } + // Read sections auto sections = configReader.Sections(); for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) { diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 1bb8b7a..60ed75b 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -29,5 +29,6 @@ extern config_t config; extern config_t configArmor; extern whitelist_t whitelist; extern std::vector raceWhitelist; +extern std::unordered_map armorIgnore; bool LoadConfig(); void DumpWhitelistToLog(); \ No newline at end of file From f44f115b99e7f797cd101f081fd2a72f7988d069 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Mon, 2 Mar 2020 22:05:32 -0800 Subject: [PATCH 25/66] Clear armorIgnore when reloading config --- CBPSSE/config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index f25ea7a..1a5acaa 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -53,6 +53,7 @@ bool LoadConfig() { configArmor.clear(); configOverrides.clear(); configArmorOverrides.clear(); + armorIgnore.clear(); // Note: Using INIReader results in a slight double read INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); From 9dc35166ee7221def232f851b3e7c52f3139ea1f Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Tue, 3 Mar 2020 01:33:49 -0800 Subject: [PATCH 26/66] Fix for armorIgnore --- CBPSSE/ActorUtils.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 6f12488..4cd5413 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -42,10 +42,21 @@ bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { // Check if armor is ignored if (isEquipped) { - auto itemFormID = actor->equipData->slots[11].item->formID; - auto idIter = armorIgnore.find(itemFormID); - if (idIter != armorIgnore.end()) + auto torsoFormID = actor->equipData->slots[11].item->formID; + auto bodyFormID = actor->equipData->slots[3].item->formID; + + //logger.Info("torsoFormID: 0x%08x\n", torsoFormID); + //logger.Info("bodyFormID: 0x%08x\n", bodyFormID); + + auto torsoIdIter = armorIgnore.find(torsoFormID); + + if (torsoIdIter != armorIgnore.end()) isArmorIgnored = true; + else if ((torsoFormID == bodyFormID - 1)) { + auto bodyIdIter = armorIgnore.find(bodyFormID); + if (bodyIdIter != armorIgnore.end()) + isArmorIgnored = true; + } } return isEquipped && !isArmorIgnored; From 094fcc615c4976403d6bb9f842a988374d72887a Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Wed, 4 Mar 2020 02:20:17 -0800 Subject: [PATCH 27/66] Some stability changes for power armor --- CBPSSE/ActorUtils.cpp | 27 ++++++++++++++++++--------- CBPSSE/SimObj.cpp | 7 ++++++- CBPSSE/Thing.cpp | 14 +++++++++----- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 4cd5413..d89e869 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -24,9 +24,19 @@ bool actorUtils::IsActorMale(Actor *actor) } bool actorUtils::IsActorInPowerArmor(Actor* actor) { - if (!actor) - return false; - return !actor->extraDataList->HasType(kExtraData_PowerArmor); + bool isInPowerArmor = false; + if (!actor) { + logger.Info("IsActorInPowerArmor: no actor!\n"); + return true; // will force game to not call Update + } + if (!actor->extraDataList) { + logger.Info("IsActorInPowerArmor: no extraDataList!\n"); + return true; // will force game to not call Update + } + + isInPowerArmor = actor->extraDataList->HasType(kExtraData_PowerArmor); + //logger.Info("is in power armor: %d\n", isInPowerArmor); + return isInPowerArmor; } // May want to change this for any armor @@ -64,12 +74,11 @@ bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { bool actorUtils::IsActorTrackable(Actor* actor) { bool inRaceWhitelist = find(raceWhitelist.begin(), raceWhitelist.end(), actorUtils::GetActorRaceEID(actor)) != raceWhitelist.end(); - return IsActorInPowerArmor(actor) && - (!playerOnly || (actor->formID == 0x14 && playerOnly)) && - (!maleOnly || (IsActorMale(actor) && maleOnly)) && - (!femaleOnly || (!IsActorMale(actor) && femaleOnly)) && - (!npcOnly || (actor->formID != 0x14 && npcOnly)) && - (!useWhitelist || (inRaceWhitelist && useWhitelist)); + return (!playerOnly || (actor->formID == 0x14 && playerOnly)) && + (!maleOnly || (IsActorMale(actor) && maleOnly)) && + (!femaleOnly || (!IsActorMale(actor) && femaleOnly)) && + (!npcOnly || (actor->formID != 0x14 && npcOnly)) && + (!useWhitelist || (inRaceWhitelist && useWhitelist)); } bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 84bd29d..1c848a5 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -9,6 +9,7 @@ #include "SimObj.h" using actorUtils::IsBoneInWhitelist; +using actorUtils::IsActorInPowerArmor; // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory std::vector boneNames; @@ -49,6 +50,8 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c } bool SimObj::ActorValid(Actor *actor) { + if (!actor) + return false; if (actor->flags & TESForm::kFlag_IsDeleted) return false; if (actor && actor->unkF0 && actor->unkF0->rootNode) @@ -72,7 +75,9 @@ void SimObj::Update(Actor *actor) { } } - if (!useWhitelist || (IsBoneInWhitelist(actor, t.first) && useWhitelist)) { + if (!useWhitelist || (IsBoneInWhitelist(actor, t.first) && useWhitelist) && + !IsActorInPowerArmor(actor)) + { t.second.Update(actor); } } diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 312a79a..1c76f92 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -112,6 +112,10 @@ template int sgn(T val) { } NiAVObject* Thing::IsActorValid(Actor* actor) { + if (!actor) { + logger.Error("No actor in Thing::Update\n"); + return NULL; + } auto loadedState = actor->unkF0; if (!loadedState || !loadedState->rootNode) { logger.Error("No loaded state for actor %08x\n", actor->formID); @@ -139,17 +143,17 @@ void Thing::Update(Actor *actor) { QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&startingTime);*/ + auto obj = IsActorValid(actor); + if (!obj) { + return; + } + auto newTime = clock(); auto deltaT = newTime - time; time = newTime; if (deltaT > 64) deltaT = 64; if (deltaT < 8) deltaT = 8; - - auto obj = IsActorValid(actor); - if (!obj) { - return; - } #if TRANSFORM_DEBUG auto sceneObj = obj; From 76991a4c9c8bef74452262953eef6bba23fa9bb0 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Fri, 6 Mar 2020 02:33:46 -0800 Subject: [PATCH 28/66] Stability checks added. Slight refactoring. --- CBPSSE/ActorUtils.cpp | 62 ++++++++++++++++++++++++++++++++++++++++--- CBPSSE/ActorUtils.h | 1 + CBPSSE/SimObj.cpp | 10 ------- CBPSSE/SimObj.h | 1 - CBPSSE/Thing.cpp | 5 ++-- CBPSSE/scan.cpp | 13 ++++----- 6 files changed, 69 insertions(+), 23 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index d89e869..7213f0f 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -8,11 +8,21 @@ #include "f4se/GameRTTI.h" std::string actorUtils::GetActorRaceEID(Actor* actor) { + if (!IsActorValid(actor)) { + logger.Info("GetActorRaceEID: no actor!\n"); + return ""; + } + return std::string(actor->race->editorId.c_str()); } bool actorUtils::IsActorMale(Actor *actor) { + if (!IsActorValid(actor)) { + logger.Info("IsActorMale: no actor!\n"); + return false; + } + TESNPC* actorNPC = DYNAMIC_CAST(actor->baseForm, TESForm, TESNPC); auto npcSex = actorNPC ? CALL_MEMBER_FN(actorNPC, GetSex)() : 1; @@ -25,7 +35,7 @@ bool actorUtils::IsActorMale(Actor *actor) bool actorUtils::IsActorInPowerArmor(Actor* actor) { bool isInPowerArmor = false; - if (!actor) { + if (!IsActorValid(actor)) { logger.Info("IsActorInPowerArmor: no actor!\n"); return true; // will force game to not call Update } @@ -44,16 +54,34 @@ bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { bool isEquipped = false; bool isArmorIgnored = false; - if (!actor) + if (!IsActorValid(actor)) { + logger.Info("Actor is not valid\n"); + return false; + } + if (!actor->equipData && !actor->equipData->slots) { + logger.Info("Actor has no equipData\n"); return false; + } // 11 IS ARMOR TORSO SLOT (41 minus 30??) isEquipped = actor->equipData->slots[11].item; // Check if armor is ignored if (isEquipped) { + if (!actor->equipData->slots[11].item) { + logger.Info("slot 11 item check failed.\n"); + // redundant check but just in case + return false; + } + UInt32 bodyFormID; + if (!actor->equipData->slots[3].item) { + logger.Info("slot 3 item check failed.\n"); + bodyFormID = 0; + } + else { + bodyFormID = actor->equipData->slots[3].item->formID;; + } auto torsoFormID = actor->equipData->slots[11].item->formID; - auto bodyFormID = actor->equipData->slots[3].item->formID; //logger.Info("torsoFormID: 0x%08x\n", torsoFormID); //logger.Info("bodyFormID: 0x%08x\n", bodyFormID); @@ -73,6 +101,11 @@ bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { } bool actorUtils::IsActorTrackable(Actor* actor) { + if (!IsActorValid(actor)) { + logger.Info("IsActorTrackable: actor is not valid.\n"); + return false; + } + bool inRaceWhitelist = find(raceWhitelist.begin(), raceWhitelist.end(), actorUtils::GetActorRaceEID(actor)) != raceWhitelist.end(); return (!playerOnly || (actor->formID == 0x14 && playerOnly)) && (!maleOnly || (IsActorMale(actor) && maleOnly)) && @@ -81,7 +114,28 @@ bool actorUtils::IsActorTrackable(Actor* actor) { (!useWhitelist || (inRaceWhitelist && useWhitelist)); } +bool actorUtils::IsActorValid(Actor* actor) { + if (!actor) { + logger.Info("IsActorValid: actor is null\n"); + return false; + } + if (actor->flags & TESForm::kFlag_IsDeleted) { + logger.Info("IsActorValid: actor has deleted flag\n"); + return false; + } + if (actor && actor->unkF0 && actor->unkF0->rootNode) { + return true; + } + logger.Info("IsActorValid: actor is not valid state\n"); + return false; +} + bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { + if (!IsActorValid(actor)) { + logger.Info("IsBoneInWhitelist: actor is not valid.\n"); + return false; + } + auto raceEID = actorUtils::GetActorRaceEID(actor); if (whitelist.find(boneName) != whitelist.end()) { auto racesMap = whitelist.at(boneName); @@ -93,4 +147,4 @@ bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { } } return false; -} \ No newline at end of file +} diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index f6d6907..c5091c3 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -8,5 +8,6 @@ namespace actorUtils { bool IsActorTorsoArmorEquipped(Actor* actor); bool IsActorMale(Actor* actor); bool IsActorTrackable(Actor* actor); + bool IsActorValid(Actor* actor); bool IsBoneInWhitelist(Actor* actor, std::string boneName); } \ No newline at end of file diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 1c848a5..39bb0b8 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -49,16 +49,6 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c return false; } -bool SimObj::ActorValid(Actor *actor) { - if (!actor) - return false; - if (actor->flags & TESForm::kFlag_IsDeleted) - return false; - if (actor && actor->unkF0 && actor->unkF0->rootNode) - return true; - return false; -} - void SimObj::Update(Actor *actor) { if (!bound) return; diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index b32ae66..c5ac424 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -15,7 +15,6 @@ class SimObj { SimObj() {} ~SimObj(); bool Bind(Actor *actor, std::vector &boneNames, config_t &config); - bool ActorValid(Actor *actor); void Update(Actor *actor); bool UpdateConfig(config_t &config); bool IsBound() { return bound; } diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 1c76f92..bcd46a9 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -1,3 +1,4 @@ +#include "ActorUtils.h" #include "Thing.h" #include "log.h" #include "f4se\NiNodes.h" @@ -112,8 +113,8 @@ template int sgn(T val) { } NiAVObject* Thing::IsActorValid(Actor* actor) { - if (!actor) { - logger.Error("No actor in Thing::Update\n"); + if (!actorUtils::IsActorValid(actor)) { + logger.Error("No valid actor in Thing::Update\n"); return NULL; } auto loadedState = actor->unkF0; diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 10a4dd7..1091efd 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -45,6 +45,7 @@ using actorUtils::IsActorMale; using actorUtils::IsActorTorsoArmorEquipped; using actorUtils::IsActorTrackable; +using actorUtils::IsActorValid; extern F4SETaskInterface *g_task; @@ -145,18 +146,18 @@ void UpdateActors() { // Find if actors is already being tracked auto soIt = actors.find(actor->formID); if (soIt == actors.end() && IsActorTrackable(actor)) { - logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", - actor->formID, actor->parentCell, - actor->race->editorId.c_str(), - IsActorMale(actor)); + //logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", + // actor->formID, actor->parentCell, + // actor->race->editorId.c_str(), + // IsActorMale(actor)); // Make SimObj and place new element in Things auto obj = SimObj(actor, config); - if (obj.ActorValid(actor)) { + if (IsActorValid(actor)) { actors.emplace(actor->formID, obj); actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } - else if (soIt->second.ActorValid(actor)) { + else if (IsActorValid(actor)) { actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } From d860798846b95a36f2b7d51a721ec0d25b8a25ab Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Tue, 10 Mar 2020 01:43:37 -0700 Subject: [PATCH 29/66] Fix for teleporting to different with different bone than CBP config --- CBPSSE/SimObj.cpp | 33 +++++++++++++++++++++++++++++---- CBPSSE/SimObj.h | 2 +- CBPSSE/scan.cpp | 18 ++++++++++++------ 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 39bb0b8..8f7e747 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -27,6 +27,9 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c { // logger.error("bind\n"); + if (!actor) { + return false; + } auto loadedData = actor->unkF0; if (loadedData && loadedData->rootNode) { bound = true; @@ -43,7 +46,7 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c things.emplace(b, Thing(bone, cs)); } } - UpdateConfig(config); + UpdateConfig(actor, std::vector(), config); return true; } return false; @@ -52,9 +55,10 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c void SimObj::Update(Actor *actor) { if (!bound) return; - //logger.error("update\n"); + logger.Error("SimObj::Update\n"); for (auto &t : things) { - + logger.Info("SimObj update: doing thing %s\n", t.first.c_str()); + // Might be a better way to do this if (boneIgnores.find(actor->formID) != boneIgnores.end()) { auto actorBoneMap = boneIgnores.at(actor->formID); @@ -68,13 +72,34 @@ void SimObj::Update(Actor *actor) { if (!useWhitelist || (IsBoneInWhitelist(actor, t.first) && useWhitelist) && !IsActorInPowerArmor(actor)) { + logger.Error("SimObj::Update - calling Thing::Update\n"); t.second.Update(actor); } } //logger.error("end SimObj update\n"); } -bool SimObj::UpdateConfig(config_t & config) { +bool SimObj::UpdateConfig(Actor* actor, std::vector& boneNames, config_t& config) { + logger.Error("SimObj::UpdateConfig\n"); + if (!actor) { + return false; + } + auto loadedData = actor->unkF0; + if (loadedData && loadedData->rootNode) { + for (std::string b : boneNames) { + logger.Error("SimObj::UpdateConfig - adding bone %s\n", b.c_str()); + BSFixedString cs(b.c_str()); + auto bone = loadedData->rootNode->GetObjectByName(&cs); + auto findBone = things.find(b); + if (!bone) { + logger.Info("Failed to find Bone %s for actor %08x\n", b.c_str(), actor->formID); + } + else if (findBone == things.end()) { + //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); + things.emplace(b, Thing(bone, cs)); + } + } + } for (auto &thing : things) { thing.second.UpdateConfig(config[std::string(thing.first)]); } diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index c5ac424..a5a04fd 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -16,7 +16,7 @@ class SimObj { ~SimObj(); bool Bind(Actor *actor, std::vector &boneNames, config_t &config); void Update(Actor *actor); - bool UpdateConfig(config_t &config); + bool UpdateConfig(Actor* actor, std::vector& boneNames, config_t& config); bool IsBound() { return bound; } }; diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 1091efd..dab818f 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -131,7 +131,7 @@ void UpdateActors() { if (!cell) goto FAILED; if (cell != curCell) { - logger.Error("cell change %d\n", cell); + logger.Error("cell change %d\n", cell->formID); curCell = cell; actors.clear(); actorEntries.clear(); @@ -147,7 +147,7 @@ void UpdateActors() { auto soIt = actors.find(actor->formID); if (soIt == actors.end() && IsActorTrackable(actor)) { //logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", - // actor->formID, actor->parentCell, + // actor->formID, actor->parentCell->formID, // actor->race->editorId.c_str(), // IsActorMale(actor)); // Make SimObj and place new element in Things @@ -184,8 +184,14 @@ void UpdateActors() { if (configReloadCount && count++ > configReloadCount) { count = 0; auto reloadActors = LoadConfig(); - for (auto &a : actors) { - a.second.UpdateConfig(config); + for (auto& a : actorEntries) { + auto objIterator = actors.find(a.id); + if (objIterator == actors.end()) { + //logger.error("Sim Object not found in tracked actors\n"); + } + else { + objIterator->second.UpdateConfig(a.actor, boneNames, config); + } } // Clear actors @@ -207,10 +213,10 @@ void UpdateActors() { // need better system for update config if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { logger.Info("torso armor detected on actor %x\n", a.actor->formID); - simObj.UpdateConfig(configArmor); + simObj.UpdateConfig(a.actor, boneNames, configArmor); } else { - simObj.UpdateConfig(config); + simObj.UpdateConfig(a.actor, boneNames, config); } simObj.Update(a.actor); } From 345f864fa92d485b42e8d05c35a061f42d42ac1e Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Wed, 8 Apr 2020 22:40:55 -0700 Subject: [PATCH 30/66] f4se 0.6.21 updates --- f4se/f4se/PapyrusGame.cpp | 2 +- f4se/f4se_common/f4se_version.h | 10 +++++----- f4se/f4se_readme.txt | 2 +- f4se/f4se_whatsnew.txt | 3 +++ 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/f4se/f4se/PapyrusGame.cpp b/f4se/f4se/PapyrusGame.cpp index 530f1ee..50e7fb6 100644 --- a/f4se/f4se/PapyrusGame.cpp +++ b/f4se/f4se/PapyrusGame.cpp @@ -49,7 +49,7 @@ namespace papyrusGame VMArray GetInstalledLightPlugins(StaticFunctionTag * base) { VMArray result; - UInt8 modCount = (*g_dataHandler)->modList.lightMods.count; + UInt32 modCount = (*g_dataHandler)->modList.lightMods.count; for (UInt32 i = 0; i < modCount; i++) { ModInfo * modInfo = (*g_dataHandler)->modList.lightMods[i]; diff --git a/f4se/f4se_common/f4se_version.h b/f4se/f4se_common/f4se_version.h index f6b60e6..b4e70e5 100644 --- a/f4se/f4se_common/f4se_version.h +++ b/f4se/f4se_common/f4se_version.h @@ -4,10 +4,10 @@ // these have to be macros so they can be used in the .rc #define F4SE_VERSION_INTEGER 0 #define F4SE_VERSION_INTEGER_MINOR 6 -#define F4SE_VERSION_INTEGER_BETA 20 -#define F4SE_VERSION_VERSTRING "0, 0, 6, 20" -#define F4SE_VERSION_PADDEDSTRING "0022" -#define F4SE_VERSION_RELEASEIDX 22 +#define F4SE_VERSION_INTEGER_BETA 21 +#define F4SE_VERSION_VERSTRING "0, 0, 6, 21" +#define F4SE_VERSION_PADDEDSTRING "0023" +#define F4SE_VERSION_RELEASEIDX 23 #define MAKE_EXE_VERSION_EX(major, minor, build, sub) ((((major) & 0xFF) << 24) | (((minor) & 0xFF) << 16) | (((build) & 0xFFF) << 4) | ((sub) & 0xF)) #define MAKE_EXE_VERSION(major, minor, build) MAKE_EXE_VERSION_EX(major, minor, build, 0) @@ -73,6 +73,6 @@ // information about the state of the game at the time of release #define F4SE_TARGETING_BETA_VERSION 0 #define CURRENT_RELEASE_RUNTIME RUNTIME_VERSION_1_10_163 -#define CURRENT_RELEASE_F4SE_STR "0.6.20" +#define CURRENT_RELEASE_F4SE_STR "0.6.21" #endif /* __F4SE_VERSION_H__ */ diff --git a/f4se/f4se_readme.txt b/f4se/f4se_readme.txt index d3cc759..173e82e 100644 --- a/f4se/f4se_readme.txt +++ b/f4se/f4se_readme.txt @@ -1,4 +1,4 @@ -Fallout 4 Script Extender v0.6.20 +Fallout 4 Script Extender v0.6.21 by Ian Patterson, Stephen Abel, and Brendan Borthwick (ianpatt, behippo, and plb) The Fallout 4 Script Extender, or F4SE for short, is a modder's resource that expands the scripting capabilities of Fallout 4. It does so without modifying the executable files on disk, so there are no permanent side effects. diff --git a/f4se/f4se_whatsnew.txt b/f4se/f4se_whatsnew.txt index 1f82c1e..a557a12 100644 --- a/f4se/f4se_whatsnew.txt +++ b/f4se/f4se_whatsnew.txt @@ -1,3 +1,6 @@ +0.6.21 +- fix Game.GetInstalledLightPlugins when more than 255 plugins are installed + 0.6.20 - support for runtime 1.10.163 From 441f6a95c8f58a63f4964dabe574bb3c1c6e8967 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Mon, 13 Apr 2020 00:21:49 -0700 Subject: [PATCH 31/66] Change plugin name. --- CBPSSE/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index 0f7e324..b1e3082 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -95,13 +95,13 @@ extern "C" bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) { - logger.Info("CBP Physics F4SE Plugin\n"); + logger.Info("OCBP Physics F4SE Plugin\n"); logger.Error("Query called\n"); // populate info structure info->infoVersion = PluginInfo::kInfoVersion; - info->name = "CBP plugin"; + info->name = "OCBP plugin"; info->version = 24; // store plugin handle so we can identify ourselves later From eb7b34d9912e4437b8036463cd184fce00f1b56a Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Mon, 14 Jun 2021 04:48:37 -0700 Subject: [PATCH 32/66] Added position offset and scalar multiplier. --- CBPSSE/Thing.cpp | 19 +++++++++++++++---- CBPSSE/Thing.h | 7 +++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index bcd46a9..763cf55 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -4,7 +4,7 @@ #include "f4se\NiNodes.h" #include -constexpr auto DEG_TO_RAD = 3.14159265 / 180; +constexpr float DEG_TO_RAD = 3.14159265 / 180; const char* skeletonNif_boneName = "skeleton.nif"; const char* COM_boneName = "COM"; @@ -66,6 +66,13 @@ void Thing::UpdateConfig(configEntry_t & centry) { rotateRotationY = centry["rotateRotationY"]; rotateRotationZ = centry["rotateRotationZ"]; + scaleMultiplierX = 1.0f;// centry["scaleMultiplierX"]; + scaleMultiplierY = 1.0f;// centry["scaleMultiplierY"]; + scaleMultiplierZ = 1.0f;// centry["scaleMultiplierZ"]; + + posOffsetX = centry["posOffsetX"]; + posOffsetY = centry["posOffsetY"]; + timeTick = centry["timetick"]; if (centry.find("timeStep") != centry.end()) @@ -346,9 +353,13 @@ void Thing::Update(Actor *actor) { auto localDiff = diff; localDiff = skeletonObj->m_localTransform.rot * localDiff; - localDiff.x *= linearX; - localDiff.y *= linearY; - localDiff.z *= linearZ; + + localDiff.x = clamp(localDiff.x - posOffsetX, -maxOffsetX, maxOffsetX) + posOffsetX; + localDiff.y = clamp(localDiff.y - posOffsetY, -maxOffsetY, maxOffsetY) + posOffsetY; + + localDiff.x *= linearX * scaleMultiplierX; + localDiff.y *= linearY * scaleMultiplierY; + localDiff.z *= linearZ * scaleMultiplierZ; auto rotDiff = localDiff; localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index aed9f9c..d4b3771 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -44,6 +44,13 @@ class Thing { float rotateRotationY = 0.0f; float rotateRotationZ = 0.0f; + float scaleMultiplierX = 1.0f; + float scaleMultiplierY = 1.0f; + float scaleMultiplierZ = 1.0f; + + float posOffsetX = 0.0f; + float posOffsetY = 0.0f; + float timeStep = 0.016f; bool absRotX = 0; From c69c98ab3534c22912a1499d53c7ca2044d6b419 Mon Sep 17 00:00:00 2001 From: ericncream Date: Wed, 21 Jul 2021 02:58:23 -0700 Subject: [PATCH 33/66] Update LICENSE --- LICENSE | 695 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 674 insertions(+), 21 deletions(-) diff --git a/LICENSE b/LICENSE index 3312f1f..f288702 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,674 @@ -MIT License - -Copyright (c) 2018 - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 34fba5b28b3165549f7296f360e78db2408d2264 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 23 Jun 2022 21:07:50 -0700 Subject: [PATCH 34/66] Priority-based override system for bone attaches based on armor equipped by actor --- CBPSSE/ActorUtils.cpp | 134 +++++++++++++++--------- CBPSSE/ActorUtils.h | 9 +- CBPSSE/config.cpp | 233 ++++++++++++++++++++++++++++++++---------- CBPSSE/config.h | 15 ++- CBPSSE/main.cpp | 38 ++++--- CBPSSE/scan.cpp | 21 +--- 6 files changed, 308 insertions(+), 142 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 7213f0f..1e449de 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -49,57 +49,6 @@ bool actorUtils::IsActorInPowerArmor(Actor* actor) { return isInPowerArmor; } -// May want to change this for any armor -bool actorUtils::IsActorTorsoArmorEquipped(Actor* actor) { - bool isEquipped = false; - bool isArmorIgnored = false; - - if (!IsActorValid(actor)) { - logger.Info("Actor is not valid\n"); - return false; - } - if (!actor->equipData && !actor->equipData->slots) { - logger.Info("Actor has no equipData\n"); - return false; - } - - // 11 IS ARMOR TORSO SLOT (41 minus 30??) - isEquipped = actor->equipData->slots[11].item; - - // Check if armor is ignored - if (isEquipped) { - if (!actor->equipData->slots[11].item) { - logger.Info("slot 11 item check failed.\n"); - // redundant check but just in case - return false; - } - UInt32 bodyFormID; - if (!actor->equipData->slots[3].item) { - logger.Info("slot 3 item check failed.\n"); - bodyFormID = 0; - } - else { - bodyFormID = actor->equipData->slots[3].item->formID;; - } - auto torsoFormID = actor->equipData->slots[11].item->formID; - - //logger.Info("torsoFormID: 0x%08x\n", torsoFormID); - //logger.Info("bodyFormID: 0x%08x\n", bodyFormID); - - auto torsoIdIter = armorIgnore.find(torsoFormID); - - if (torsoIdIter != armorIgnore.end()) - isArmorIgnored = true; - else if ((torsoFormID == bodyFormID - 1)) { - auto bodyIdIter = armorIgnore.find(bodyFormID); - if (bodyIdIter != armorIgnore.end()) - isArmorIgnored = true; - } - } - - return isEquipped && !isArmorIgnored; -} - bool actorUtils::IsActorTrackable(Actor* actor) { if (!IsActorValid(actor)) { logger.Info("IsActorTrackable: actor is not valid.\n"); @@ -148,3 +97,86 @@ bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { } return false; } + +const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, UInt32 slot) { + bool isEquipped = false; + bool isArmorIgnored = false; + + if (!actorUtils::IsActorValid(actor)) { + logger.Error("Actor is not valid"); + return actorUtils::EquippedArmor{ nullptr, nullptr }; + } + if (!actor->equipData && !actor->equipData->slots) { + logger.Error("Actor has no equipData"); + return actorUtils::EquippedArmor{ nullptr, nullptr }; + } + + isEquipped = actor->equipData->slots[slot].item; + + // Check if armor is ignored + if (isEquipped) { + if (!actor->equipData->slots[slot].item) { + logger.Error("slot %d item check failed.", slot); + // redundant check but just in case + return actorUtils::EquippedArmor{ nullptr, nullptr }; + } + return actorUtils::EquippedArmor{ actor->equipData->slots[slot].item, actor->equipData->slots[slot].model }; + } + + return actorUtils::EquippedArmor{ nullptr, nullptr }; +} + +config_t actorUtils::BuildConfigForActor(Actor* actor) { + config_t baseConfig = config; + + for (auto overrideConfigIter = configArmorOverrideMap.rbegin(); overrideConfigIter != configArmorOverrideMap.rend(); ++overrideConfigIter) { + auto data = overrideConfigIter->second; + + std::vector equippedList; + for (auto slot : data.slots) { + auto equipped = actorUtils::GetActorEquippedArmor(actor, slot); + if (equipped.armor && equipped.model) { + equippedList.push_back(equipped); + } + } + + auto armorFormID = data.armors.begin(); + for (; armorFormID != data.armors.end(); ++armorFormID) { + bool breakOutside = false; + for (auto equipped : equippedList) { + if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) { + if (data.isWhitelist) { + for (auto val : data.config) { + if (data.config[val.first].empty()) { + baseConfig.erase(val.first); + } + else { + baseConfig[val.first] = val.second; + } + } + } + + breakOutside = true; + break; + } + } + + if (breakOutside) { + break; + } + } + + if (!data.isWhitelist && armorFormID == data.armors.end()) { + for (auto val : data.config) { + if (data.config[val.first].empty()) { + baseConfig.erase(val.first); + } + else { + baseConfig[val.first] = val.second; + } + } + } + } + + return baseConfig; +} \ No newline at end of file diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index c5091c3..6ba34b9 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -3,11 +3,18 @@ #include "config.h" namespace actorUtils { + struct EquippedArmor { + const TESForm* armor; + const TESForm* model; + }; + std::string GetActorRaceEID(Actor* actor); bool IsActorInPowerArmor(Actor* actor); - bool IsActorTorsoArmorEquipped(Actor* actor); bool IsActorMale(Actor* actor); bool IsActorTrackable(Actor* actor); bool IsActorValid(Actor* actor); bool IsBoneInWhitelist(Actor* actor, std::string boneName); + + const EquippedArmor GetActorEquippedArmor(Actor* actor, UInt32 slot); + config_t BuildConfigForActor(Actor* actor); } \ No newline at end of file diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 1a5acaa..f705910 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -14,6 +14,7 @@ #include "f4se/GameObjects.h" #include "f4se/GameRTTI.h" #include "f4se_common/Utilities.h" +#include "f4se/GameData.h" #define DEBUG 0 #pragma warning(disable : 4996) @@ -23,22 +24,69 @@ bool playerOnly = false; bool femaleOnly = false; bool maleOnly = false; bool npcOnly = false; -bool detectArmor = false; bool useWhitelist = false; config_t config; config_t configArmor; -configOverrides_t configOverrides; -configOverrides_t configArmorOverrides; +std::map configArmorOverrideMap; // TODO data structure these whitelist_t whitelist; std::vector raceWhitelist; -std::unordered_map armorIgnore; + +UInt32 GetFormIDFromString(std::string const& configString) +{ + size_t colonPos = configString.find_first_of(":"); + auto pluginFormID = configString.substr(0, colonPos); + std::string pluginName = ""; + if (colonPos != std::string::npos && colonPos < configString.length() - 1) { + pluginName = configString.substr(colonPos + 1); + } + + for (auto digit : pluginFormID) { + if (!std::isxdigit(digit)) { + logger.Error("Invalid FormID %s, invalid hex character %c\n", pluginFormID.c_str(), digit); + return -1; + } + } + + UInt32 formID = std::stoul(pluginFormID, nullptr, 16); + + if (!pluginName.empty()) { + auto dataHandler = *g_dataHandler; + auto modInfo = dataHandler->LookupLoadedModByName(pluginName.c_str()); + + if (!modInfo) { + logger.Error("Plugin with name %s does not exist\n", pluginName.c_str()); + return -1; + } + + bool isLightPlugin = modInfo->recordFlags & modInfo->kRecordFlags_ESL; + size_t maxPartialIdLength = isLightPlugin ? 3 : 6; + + if (pluginFormID.length() > maxPartialIdLength) { + logger.Error("Invalid FormID %s, too many characters when%s plugin name specified\n", pluginFormID.c_str(), isLightPlugin ? " light" : ""); + return -1; + } + + UInt32 loadIndex = isLightPlugin ? dataHandler->GetLoadedLightModIndex(pluginName.c_str()) : dataHandler->GetLoadedModIndex(pluginName.c_str()); + formID |= loadIndex << 24; + } + else { + if (pluginFormID.length() > 8) { + logger.Error("Invalid FormID %s, too many characters\n", pluginFormID.c_str()); + return -1; + } + } + + return formID; +} bool LoadConfig() { logger.Info("loadConfig\n"); + config_t configOverrides; + std::map configArmorBoneOverrides; std::set bonesSet; bool reloadActors = false; @@ -50,10 +98,7 @@ bool LoadConfig() { boneNames.clear(); config.clear(); - configArmor.clear(); - configOverrides.clear(); - configArmorOverrides.clear(); - armorIgnore.clear(); + configArmorOverrideMap.clear(); // Note: Using INIReader results in a slight double read INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); @@ -82,37 +127,24 @@ bool LoadConfig() { (npcOnly ^ npcOnlyOld) || (useWhitelist ^ useWhitelistOld); - detectArmor = configReader.GetBoolean("General", "detectArmor", false); configReloadCount = configReader.GetInteger("Tuning", "rate", 0); - //Read armorIgnore - auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); - { - size_t commaPos; - do { - commaPos = armorIgnoreStr.find_first_of(","); - auto token = armorIgnoreStr.substr(0, commaPos); - UInt32 formID; - std::stringstream ss; - ss << std::hex << token; - ss >> formID; - armorIgnore[formID] = true; - armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); - - //logger.Info(" %s, %s, %d, %d\n", token.c_str(), whitelistName.c_str(), commaPos >= 0, colonPos < 0); - } while (commaPos != -1); - } - // Read sections auto sections = configReader.Sections(); for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) { // Split for override section check auto overrideStr = std::string("Override:"); - auto splitStr = std::mismatch(overrideStr.begin(), overrideStr.end(), sectionsIter->begin()); + auto splitOverrideStr = std::mismatch(overrideStr.begin(), overrideStr.end(), sectionsIter->begin()); + + auto subOverrideStr = std::string("Override."); + auto splitSubOverrideStr = std::mismatch(subOverrideStr.begin(), subOverrideStr.end(), sectionsIter->begin()); - auto overrideAStr = std::string("Override.A:"); - auto splitAStr = std::mismatch(overrideAStr.begin(), overrideAStr.end(), sectionsIter->begin()); + auto subAttachStr = std::string("Attach."); + auto splitSubAttachStr = std::mismatch(subAttachStr.begin(), subAttachStr.end(), sectionsIter->begin()); + + auto armorStr = std::string("Armor."); + auto splitArmorStr = std::mismatch(armorStr.begin(), armorStr.end(), sectionsIter->begin()); if (*sectionsIter == std::string("Attach")) { // Get section contents @@ -131,23 +163,87 @@ bool LoadConfig() { } } } - else if (*sectionsIter == std::string("Attach.A") && detectArmor) { - // Get section contents + else if (splitSubAttachStr.first == subAttachStr.end()) { + auto attachSubname = std::string(splitSubAttachStr.second, sectionsIter->end()); + + UInt32 attachPriority; + try { + attachPriority = std::stoul(attachSubname); + } + catch (const std::exception&) { + continue; + } + auto sectionMap = configReader.Section(*sectionsIter); - for (auto &valuesIter : sectionMap) { - auto &boneName = valuesIter.first; - auto &attachName = valuesIter.second; + for (auto& valuesIter : sectionMap) { + auto& boneName = valuesIter.first; + auto& attachName = valuesIter.second; boneNames.push_back(boneName); - // Find specified bone section and insert map values into configArmor - if (sections.find(attachName) != sections.end()) { + // Find specified bone section and insert map values into config_t in configArmorOverrideMap at attachPriority + if (attachName.empty()) { + // "Touch" the map to add empty entry for bone in config_t to signal deletion later when building config from overrides + configArmorOverrideMap[attachPriority].config[boneName]; + } + else if (sections.find(attachName) != sections.end()) { auto attachMapSection = configReader.Section(attachName); - for (auto &attachIter : attachMapSection) { + for (auto& attachIter : attachMapSection) { auto& keyName = attachIter.first; - configArmor[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); + configArmorOverrideMap[attachPriority].config[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); } } } } + else if (splitArmorStr.first == armorStr.end()) { + auto armorSubname = std::string(splitArmorStr.second, sectionsIter->end()); + + UInt32 armorPriority; + try { + armorPriority = std::stoul(armorSubname); + } + catch (const std::exception&) { + continue; + } + + std::string slots = configReader.Get(*sectionsIter, "slots", ""); + if (slots.empty()) { + continue; + } + + size_t commaPos; + do { + commaPos = slots.find_first_of(","); + auto token = slots.substr(0, commaPos); + slots = slots.substr(commaPos + 1); + + UInt32 slot; + try { + slot = std::stoul(token); + } + catch (const std::exception&) { + continue; + } + + configArmorOverrideMap[armorPriority].slots.emplace(slot - 30); + } while (commaPos != std::string::npos); + + configArmorOverrideMap[armorPriority].isWhitelist = configReader.GetBoolean(*sectionsIter, "whitelist", false); + + // Get section contents + auto sectionMap = configReader.Section(*sectionsIter); + for (auto& valuesIter : sectionMap) { + auto& key = valuesIter.first; + if (key == "whitelist" || key == "slots") { + continue; + } + + auto formID = GetFormIDFromString(valuesIter.second); + if (formID == -1) { + continue; + } + + configArmorOverrideMap[armorPriority].armors.emplace(formID); + } + } else if (*sectionsIter == std::string("Whitelist") && useWhitelist) { whitelist.clear(); raceWhitelist.clear(); @@ -185,9 +281,9 @@ bool LoadConfig() { } while (commaPos != -1); } } - else if (splitStr.first == overrideStr.end()) { + else if (splitOverrideStr.first == overrideStr.end()) { // If section name is prefixed with "Override:", grab other half of name for bone - auto boneName = std::string(splitStr.second, sectionsIter->end()); + auto boneName = std::string(splitOverrideStr.second, sectionsIter->end()); // Get section contents auto sectionMap = configReader.Section(*sectionsIter); @@ -195,14 +291,26 @@ bool LoadConfig() { configOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); } } - else if (splitAStr.first == overrideAStr.end()) { - // If section name is prefixed with "Override:", grab other half of name for bone - auto boneName = std::string(splitAStr.second, sectionsIter->end()); + else if (splitSubOverrideStr.first == subOverrideStr.end()) { + // If section name is prefixed with "Override.", grab other half for priority and name of bone + auto overrideSection = std::string(splitSubOverrideStr.second, sectionsIter->end()); + + size_t colonPos = overrideSection.find_first_of(":"); + auto overrideSubname = overrideSection.substr(0, colonPos); + auto boneName = overrideSection.substr(colonPos + 1); + + UInt32 overridePriority; + try { + overridePriority = std::stoul(overrideSubname); + } + catch (const std::exception&) { + continue; + } // Get section contents auto sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIt : sectionMap) { - configArmorOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); + configArmorBoneOverrides[overridePriority][boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); } } } @@ -217,10 +325,12 @@ bool LoadConfig() { } // replace armor configs with override settings (if any) - for (auto& boneIter : configArmorOverrides) { - if (configArmor.count(boneIter.first) > 0) { - for (auto settingIter : boneIter.second) { - configArmor[boneIter.first][settingIter.first] = settingIter.second; + for (auto conf : configArmorBoneOverrides) { + for (auto& boneIter : conf.second) { + if (configArmorOverrideMap[conf.first].config.count(boneIter.first) > 0) { + for (auto settingIter : boneIter.second) { + configArmorOverrideMap[conf.first].config[boneIter.first][settingIter.first] = settingIter.second; + } } } } @@ -244,13 +354,26 @@ void DumpConfigToLog() } } - logger.Info("***** ConfigArmor Dump *****\n"); - for (auto section : configArmor) { - logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) { - logger.Info("%s=%f\n", setting.first.c_str(), setting.second); + logger.Info("***** ConfigArmorOverride Dump *****\n"); + for (auto conf : configArmorOverrideMap) + { + logger.Info("** Slot-Armor Map priority %ul **\n", conf.first); + logger.Info("[Slots]\n"); + for (auto slot : conf.second.slots) { + logger.Info("%ul\n", slot); + } + logger.Info("[Armors]\n"); + for (auto formID : conf.second.armors) { + logger.Info("%ul\n", formID); + } + logger.Info("** Config priority %ul **\n", conf.first); + for (auto section : conf.second.config) { + logger.Info("[%s]\n", section.first.c_str()); + for (auto setting : section.second) { + logger.Info("%s=%f\n", setting.first.c_str(), setting.second); + } + } } - } } void DumpWhitelistToLog() { diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 60ed75b..fad52aa 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -1,5 +1,7 @@ #pragma once #include +#include +#include #include #include "f4se/GameReferences.h" @@ -14,21 +16,26 @@ struct whitelistSex { typedef std::unordered_map configEntry_t; typedef std::unordered_map config_t; -typedef std::unordered_map configOverrides_t; typedef std::unordered_map> whitelist_t; +struct armorOverrideData { + bool isWhitelist; + std::unordered_set slots; + std::unordered_set armors; + config_t config; +}; + extern bool playerOnly; extern bool femaleOnly; extern bool maleOnly; extern bool npcOnly; -extern bool detectArmor; extern bool useWhitelist; extern int configReloadCount; extern config_t config; -extern config_t configArmor; +extern std::map configArmorOverrideMap; extern whitelist_t whitelist; extern std::vector raceWhitelist; -extern std::unordered_map armorIgnore; + bool LoadConfig(); void DumpWhitelistToLog(); \ No newline at end of file diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index b1e3082..11d11fd 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -12,14 +12,14 @@ bool RegisterFuncs(VirtualMachine* vm); -PluginHandle g_pluginHandle = kPluginHandle_Invalid; -//F4SEMessagingInterface * g_messagingInterface = NULL; +PluginHandle g_pluginHandle = kPluginHandle_Invalid; +F4SEMessagingInterface * g_messagingInterface = NULL; -//F4SEScaleformInterface * g_scaleform = NULL; -//F4SESerializationInterface * g_serialization = NULL; -F4SETaskInterface * g_task = nullptr; -F4SEPapyrusInterface * g_papyrus = nullptr; -//IDebugLog gLog("Data\\F4SE\\Plugins\\hook.log"); +//F4SEScaleformInterface * g_scaleform = NULL; +//F4SESerializationInterface * g_serialization = NULL; +F4SETaskInterface * g_task = nullptr; +F4SEPapyrusInterface * g_papyrus = nullptr; +//IDebugLog gLog("Data\\F4SE\\Plugins\\hook.log"); void DoHook(); @@ -33,6 +33,12 @@ void MessageHandler(F4SEMessagingInterface::Message * msg) case F4SEMessagingInterface::kMessage_GameDataReady: { logger.Info("kMessage_GameDataReady\n"); + // Load initial config + logger.Error("Loading Config"); + LoadConfig(); + logger.Error("Hooking Game"); + DoHook(); + logger.Error("CBP Load Complete\n"); } break; case F4SEMessagingInterface::kMessage_GameLoaded: @@ -142,14 +148,16 @@ extern "C" if (g_papyrus) g_papyrus->Register(RegisterFuncs); - - // Load initial config before the hook. - logger.Error("Loading Config\n"); - LoadConfig(); - //g_messagingInterface->RegisterListener(0, "F4SE", MessageHandler); - logger.Error("Hooking Game\n"); - DoHook(); - logger.Error("CBP Load Complete\n"); + + g_messagingInterface = (F4SEMessagingInterface*)f4se->QueryInterface(kInterface_Messaging); + if (!g_messagingInterface) + { + logger.Error("Couldn't get messaging interface"); + return false; + } + + g_messagingInterface->RegisterListener(g_pluginHandle, "F4SE", MessageHandler); + return true; } }; diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index dab818f..1855905 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -43,9 +43,9 @@ #pragma warning(disable : 4996) using actorUtils::IsActorMale; -using actorUtils::IsActorTorsoArmorEquipped; using actorUtils::IsActorTrackable; using actorUtils::IsActorValid; +using actorUtils::BuildConfigForActor; extern F4SETaskInterface *g_task; @@ -208,26 +208,15 @@ void UpdateActors() { //logger.error("Sim Object not found in tracked actors\n"); } else { + config_t composedConfig = BuildConfigForActor(a.actor); + auto &simObj = objIterator->second; if (simObj.IsBound()) { - // need better system for update config - if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { - logger.Info("torso armor detected on actor %x\n", a.actor->formID); - simObj.UpdateConfig(a.actor, boneNames, configArmor); - } - else { - simObj.UpdateConfig(a.actor, boneNames, config); - } + simObj.UpdateConfig(a.actor, boneNames, composedConfig); simObj.Update(a.actor); } else { - if (IsActorTorsoArmorEquipped(a.actor) && detectArmor) { - logger.Info("torso armor detected on actor %x\n", a.actor->formID); - simObj.Bind(a.actor, boneNames, configArmor); - } - else { - simObj.Bind(a.actor, boneNames, config); - } + simObj.Bind(a.actor, boneNames, composedConfig); } } } From 9ea7bc7acdad5826dd8bf19bc87b3abf49c55845 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Sun, 26 Jun 2022 17:32:40 -0700 Subject: [PATCH 35/66] Changed priority numbers to name-number mappings to make reordering easier --- CBPSSE/config.cpp | 52 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index f705910..32ab9f0 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -88,6 +88,7 @@ bool LoadConfig() { config_t configOverrides; std::map configArmorBoneOverrides; std::set bonesSet; + std::unordered_map priorityNameMappings; bool reloadActors = false; auto playerOnlyOld = playerOnly; @@ -167,11 +168,20 @@ bool LoadConfig() { auto attachSubname = std::string(splitSubAttachStr.second, sectionsIter->end()); UInt32 attachPriority; - try { - attachPriority = std::stoul(attachSubname); + auto mapEntry = priorityNameMappings.find(attachSubname); + if (mapEntry != priorityNameMappings.end()) { + attachPriority = mapEntry->second; } - catch (const std::exception&) { - continue; + else { + std::string priorityMapping = configReader.Get("Priority", attachSubname, ""); + try { + attachPriority = std::stoul(priorityMapping); + } + catch (const std::exception&) { + continue; + } + + priorityNameMappings[attachSubname] = attachPriority; } auto sectionMap = configReader.Section(*sectionsIter); @@ -197,11 +207,20 @@ bool LoadConfig() { auto armorSubname = std::string(splitArmorStr.second, sectionsIter->end()); UInt32 armorPriority; - try { - armorPriority = std::stoul(armorSubname); + auto mapEntry = priorityNameMappings.find(armorSubname); + if (mapEntry != priorityNameMappings.end()) { + armorPriority = mapEntry->second; } - catch (const std::exception&) { - continue; + else { + std::string priorityMapping = configReader.Get("Priority", armorSubname, ""); + try { + armorPriority = std::stoul(priorityMapping); + } + catch (const std::exception&) { + continue; + } + + priorityNameMappings[armorSubname] = armorPriority; } std::string slots = configReader.Get(*sectionsIter, "slots", ""); @@ -300,11 +319,20 @@ bool LoadConfig() { auto boneName = overrideSection.substr(colonPos + 1); UInt32 overridePriority; - try { - overridePriority = std::stoul(overrideSubname); + auto mapEntry = priorityNameMappings.find(overrideSubname); + if (mapEntry != priorityNameMappings.end()) { + overridePriority = mapEntry->second; } - catch (const std::exception&) { - continue; + else { + std::string priorityMapping = configReader.Get("Priority", overrideSubname, ""); + try { + overridePriority = std::stoul(priorityMapping); + } + catch (const std::exception&) { + continue; + } + + priorityNameMappings[overrideSubname] = overridePriority; } // Get section contents From 17a950c13ee9ed8b3384442bc965ce652b413a49 Mon Sep 17 00:00:00 2001 From: ericnam808 Date: Tue, 26 Jul 2022 02:39:40 -0700 Subject: [PATCH 36/66] Update to f4se 0.6.23 --- common/IPrefix.h | 4 +- common/common.vcproj | 3 - common/common.vcxproj | 3 - common/common_vc11.sln | 4 - common/common_vc11.vcxproj | 4 - common/common_vc14.sln | 6 - common/common_vc14.vcxproj | 8 - common/common_vc9.vcproj | 3 - f4se/f4se/BSGeometry.h | 2 +- f4se/f4se/BSGraphics.cpp | 12 +- f4se/f4se/BSGraphics.h | 26 +- f4se/f4se/CustomMenu.cpp | 16 +- f4se/f4se/GameData.h | 24 - f4se/f4se/GameExtraData.cpp | 4 +- f4se/f4se/GameExtraData.h | 7 - f4se/f4se/GameFormComponents.h | 2 +- f4se/f4se/GameForms.h | 4 +- f4se/f4se/GameHandle.cpp | 10 + f4se/f4se/GameHandle.h | 277 ++++++++++ f4se/f4se/GameInput.h | 4 +- f4se/f4se/GameMenus.cpp | 41 +- f4se/f4se/GameMenus.h | 231 ++++---- f4se/f4se/GameObjects.h | 35 +- f4se/f4se/GameReferences.cpp | 23 - f4se/f4se/GameReferences.h | 31 +- f4se/f4se/GameTypes.cpp | 92 ---- f4se/f4se/GameTypes.h | 768 ++++++++++++++++++++++++++- f4se/f4se/GameUtilities.cpp | 1 + f4se/f4se/GameUtilities.h | 68 +++ f4se/f4se/GameWorkshop.cpp | 16 +- f4se/f4se/GameWorkshop.h | 117 +++- f4se/f4se/Hooks_Scaleform.cpp | 6 +- f4se/f4se/InternalSerialization.cpp | 4 +- f4se/f4se/NiNodes.cpp | 2 +- f4se/f4se/NiObjects.cpp | 20 + f4se/f4se/NiObjects.h | 22 +- f4se/f4se/NiTypes.h | 40 +- f4se/f4se/PapyrusActor.cpp | 12 +- f4se/f4se/PapyrusEvents.h | 88 +-- f4se/f4se/PapyrusGame.cpp | 4 +- f4se/f4se/PapyrusObjectReference.cpp | 85 +-- f4se/f4se/PapyrusPerk.cpp | 4 +- f4se/f4se/PapyrusUI.cpp | 2 +- f4se/f4se/PluginAPI.h | 19 + f4se/f4se/PluginManager.cpp | 178 ++++++- f4se/f4se/PluginManager.h | 52 +- f4se/f4se/ScaleformCallbacks.h | 3 +- f4se/f4se/ScaleformValue.cpp | 27 +- f4se/f4se/ScaleformValue.h | 124 +++-- f4se/f4se/Serialization.cpp | 2 +- f4se/f4se/f4se.cpp | 61 ++- f4se/f4se/f4se.vcxproj | 4 +- f4se/f4se/f4se.vcxproj.filters | 8 +- f4se/f4se_common/f4se_version.h | 10 +- f4se/f4se_readme.txt | 2 +- f4se/f4se_whatsnew.txt | 14 + 56 files changed, 2031 insertions(+), 608 deletions(-) create mode 100644 f4se/f4se/GameHandle.cpp create mode 100644 f4se/f4se/GameHandle.h diff --git a/common/IPrefix.h b/common/IPrefix.h index 9e09446..d514db9 100644 --- a/common/IPrefix.h +++ b/common/IPrefix.h @@ -10,8 +10,8 @@ // 4312 - pointer extension #pragma warning(disable: 4018 4200 4244 4267 4305 4288 4312 4311) -// winxp and above -#define _WIN32_WINNT 0x0501 +// need win8 for windows store APIs +#define _WIN32_WINNT 0x0602 #include #include diff --git a/common/common.vcproj b/common/common.vcproj index ca58e22..b6ae6cc 100644 --- a/common/common.vcproj +++ b/common/common.vcproj @@ -5,9 +5,6 @@ Name="common" ProjectGUID="{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}" RootNamespace="common" - SccProjectName="Perforce Project" - SccLocalPath="." - SccProvider="MSSCCI:Perforce SCM" Keyword="Win32Proj" > diff --git a/common/common.vcxproj b/common/common.vcxproj index 0a5c2b5..ef25286 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -13,9 +13,6 @@ {20C6411C-596F-4B85-BE4E-8BC91F59D8A6} common - Perforce Project - . - MSSCCI:Perforce SCM Win32Proj diff --git a/common/common_vc11.sln b/common/common_vc11.sln index 4835e8a..c19dc71 100644 --- a/common/common_vc11.sln +++ b/common/common_vc11.sln @@ -4,10 +4,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common_vc11", "common_vc11.vcxproj", "{D4C128A1-73DC-4941-A453-CE55AF239BA8}" EndProject Global - GlobalSection(SourceCodeControl) = preSolution - SccNumberOfProjects = 1 - SccLocalPath0 = . - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Release|x64 = Release|x64 diff --git a/common/common_vc11.vcxproj b/common/common_vc11.vcxproj index 4d5bdc7..e822fd5 100644 --- a/common/common_vc11.vcxproj +++ b/common/common_vc11.vcxproj @@ -55,10 +55,6 @@ {D4C128A1-73DC-4941-A453-CE55AF239BA8} Win32Proj common_vc11 - SAK - SAK - SAK - SAK diff --git a/common/common_vc14.sln b/common/common_vc14.sln index 5c220f2..155076d 100644 --- a/common/common_vc14.sln +++ b/common/common_vc14.sln @@ -19,10 +19,4 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(SourceCodeControl) = preSolution - SccNumberOfProjects = 2 - SccLocalPath0 = . - SccProjectUniqueName1 = common_vc14.vcxproj - SccLocalPath1 = . - EndGlobalSection EndGlobal diff --git a/common/common_vc14.vcxproj b/common/common_vc14.vcxproj index 40cac95..9b0dad9 100644 --- a/common/common_vc14.vcxproj +++ b/common/common_vc14.vcxproj @@ -47,14 +47,6 @@ Win32Proj common_vc14 10.0.18362.0 - - - - - - - - diff --git a/common/common_vc9.vcproj b/common/common_vc9.vcproj index 11cfd49..940af31 100644 --- a/common/common_vc9.vcproj +++ b/common/common_vc9.vcproj @@ -5,9 +5,6 @@ Name="common_vc9" ProjectGUID="{20C6411C-596F-4B85-BE4E-8BC91F59D8A6}" RootNamespace="common" - SccProjectName="Perforce Project" - SccLocalPath="." - SccProvider="MSSCCI:Perforce SCM" Keyword="Win32Proj" TargetFrameworkVersion="131072" > diff --git a/f4se/f4se/BSGeometry.h b/f4se/f4se/BSGeometry.h index 7ccbc31..26fece1 100644 --- a/f4se/f4se/BSGeometry.h +++ b/f4se/f4se/BSGeometry.h @@ -4,7 +4,7 @@ #include "f4se/BSSkin.h" class NiProperty; -class ID3D11Buffer; +struct ID3D11Buffer; // 38 struct BSGeometrySegmentFlagData diff --git a/f4se/f4se/BSGraphics.cpp b/f4se/f4se/BSGraphics.cpp index 1dabba7..12a3525 100644 --- a/f4se/f4se/BSGraphics.cpp +++ b/f4se/f4se/BSGraphics.cpp @@ -1,19 +1,19 @@ #include "f4se/BSGraphics.h" // 2CA5233612B3158658DB6DB9C90FD0258F1836E2+124 -RelocPtr g_renderer(0x067220E8); +RelocPtr g_imageSpaceManager(0x067220E8); // 6BF9214E9DC5338FC817F85B0716990E5CA7C862+31 -RelocPtr g_renderManager(0x061E0900); +RelocPtr g_renderManager(0x061E0900); // FA43F2F87927D8F20B17E756782BC91BB6BD04C2+3B -RelocPtr g_renderTargetManager(0x0384FD30); +RelocPtr g_renderTargetManager(0x0384FD30); // 4A4A200E8F9173F8CE99D39E27F4BDAF680DF52B+9C -RelocPtr g_shaderResourceManager(0x05C08F08); +RelocPtr g_shaderResourceManager(0x05C08F08); // 84E19996C30AD51CE0D4AEF3E6ED8FFFF4AE4BD7+18 -RelocPtr g_D3D11Device(0x0609BF88); +RelocPtr g_D3D11Device(0x0609BF88); // BFDAF477B684098E9F394A636CCDC1BBD06EDEBF+25 -RelocPtr g_D3D11DeviceContext(0x061DDC60); +RelocPtr g_D3D11DeviceContext(0x061DDC60); diff --git a/f4se/f4se/BSGraphics.h b/f4se/f4se/BSGraphics.h index 0dcc7ac..d2db1d2 100644 --- a/f4se/f4se/BSGraphics.h +++ b/f4se/f4se/BSGraphics.h @@ -8,17 +8,19 @@ class BSGeometryData; -// ?? -class BSRenderManager +namespace BSGraphics +{ +class Renderer { public: UInt64 unk2588[0x2590 >> 3]; // 2588 - CRITICAL_SECTION m_textureLock; // 2590 + CRITICAL_SECTION m_renderLock; // 2590 - MEMBER_FN_PREFIX(BSRenderManager); + MEMBER_FN_PREFIX(Renderer); DEFINE_MEMBER_FN(CreateBSGeometryData, BSGeometryData*, 0x01D0BD60, UInt32 * blockSize, UInt8 * vertexData, UInt64 vertexDesc, BSGeometryData::TriangleData * triData); // Creates a block with a vertex copy in the resource pool with a reference to the supplied triblock (partial deep copy) }; -STATIC_ASSERT(offsetof(BSRenderManager, m_textureLock) == 0x2590); +STATIC_ASSERT(offsetof(Renderer, m_renderLock) == 0x2590); +} class BSShaderResourceManager { @@ -65,7 +67,7 @@ class BSRenderTargetManager }; // 1B8 -class BSRenderer +class ImageSpaceManager { public: @@ -74,9 +76,9 @@ class BSRenderer struct ID3D11DeviceContext; struct ID3D11Device; -extern RelocPtr g_renderer; -extern RelocPtr g_renderManager; -extern RelocPtr g_renderTargetManager; -extern RelocPtr g_shaderResourceManager; -extern RelocPtr g_D3D11Device; -extern RelocPtr g_D3D11DeviceContext; +extern RelocPtr g_imageSpaceManager; +extern RelocPtr g_renderManager; +extern RelocPtr g_renderTargetManager; +extern RelocPtr g_shaderResourceManager; +extern RelocPtr g_D3D11Device; +extern RelocPtr g_D3D11DeviceContext; diff --git a/f4se/f4se/CustomMenu.cpp b/f4se/f4se/CustomMenu.cpp index 8c2ad3a..f1b7625 100644 --- a/f4se/f4se/CustomMenu.cpp +++ b/f4se/f4se/CustomMenu.cpp @@ -12,7 +12,7 @@ CustomMenu::CustomMenu() : GameMenuBase() // Normally this code would happen in the constructor, but the menu name isn't set until after construction void LoadCustomMenu_Hook(IMenu * menu) { - BSReadAndWriteLocker locker(&g_customMenuLock); + BSWriteLocker locker(&g_customMenuLock); auto menuData = g_customMenuData.find(menu->menuName.c_str()); if(menuData != g_customMenuData.end()) { @@ -23,10 +23,10 @@ void LoadCustomMenu_Hook(IMenu * menu) menu->flags = menuData->second.menuFlags; menu->depth = menuData->second.depth; - if((menu->flags & IMenu::kFlag_ShowCursor) && (extFlags & CustomMenuData::kExtFlag_CheckForGamepad)) + if((menu->flags & IMenu::kFlag_UsesCursor) && (extFlags & CustomMenuData::kExtFlag_CheckForGamepad)) { if((*g_inputDeviceMgr)->IsGamepadEnabled()) - menu->flags &= ~IMenu::kFlag_ShowCursor; + menu->flags &= ~IMenu::kFlag_UsesCursor; } if (CALL_MEMBER_FN((*g_scaleformManager), LoadMovie)(menu, menu->movie, menuPath.c_str(), rootPath.c_str(), movieFlags)) @@ -37,17 +37,17 @@ void LoadCustomMenu_Hook(IMenu * menu) GameMenuBase * gameMenu = static_cast(menu); - CreateBaseShaderTarget(gameMenu->shaderTarget, menu->stage); + CreateBaseShaderTarget(gameMenu->filterHolder, menu->stage); if(extFlags & CustomMenuData::kExtFlag_InheritColors) { - gameMenu->shaderTarget->SetFilterColor(false); - (*g_colorUpdateDispatcher)->eventDispatcher.AddEventSink(gameMenu->shaderTarget); + gameMenu->filterHolder->SetFilterColor(false); + (*g_colorUpdateDispatcher)->eventDispatcher.AddEventSink(gameMenu->filterHolder); } - if (menu->flags & IMenu::kFlag_ApplyDropDownFilter) + if (menu->flags & IMenu::kFlag_CustomRendering) { - gameMenu->subcomponents.Push(gameMenu->shaderTarget); + gameMenu->shaderFXObjects.Push(gameMenu->filterHolder); } } } diff --git a/f4se/f4se/GameData.h b/f4se/f4se/GameData.h index 41286c8..4f2e1ba 100644 --- a/f4se/f4se/GameData.h +++ b/f4se/f4se/GameData.h @@ -308,30 +308,6 @@ class DataHandler extern RelocPtr g_dataHandler; extern RelocPtr g_isGameDataReady; -// 30 -class LocationData -{ -public: - LocationData(Actor * actor) - { - CALL_MEMBER_FN(this, ctor)(actor); - } - ~LocationData() { }; - - float unk00; // 00 - float unk04; // 04 - float unk08; // 08 - float unk0C; // 0C - float unk10; // 10 - float unk14; // 14 - void * unk18; // 18 - TESObjectCELL * cell; // 20 - TESWorldSpace * worldspace; // 28 - - MEMBER_FN_PREFIX(LocationData); - DEFINE_MEMBER_FN(ctor, LocationData*, 0x001F8830, Actor * refr); -}; - struct DefaultObjectEntry { BSFixedString editorId; diff --git a/f4se/f4se/GameExtraData.cpp b/f4se/f4se/GameExtraData.cpp index 27bfe6f..51edab7 100644 --- a/f4se/f4se/GameExtraData.cpp +++ b/f4se/f4se/GameExtraData.cpp @@ -47,7 +47,7 @@ bool ExtraDataList::Remove(UInt8 type, BSExtraData* toRemove) { if (!toRemove) return false; - BSReadAndWriteLocker locker(&m_lock); + BSWriteLocker locker(&m_lock); if (HasType(type)) { bool bRemoved = false; if (m_data == toRemove) { @@ -75,7 +75,7 @@ bool ExtraDataList::Add(UInt8 type, BSExtraData* toAdd) { if (!toAdd || HasType(type)) return false; - BSReadAndWriteLocker locker(&m_lock); + BSWriteLocker locker(&m_lock); BSExtraData* next = m_data; m_data = toAdd; toAdd->next = next; diff --git a/f4se/f4se/GameExtraData.h b/f4se/f4se/GameExtraData.h index 3cfd57d..1628124 100644 --- a/f4se/f4se/GameExtraData.h +++ b/f4se/f4se/GameExtraData.h @@ -314,13 +314,6 @@ class ExtraBendableSplineParams : public BSExtraData float unk2C; // 2C }; -// 58 -class ExtraWorkshopData -{ -public: - UInt64 unk18[(0x58 - 0x18) >> 3]; -}; - // 20 class ExtraMaterialSwap : public BSExtraData { diff --git a/f4se/f4se/GameFormComponents.h b/f4se/f4se/GameFormComponents.h index 4f6a6bc..a017801 100644 --- a/f4se/f4se/GameFormComponents.h +++ b/f4se/f4se/GameFormComponents.h @@ -1497,7 +1497,7 @@ struct BGSInventoryItem void Dump(); #endif template - bool Visit(T & f) + bool Visit(const T & f) { if(f(this) && next) return next->Visit(f); diff --git a/f4se/f4se/GameForms.h b/f4se/f4se/GameForms.h index e4ad133..3b301ed 100644 --- a/f4se/f4se/GameForms.h +++ b/f4se/f4se/GameForms.h @@ -1732,8 +1732,8 @@ class BGSConstructibleObject : public TESForm struct Component { - BGSComponent * component; // 00 - UInt32 count; // 08 + TESForm * component; // 00 + UInt32 count; // 08 }; tArray * components; // 50 diff --git a/f4se/f4se/GameHandle.cpp b/f4se/f4se/GameHandle.cpp new file mode 100644 index 0000000..5f07604 --- /dev/null +++ b/f4se/f4se/GameHandle.cpp @@ -0,0 +1,10 @@ +#include "f4se/GameHandle.h" + +// 0BE70664D4DF11A3F88748D9CB45B23D0B4FD50C+2F +RelocAddr <_LookupREFRByHandle> LookupREFRByHandle(0x0000AB60); + +// 4369DFCC9BCC88536EEB89E5A107B60941016295+26 +RelocAddr <_CreateHandleByREFR> CreateHandleByREFR(0x0000A8A0); + +// 3A53807E195FFAEA7AA0EF7FD42D84E4EA0B755A+D0 +RelocPtr g_invalidRefHandle(0x038CCE04); \ No newline at end of file diff --git a/f4se/f4se/GameHandle.h b/f4se/f4se/GameHandle.h new file mode 100644 index 0000000..3c779cf --- /dev/null +++ b/f4se/f4se/GameHandle.h @@ -0,0 +1,277 @@ +#pragma once + +class TESObjectREFR; + +#include "f4se_common/Relocation.h" +#include "f4se/NiTypes.h" + +typedef bool(*_CreateHandleByREFR)(UInt32 & handleOut, const TESObjectREFR * ref); +extern RelocAddr <_CreateHandleByREFR> CreateHandleByREFR; + +typedef bool(*_LookupREFRByHandle)(const UInt32 & handleIn, NiPointer& ref); +extern RelocAddr <_LookupREFRByHandle> LookupREFRByHandle; + +extern RelocPtr g_invalidRefHandle; + +template +class BSUntypedPointerHandle +{ +public: + typedef UInt32 value_type; + + + BSUntypedPointerHandle() : + _handle(0) + { + _handle = get_null_handle(); + } + + + BSUntypedPointerHandle(const BSUntypedPointerHandle& a_rhs) : + _handle(a_rhs._handle) + {} + + + BSUntypedPointerHandle(BSUntypedPointerHandle&& a_rhs) : + _handle(std::move(a_rhs._handle)) + { + a_rhs.reset(); + } + + + BSUntypedPointerHandle(value_type a_handle) : + _handle(a_handle) + {} + + + ~BSUntypedPointerHandle() + { + reset(); + } + + + BSUntypedPointerHandle& operator=(const BSUntypedPointerHandle& a_rhs) + { + _handle = a_rhs._handle; + return *this; + } + + + BSUntypedPointerHandle& operator=(BSUntypedPointerHandle&& a_rhs) + { + _handle = std::move(a_rhs._handle); + a_rhs.reset(); + return *this; + } + + + BSUntypedPointerHandle& operator=(value_type a_rhs) + { + _handle = a_rhs; + return *this; + } + + + operator bool() const + { + has_value(); + } + + + bool has_value() const + { + return _handle != get_null_handle(); + } + + + value_type value() const + { + return _handle; + } + + operator UInt32&() + { + return _handle; + } + + + void reset() + { + _handle = get_null_handle(); + } + + + friend bool operator==(const BSUntypedPointerHandle& a_lhs, const BSUntypedPointerHandle& a_rhs) + { + return a_lhs.value() == a_rhs.value(); + } + + + friend bool operator!=(const BSUntypedPointerHandle& a_lhs, const BSUntypedPointerHandle& a_rhs) + { + return !(a_lhs == a_rhs); + } + +private: + static UInt32 get_null_handle() + { + return *g_invalidRefHandle; + } + + // members + UInt32 _handle; // 0 +}; +STATIC_ASSERT(sizeof(BSUntypedPointerHandle<>) == 0x4); + +template > +class BSPointerHandle : protected Handle +{ +public: + typedef typename Handle::value_type native_handle_type; + + + BSPointerHandle() : + Handle() + {} + + + BSPointerHandle(const BSPointerHandle& a_rhs) : + Handle(a_rhs) + {} + + + BSPointerHandle(BSPointerHandle&& a_rhs) : + Handle(std::move(a_rhs)) + {} + + + template + explicit BSPointerHandle(const Y* a_rhs) : + Handle() + { + if (a_rhs && a_rhs->BSHandleRefObject::QRefCount() > 0) { + create(a_rhs); + } + } + + + template + BSPointerHandle(const BSPointerHandle& a_rhs) : + Handle(a_rhs) + {} + + + template + BSPointerHandle(BSPointerHandle&& a_rhs) : + Handle(std::move(a_rhs)) + {} + + + BSPointerHandle& operator=(const BSPointerHandle& a_rhs) + { + Handle::operator=(a_rhs); + return *this; + } + + + BSPointerHandle& operator=(BSPointerHandle&& a_rhs) + { + Handle::operator=(std::move(a_rhs)); + return *this; + } + + + template + BSPointerHandle& operator=(const Y* a_rhs) + { + if (a_rhs && a_rhs->handleRefObject.QRefCount() > 0) { + create(a_rhs); + } + else { + reset(); + } + return *this; + } + + + template + BSPointerHandle& operator=(const BSPointerHandle& a_rhs) + { + Handle::operator=(static_cast(a_rhs)); + return *this; + } + + void reset() + { + Handle::reset(); + } + + NiPointer get() const + { + NiPointer ptr; + lookup(ptr); + return ptr; + } + + + native_handle_type native_handle() + { + return Handle::value(); + } + + + operator bool() const + { + return Handle::has_value(); + } + + + friend bool operator==(const BSPointerHandle& a_lhs, const BSPointerHandle& a_rhs) + { + return static_cast(a_lhs) == static_cast(a_rhs); + } + + + friend bool operator!=(const BSPointerHandle& a_lhs, const BSPointerHandle& a_rhs) + { + return !(a_lhs == a_rhs); + } + +private: + void create(const T* a_ptr) + { + CreateHandleByREFR(*this, a_ptr); + } + + + bool lookup(NiPointer& a_refPtr) const + { + return LookupREFRByHandle(*this, a_refPtr); + } +}; + +template +class BSPointerHandleManager +{ +public: +}; +STATIC_ASSERT(sizeof(BSPointerHandleManager) == 0x1); + +class HandleManager : public BSPointerHandleManager> +{ +public: +}; +STATIC_ASSERT(sizeof(HandleManager) == 0x1); + +template +class BSPointerHandleManagerInterface +{ +public: + typedef T value_type; +}; + +template +class BSPointerHandleSmartPointer : public NiPointer +{ +public: +}; diff --git a/f4se/f4se/GameInput.h b/f4se/f4se/GameInput.h index 160b178..51ce05f 100644 --- a/f4se/f4se/GameInput.h +++ b/f4se/f4se/GameInput.h @@ -37,7 +37,7 @@ class InputEvent UInt64 eventType; // 10 InputEvent * next; // 18 UInt32 unk20; // 20 - UInt32 unk24; // 24 - When this != 2 it means stop processing + UInt32 handled; // 24 - When this != 2 it means stop processing }; STATIC_ASSERT(sizeof(InputEvent) == 0x28); @@ -231,7 +231,7 @@ class BSInputEventUser BSInputEventUser(bool bEnabled) : enabled(bEnabled) { } virtual ~BSInputEventUser() { }; - virtual bool IsEnabled(InputEvent * inputEvent = nullptr) { return enabled; }; + virtual bool ShouldHandleEvent(InputEvent * inputEvent = nullptr) { return enabled; }; virtual void OnKinectEvent(KinectEvent * inputEvent) { }; virtual void OnDeviceConnectEvent(DeviceConnectEvent * inputEvent) { }; virtual void OnThumbstickEvent(ThumbstickEvent * inputEvent) { }; diff --git a/f4se/f4se/GameMenus.cpp b/f4se/f4se/GameMenus.cpp index 353612b..5e37a7c 100644 --- a/f4se/f4se/GameMenus.cpp +++ b/f4se/f4se/GameMenus.cpp @@ -77,7 +77,7 @@ IMenu * UI::GetMenuByMovie(GFxMovieView * movie) bool UI::UnregisterMenu(BSFixedString & name, bool force) { - BSReadAndWriteLocker locker(g_menuTableLock); + BSWriteLocker locker(g_menuTableLock); MenuTableItem * item = menuTable.Find(&name); if (!item || (item->menuInstance && !force)) { return false; @@ -86,25 +86,18 @@ bool UI::UnregisterMenu(BSFixedString & name, bool force) return menuTable.Remove(&name); } -HUDComponentBase::HUDComponentBase(GFxValue * parent, const char * componentName, HUDContextArray * contextList) +HUDComponentBase::HUDComponentBase(GFxValue * parent, const char * componentName, const HUDModeInitParams* initParams) { - Impl_ctor(parent, componentName, contextList); + Impl_ctor(parent, componentName, initParams); } HUDComponentBase::~HUDComponentBase() { - for(UInt32 i = 0; i < contexts.count; i++) - { - contexts.entries[i].Release(); - } - - Heap_Free(contexts.entries); - contexts.count = 0; } void HUDComponentBase::UpdateVisibilityContext(void * unk1) { - HasHUDContext(&contexts, unk1); + HasHUDContext(&hudModes, unk1); bool bVisible = IsVisible(); double alpha = 0.0; if(bVisible) { @@ -116,12 +109,12 @@ void HUDComponentBase::UpdateVisibilityContext(void * unk1) SetExtDisplayInfoAlpha(unk2, alpha); SetExtDisplayInfo(&dInfo); - unkEC = bVisible == false; + fadeState = bVisible == false; } void HUDComponentBase::ColorizeComponent() { - SetFilterColor(isWarning); + SetFilterColor(bDisplayWarningColor); } GameMenuBase::GameMenuBase() : IMenu() @@ -132,4 +125,26 @@ GameMenuBase::GameMenuBase() : IMenu() GameMenuBase::~GameMenuBase() { Impl_dtor(); +} + +bool GameMenuBase::ShouldHandleEvent(InputEvent* inputEvent) +{ + if (inputEvent->handled != 2 && inputEvent->eventType == InputEvent::kEventType_Button) + { + ButtonEvent* buttonEvent = static_cast(inputEvent); + + if (buttonEvent->isDown == 0.0f) + { + if (buttonEvent->timer >= 0.0f) + { + return true; + } + } + else if (buttonEvent->timer == 0.0f) + { + return true; + } + } + + return false; } \ No newline at end of file diff --git a/f4se/f4se/GameMenus.h b/f4se/f4se/GameMenus.h index 90909ea..3e90dfa 100644 --- a/f4se/f4se/GameMenus.h +++ b/f4se/f4se/GameMenus.h @@ -10,6 +10,8 @@ #include "f4se/ScaleformCallbacks.h" #include "f4se/ScaleformValue.h" +#include "f4se/NiTypes.h" + typedef GFxValue* (*_GetChildElement)(GFxValue * parent, GFxValue & child, const char * path); extern RelocAddr<_GetChildElement> GetChildElement; @@ -49,45 +51,52 @@ class IMenu : public: enum { - //Confirmed - kFlag_PauseGame = 0x01, - kFlag_DoNotDeleteOnClose = 0x02, - kFlag_ShowCursor = 0x04, - kFlag_EnableMenuControl = 0x08, // 1, 2 - kFlag_ShaderdWorld = 0x20, - kFlag_Open = 0x40,//set it after open. - kFlag_DoNotPreventGameSave = 0x800, - kFlag_ApplyDropDownFilter = 0x8000, // - kFlag_BlurBackground = 0x400000, - - //Unconfirmed - kFlag_Modal = 0x10, - kFlag_PreventGameLoad = 0x80, - kFlag_Unk0100 = 0x100, - kFlag_HideOther = 0x200, - kFlag_DisableInteractive = 0x4000, - kFlag_UpdateCursorOnPlatformChange = 0x400, - kFlag_Unk1000 = 0x1000, - kFlag_ItemMenu = 0x2000, - kFlag_Unk10000 = 0x10000, // mouse cursor - kFlag_Unk800000 = 0x800000 + kFlag_None = 0, + kFlag_PausesGame = 1 << 0, + kFlag_AlwaysOpen = 1 << 1, + kFlag_UsesCursor = 1 << 2, + kFlag_UsesMenuContext = 1 << 3, + kFlag_Modal = 1 << 4, // prevents lower movies with this flag from advancing + kFlag_FreezeFrameBackground = 1 << 5, + kFlag_OnStack = 1 << 6, + kFlag_DisablePauseMenu = 1 << 7, + kFlag_RequiresUpdate = 1 << 8, + kFlag_TopmostRenderedMenu = 1 << 9, + kFlag_UpdateUsesCursor = 1 << 10, + kFlag_AllowSaving = 1 << 11, + kFlag_RendersOffscreenTargets = 1 << 12, + kFlag_InventoryItemMenu = 1 << 13, + kFlag_DontHideCursorWhenTopmost = 1 << 14, + kFlag_CustomRendering = 1 << 15, + kFlag_AssignCursorToRenderer = 1 << 16, + kFlag_ApplicationMenu = 1 << 17, + kFlag_HasButtonBar = 1 << 18, + kFlag_IsTopButtonBar = 1 << 19, + kFlag_AdvancesUnderPauseMenu = 1 << 20, + kFlag_RendersUnderPauseMenu = 1 << 21, + kFlag_UsesBlurredBackground = 1 << 22, + kFlag_CompanionAppAllowed = 1 << 23, + kFlag_FreezeFramePause = 1 << 24, + kFlag_SkipRenderDuringFreezeFrameScreenshot = 1 << 25, + kFlag_LargeScaleformRenderCacheMode = 1 << 26, + kFlag_UsesMovementToDirection = 1 << 27 }; virtual UInt32 ProcessMessage(UIMessage * msg) = 0;//??? - virtual void DrawNextFrame(float unk0, void * unk1) = 0; //210E8C0 - virtual void * Unk_05(void) { return nullptr; }; //return 0; - virtual void * Unk_06(void) { return nullptr; }; //return 0; - virtual bool Unk_07(UInt32 unk0, void * unk1) = 0; - virtual void Unk_08(UInt8 unk0) = 0; - virtual void Unk_09(BSFixedString & menuName, bool unk1) = 0; //UInt64 = 0; //UInt64 - virtual void Unk_0A(void) = 0; - virtual void Unk_0B(void) = 0; - virtual void Unk_0C(void) = 0; - virtual bool Unk_0D(bool unk0) = 0; - virtual bool Unk_0E(void) { return false; }; - virtual bool CanProcessControl(BSFixedString & controlID) { return false; }; - virtual bool Unk_10(void) = 0; - virtual void Unk_11(void) = 0; - virtual void Unk_12(void * unk0) = 0; + virtual void AdvanceMovie(float unk0, void * unk1) = 0; //210E8C0 + virtual void PreDisplay(void) { }; + virtual void PostDisplay(void) { }; + virtual bool PassesRenderConditionText(UInt32 menuRenderContext, const BSFixedString& unk1) = 0; + virtual void SetIsTopButtonBar(bool unk0) = 0; + virtual void OnMenuStackChanged(BSFixedString & menuName, bool unk1) = 0; //UInt64 = 0; //UInt64 + virtual void OnMenuDisplayStateChanged(void) = 0; + virtual void OnAddedToMenuStack(void) = 0; + virtual void OnRemovedFromMenuStack(void) = 0; + virtual bool CanAdvanceMovie(bool unk0) = 0; + virtual bool CanHandleWhenDisabled(const ButtonEvent*) { return false; }; + virtual bool OnButtonEventRelease(const BSFixedString& controlID) { return false; }; + virtual bool CacheShaderFXQuadsForRenderer_Impl(void) = 0; + virtual void TransferCachedShaderFXQuadsForRenderer(const BSFixedString&) = 0; + virtual void SetViewportRect(const NiRect& unk0) = 0; GFxValue stage; // 20 GFxMovieView * movie; // 40 @@ -137,6 +146,11 @@ class IMenu : STATIC_ASSERT(offsetof(IMenu, movie) == 0x40); STATIC_ASSERT(offsetof(IMenu, flags) == 0x58); +struct HUDModeType +{ + BSFixedString modeString; +}; + // E0 class GameMenuBase : public IMenu { @@ -145,31 +159,35 @@ class GameMenuBase : public IMenu virtual ~GameMenuBase(); // BSInputEventUser overrides + virtual bool ShouldHandleEvent(InputEvent* inputEvent /* = nullptr */) override; virtual void OnButtonEvent(ButtonEvent * inputEvent) override { Impl_OnGameMenuBaseButtonEvent(inputEvent); }; // IMenu overrides virtual void Invoke(Args * args) override { } virtual void RegisterFunctions() override { } virtual UInt32 ProcessMessage(UIMessage * msg) override { return Impl_ProcessMessage(msg); };//??? - virtual void DrawNextFrame(float unk0, void * unk1) override { return Impl_DrawNextFrame(unk0, unk1); }; //render,HUD menu uses this function to update its HUD components. - virtual bool Unk_07(UInt32 unk0, void * unk1) override { return Impl_Unk07(unk0, unk1); }; - virtual void Unk_08(UInt8 unk0) override { return Impl_Unk08(unk0); }; - virtual void Unk_09(BSFixedString & menuName, bool unk1) override { return Impl_Unk09(menuName, unk1); }; //UInt64 - virtual void Unk_0A(void) override { return Impl_Unk0A(); }; - virtual void Unk_0B(void) override { return Impl_Unk0B(); } - virtual void Unk_0C(void) override { return Impl_Unk0C(); }; - virtual bool Unk_0D(bool unk0) override { return Impl_Unk0D(unk0); } - virtual bool Unk_0E(void) override { return false; }; - virtual bool CanProcessControl(BSFixedString & controlID) override { return false; }; - virtual bool Unk_10(void) override { return Impl_Unk10(); } //90 - E0 - virtual void Unk_11(void) override { return Impl_Unk11(); }; - virtual void Unk_12(void * unk0) override { return Impl_Unk12(unk0); } - virtual void Unk_13(void * unk0, void * unk1) { return Impl_Unk13(unk0, unk1); } - - tArray subcomponents; // 70 - BSGFxShaderFXTarget * shaderTarget; // 88 - void * unk90; // 90 - UInt64 unk98[(0xE0 - 0x98) >> 3]; // 98 + virtual void AdvanceMovie(float unk0, void * unk1) override { return Impl_AdvanceMovie(unk0, unk1); }; //render,HUD menu uses this function to update its HUD components. + virtual bool PassesRenderConditionText(UInt32 menuRenderContext, const BSFixedString& unk1) override { return Impl_PassesRenderConditionText(menuRenderContext, unk1); }; + virtual void SetIsTopButtonBar(bool unk0) override { return Impl_SetIsTopButtonBar(unk0); }; + virtual void OnMenuStackChanged(BSFixedString & menuName, bool unk1) override { return Impl_OnMenuStackChanged(menuName, unk1); }; //UInt64 + virtual void OnMenuDisplayStateChanged(void) override { return Impl_OnMenuDisplayStateChanged(); }; + virtual void OnAddedToMenuStack(void) override { return Impl_OnAddedToMenuStack(); } + virtual void OnRemovedFromMenuStack(void) override { return Impl_OnRemovedFromMenuStack(); }; + virtual bool CanAdvanceMovie(bool unk0) override { return Impl_CanAdvanceMovie(unk0); } + virtual bool CanHandleWhenDisabled(const ButtonEvent*) override { return false; }; + virtual bool OnButtonEventRelease(const BSFixedString & controlID) override { return false; }; + virtual bool CacheShaderFXQuadsForRenderer_Impl(void) override { return Impl_CacheShaderFXQuadsForRenderer(); } //90 - E0 + virtual void TransferCachedShaderFXQuadsForRenderer(const BSFixedString& unk1) override { return Impl_TransferCachedShaderFXQuadsForRenderer(unk1); }; + virtual void SetViewportRect(const NiRect& unk0) override { return Impl_SetViewportRect(unk0); } + virtual void AppendShaderFXInfos(BSTArray* colorFX, BSTArray* backgroundFX) { return Impl_AppendShaderFXInfos(colorFX, backgroundFX); } + + BSTArray shaderFXObjects; + BSGFxShaderFXTarget* filterHolder; + class ButtonHintBar* buttonHintBar; + BSTArray cachedColorFX; + BSTArray cachedBackgroundFX; + BSReadWriteLock cachedQuadsLock; + BSTOptional MenuHUDMode; DEFINE_STATIC_HEAP(ScaleformHeap_Allocate, ScaleformHeap_Free) private: @@ -177,21 +195,22 @@ class GameMenuBase : public IMenu DEFINE_MEMBER_FN_0(Impl_ctor, void *, 0x00B324E0); DEFINE_MEMBER_FN_0(Impl_dtor, void *, 0x00B325A0); - DEFINE_MEMBER_FN_2(Impl_DrawNextFrame, void, 0x0210EED0, float unk0, void * unk1); + DEFINE_MEMBER_FN_2(Impl_AdvanceMovie, void, 0x0210EED0, float unk0, void * unk1); DEFINE_MEMBER_FN_1(Impl_ProcessMessage, UInt32, 0x0210EE50, UIMessage * msg); - DEFINE_MEMBER_FN_2(Impl_Unk07, bool, 0x0210F310, UInt32 unk0, void * unk1); - DEFINE_MEMBER_FN_1(Impl_Unk08, void, 0x00B32A50, UInt8 unk0); - DEFINE_MEMBER_FN_2(Impl_Unk09, void, 0x0210F550, BSFixedString & menuName, bool unk1); - DEFINE_MEMBER_FN_0(Impl_Unk0A, void, 0x00B32AC0); - DEFINE_MEMBER_FN_0(Impl_Unk0B, void, 0x00B32B80); - DEFINE_MEMBER_FN_0(Impl_Unk0C, void, 0x00B32BC0) - DEFINE_MEMBER_FN_1(Impl_Unk0D, bool, 0x0210F6A0, bool unk0); - DEFINE_MEMBER_FN_0(Impl_Unk10, bool, 0x00B32870); - DEFINE_MEMBER_FN_0(Impl_Unk11, void, 0x00B32900); - DEFINE_MEMBER_FN_1(Impl_Unk12, void, 0x00B32970, void * unk0); - DEFINE_MEMBER_FN_2(Impl_Unk13, void, 0x00B329C0, void * unk0, void * unk1); + DEFINE_MEMBER_FN_2(Impl_PassesRenderConditionText, bool, 0x0210F310, UInt32 unk0, const BSFixedString& unk1); + DEFINE_MEMBER_FN_1(Impl_SetIsTopButtonBar, void, 0x00B32A50, UInt8 unk0); + DEFINE_MEMBER_FN_2(Impl_OnMenuStackChanged, void, 0x0210F550, BSFixedString & menuName, bool unk1); + DEFINE_MEMBER_FN_0(Impl_OnMenuDisplayStateChanged, void, 0x00B32AC0); + DEFINE_MEMBER_FN_0(Impl_OnAddedToMenuStack, void, 0x00B32B80); + DEFINE_MEMBER_FN_0(Impl_OnRemovedFromMenuStack, void, 0x00B32BC0) + DEFINE_MEMBER_FN_1(Impl_CanAdvanceMovie, bool, 0x0210F6A0, bool unk0); + DEFINE_MEMBER_FN_0(Impl_CacheShaderFXQuadsForRenderer, bool, 0x00B32870); + DEFINE_MEMBER_FN_1(Impl_TransferCachedShaderFXQuadsForRenderer, void, 0x00B32900, const BSFixedString& unk1); + DEFINE_MEMBER_FN_1(Impl_SetViewportRect, void, 0x00B32970, const NiRect& unk0); + DEFINE_MEMBER_FN_2(Impl_AppendShaderFXInfos, void, 0x00B329C0, BSTArray* colorFX, BSTArray* backgroundFX); }; -STATIC_ASSERT(offsetof(GameMenuBase, shaderTarget) == 0x88); +STATIC_ASSERT(offsetof(GameMenuBase, filterHolder) == 0x88); +STATIC_ASSERT(sizeof(GameMenuBase) == 0xE0); // 218 class LooksMenu : public GameMenuBase @@ -213,25 +232,41 @@ class LooksMenu : public GameMenuBase }; STATIC_ASSERT(offsetof(LooksMenu, nextBoneID) == 0x150); +enum PowerArmorHUDVisibilityRule +{ + kPowerArmorHUDVisibilityRule_Ignores_PA_HUD_Visibility = 0x0, + kPowerArmorHUDVisibilityRule_OnlyWithPAHUDVisible, + kPowerArmorHUDVisibilityRule_OnlyWithPAHUDNotVisible +}; + // 20 -template -class HUDContextArray +struct HUDModes { -public: - T * entries; // 00 - UInt32 count; // 08 - UInt32 unk0C; // 0C - UInt32 flags; // 10 - UInt32 unk14; // 14 - UInt32 unk18; // 18 - bool unk1C; // 1C + BSTArray validHUDModes; + PowerArmorHUDVisibilityRule powerArmorHUDVisibilityRule; + bool bCanBeVisible; +}; + +// +struct HUDModeInitParams +{ + const HUDModeType* validHUDModes; + UInt32 numHUDModes; + PowerArmorHUDVisibilityRule powerArmorHUDVisibilityRule; +}; + +struct CountdownTimer +{ + UInt64 StartTime; + UInt64 EndTime; + bool IsActive; }; // F8 class HUDComponentBase : public BSGFxShaderFXTarget { public: - HUDComponentBase(GFxValue * parent, const char * componentName, HUDContextArray * contextList); + HUDComponentBase(GFxValue * parent, const char * componentName, const HUDModeInitParams* initParams); virtual ~HUDComponentBase(); virtual bool Unk_02(void * unk1) { return false; } @@ -240,30 +275,28 @@ class HUDComponentBase : public BSGFxShaderFXTarget virtual void UpdateVisibilityContext(void * unk1); virtual void ColorizeComponent(); virtual bool IsVisible() { return Impl_IsVisible(); } - virtual bool Unk_08() { return contexts.unk1C; } - - UInt64 unkB0; // B0 - UInt64 unkB8; // B8 - UInt64 unkC0; // C0 - HUDContextArray contexts; // C8 - float unkE8; // E8 - UInt32 unkEC; // EC - UInt8 unkF0; // F0 - UInt8 unkF1; // F1 - bool isWarning; // F2 - This chooses the warning color over the default color - UInt8 padF3[5]; // F3 + virtual bool Unk_08() { return hudModes.bCanBeVisible; } + + CountdownTimer fadeOutTimer; // B0 + HUDModes hudModes; // C8 + float fadePerSecond; // E8 + UInt32 fadeState; // EC + bool bFlashElementsRegistered; // F0 + bool bWidgetDisplayEnabled; // F1 + bool bDisplayWarningColor; // F2 - This chooses the warning color over the default color + UInt8 padF3[5]; // F3 MEMBER_FN_PREFIX(HUDComponentBase); - DEFINE_MEMBER_FN_3(Impl_ctor, HUDComponentBase *, 0x00A22A70, GFxValue * parent, const char * componentName, HUDContextArray * contextList); + DEFINE_MEMBER_FN_3(Impl_ctor, HUDComponentBase *, 0x00A22A70, GFxValue * parent, const char * componentName, const HUDModeInitParams* initParams); DEFINE_MEMBER_FN_0(Impl_IsVisible, bool, 0x00A22DB0); DEFINE_MEMBER_FN_0(Impl_UpdateComponent, void, 0x00A22B10); }; -STATIC_ASSERT(offsetof(HUDComponentBase, contexts) == 0xC8); -STATIC_ASSERT(offsetof(HUDComponentBase, unkE8) == 0xE8); +STATIC_ASSERT(offsetof(HUDComponentBase, hudModes) == 0xC8); +STATIC_ASSERT(offsetof(HUDComponentBase, fadePerSecond) == 0xE8); STATIC_ASSERT(sizeof(HUDComponentBase) == 0xF8); -typedef bool (* _HasHUDContext)(HUDContextArray * contexts, void * unk1); +typedef bool (* _HasHUDContext)(HUDModes* contexts, void * unk1); extern RelocAddr <_HasHUDContext> HasHUDContext; @@ -395,6 +428,8 @@ class MenuTableItem } }; +extern RelocPtr g_menuTableLock; + // 250 ? class UI { @@ -418,9 +453,8 @@ class UI template void ForEachMenu(T & menuFunc) { - g_menuTableLock->LockForReadAndWrite(); + const BSWriteLocker l(g_menuTableLock.GetPtr()); menuTable.ForEach(menuFunc); - g_menuTableLock->Release(); } bool UnregisterMenu(BSFixedString & name, bool force = false); @@ -444,5 +478,4 @@ class UI DEFINE_MEMBER_FN(IsMenuOpen, bool, 0x02042160, const BSFixedString & name); }; -extern RelocPtr g_menuTableLock; extern RelocPtr g_ui; diff --git a/f4se/f4se/GameObjects.h b/f4se/f4se/GameObjects.h index 7d6feb9..5321b5b 100644 --- a/f4se/f4se/GameObjects.h +++ b/f4se/f4se/GameObjects.h @@ -20,6 +20,7 @@ class SpellItem; class TESObjectMISC; class BGSDamageType; class VirtualMachine; +class BGSCollisionLayer; typedef TESObjectREFR* (* _PlaceAtMe_Native)(VirtualMachine* vm, UInt32 stackId, TESObjectREFR** target, TESForm* form, SInt32 count, bool bForcePersist, bool bInitiallyDisabled, bool bDeleteWhenAble); extern RelocAddr<_PlaceAtMe_Native> PlaceAtMe_Native; @@ -778,8 +779,40 @@ class BGSProjectile : public TESBoundObject BGSPreloadable preloadable; // A8 BGSDestructibleObjectForm destructible; // B0 - UInt64 unkC0[(0x188 - 0xC0) >> 3]; + struct BGSProjectileData + { + UInt32 flags; // 00 + float gravity; // 04 + float speed; // 08 + float range; // 0C + TESObjectLIGH* light; // 10 + TESObjectLIGH* muzzleFlashLight; // 18 + float explosionProximity; // 20 + float explosionTimer; // 24 + BGSExplosion* explosionType; // 28 + BGSSoundDescriptorForm* activeSoundLoop; // 30 + float muzzleFlashDuration; // 38 + float fadeOutTime; // 3C + float force; // 40 + BGSSoundDescriptorForm* countdownSound; // 48 + BGSSoundDescriptorForm* deactivateSound; // 50 + TESObjectWEAP* defaultWeaponSource; // 58 + float coneSpread; // 60 + float collisionRadius; // 64 + float lifetime; // 68 + float relaunchInterval; // 6C + BGSTextureSet* decalData; // 70 + BGSCollisionLayer* collisionLayer; // 78 + BGSProjectile* VATSProjectile; // 80 + SInt8 tracerFrequency; // 88 + }; + + BGSProjectileData projectileData; // C0 + TESModel muzzleFlashModel; // 150 + UInt32 soundLevel; // 180 }; +STATIC_ASSERT(sizeof(BGSProjectile) == 0x188); +STATIC_ASSERT(sizeof(BGSProjectile::BGSProjectileData) == 0x90); // D8 class TESLevCharacter : public TESBoundAnimObject diff --git a/f4se/f4se/GameReferences.cpp b/f4se/f4se/GameReferences.cpp index b1f832a..7ebd6d7 100644 --- a/f4se/f4se/GameReferences.cpp +++ b/f4se/f4se/GameReferences.cpp @@ -5,15 +5,6 @@ // 1FA931E3C3B406454210A0EDC37BDD0C84C8C04A+6B RelocPtr g_player(0x05AA4388); -// 0BE70664D4DF11A3F88748D9CB45B23D0B4FD50C+2F -RelocAddr <_LookupREFRByHandle> LookupREFRByHandle(0x0000AB60); - -// 4369DFCC9BCC88536EEB89E5A107B60941016295+26 -RelocAddr <_CreateHandleByREFR> CreateHandleByREFR(0x0000A8A0); - -// 3A53807E195FFAEA7AA0EF7FD42D84E4EA0B755A+D0 -RelocPtr g_invalidRefHandle(0x038CCE04); - RelocAddr <_HasDetectionLOS> HasDetectionLOS(0x0135B680); RelocAddr <_GetLinkedRef_Native> GetLinkedRef_Native(0x00481000); @@ -22,20 +13,6 @@ RelocAddr <_SetLinkedRef_Native> SetLinkedRef_Native(0x00481020); RelocAddr <_MoveRefrToPosition> MoveRefrToPosition(0x013FE7E0); -UInt32 TESObjectREFR::CreateRefHandle(void) -{ - if (handleRefObject.GetRefCount() > 0) - { - UInt32 refHandle = 0; - CreateHandleByREFR(&refHandle, this); - return refHandle; - } - else - { - return *g_invalidRefHandle; - } -} - bool Actor::GetEquippedExtraData(UInt32 slotIndex, ExtraDataList ** extraData) { // Invalid slot id diff --git a/f4se/f4se/GameReferences.h b/f4se/f4se/GameReferences.h index 679bbb6..2b7e65d 100644 --- a/f4se/f4se/GameReferences.h +++ b/f4se/f4se/GameReferences.h @@ -3,6 +3,7 @@ #include "f4se/GameForms.h" #include "f4se/GameEvents.h" #include "f4se/GameCustomization.h" +#include "f4se/GameHandle.h" #include "f4se/NiObjects.h" class BSActiveGraphIfInactiveEvent; @@ -15,14 +16,6 @@ class TESWorldSpace; class BGSScene; class TESQuest; -typedef bool (* _CreateHandleByREFR)(UInt32 * handle, TESObjectREFR * ref); -extern RelocAddr <_CreateHandleByREFR> CreateHandleByREFR; - -typedef bool (* _LookupREFRByHandle)(UInt32 * handle, TESObjectREFR ** ref); -extern RelocAddr <_LookupREFRByHandle> LookupREFRByHandle; - -extern RelocPtr g_invalidRefHandle; - typedef bool (* _HasDetectionLOS)(Actor* source, TESObjectREFR* target, UInt8 * unk1); extern RelocAddr<_HasDetectionLOS> HasDetectionLOS; @@ -54,6 +47,11 @@ class BSHandleRefObject : public NiRefObject if((InterlockedDecrement(&m_uiRefCount) & kMask_RefCount) == 0) DeleteThis(); } + + UInt32 QRefCount() const + { + return m_uiRefCount & kMask_RefCount; + } }; // 110 @@ -238,7 +236,8 @@ class TESObjectREFR : public TESForm UInt32 unk104; // 104 UInt32 unk108; // 108 - UInt32 CreateRefHandle(void); + void IncRef() { handleRefObject.IncRef(); } + void DecRef() { handleRefObject.DecRef(); } MEMBER_FN_PREFIX(TESObjectREFR); DEFINE_MEMBER_FN(GetReferenceName, const char *, 0x0040B760); @@ -408,9 +407,9 @@ class Actor : public TESObjectREFR EquippedWeaponData * equippedData; // 20 }; - EquipData * equipData; // 288 + tArray equipData; // 288 - UInt64 unk290[(0x3A0 - 0x290) >> 3]; + UInt64 unk290[(0x3A0 - 0x2A0) >> 3]; UInt32 unk3A0; // 3A0 UInt32 furnitureHandle1; // 3A4 UInt32 furnitureHandle2; // 3A8 @@ -426,6 +425,7 @@ class Actor : public TESObjectREFR UInt16 unk494; // 494 UInt16 unk496; // 496 - somekind of dirty flag? }; + STATIC_ASSERT(offsetof(Data08, furnitureHandle1) == 0x3A4); Data08 * unk08; // 08 @@ -455,7 +455,10 @@ class Actor : public TESObjectREFR TESRace * race; // 418 UInt64 unk420; // 420 ActorEquipData * equipData; // 428 - UInt64 unk430[(0x490-0x430)/8]; // 430 + UInt64 unk430; // 430 + UInt32 unk438; // 438 + UInt32 uiFlags; // 43C + UInt64 unk440[(0x490-0x440)/8]; // 440 bool IsPlayerTeammate() { @@ -466,10 +469,12 @@ class Actor : public TESObjectREFR MEMBER_FN_PREFIX(Actor); DEFINE_MEMBER_FN(QueueUpdate, void, 0x00D8A1F0, bool bDoFaceGen, UInt32 unk2, bool DoQueue, UInt32 flags); // 0, 0, 1, 0 DEFINE_MEMBER_FN(IsHostileToActor, bool, 0x00D91080, Actor * actor); - DEFINE_MEMBER_FN(UpdateEquipment, void, 0x00408270); + DEFINE_MEMBER_FN(UpdateEquipment, void, 0x00408270); // TESObjectREFR::ReplaceModel }; STATIC_ASSERT(offsetof(Actor, equipData) == 0x428); +STATIC_ASSERT(offsetof(Actor, uiFlags) == 0x43C); STATIC_ASSERT(offsetof(Actor::MiddleProcess::Data08, equipData) == 0x288); +STATIC_ASSERT(sizeof(Actor) == 0x490); // E10 class PlayerCharacter : public Actor diff --git a/f4se/f4se/GameTypes.cpp b/f4se/f4se/GameTypes.cpp index 911745a..f00842d 100644 --- a/f4se/f4se/GameTypes.cpp +++ b/f4se/f4se/GameTypes.cpp @@ -138,95 +138,3 @@ void BSReadWriteLock::LockForWrite() } } */ - -void BSReadWriteLock::LockForReadAndWrite() -{ - SInt32 myThreadID = GetCurrentThreadId(); - - if (threadID == myThreadID) - { - InterlockedIncrement(&lockValue); - } - else - { - UInt32 spinCount = 0; - while (InterlockedCompareExchange(&lockValue, 1, 0) != 1) - Sleep(++spinCount >= kFastSpinThreshold ? 1 : 0); - - threadID = myThreadID; - _mm_mfence(); - } -} - -bool BSReadWriteLock::TryLockForWrite() -{ - SInt32 myThreadID = GetCurrentThreadId(); - - bool result = false; - if (threadID == myThreadID) - { - InterlockedIncrement(&lockValue); - result = true; - } - else - { - result = InterlockedCompareExchange(&lockValue, UInt32(1 | kLockWrite), 0) == UInt32(1 | kLockWrite); - if (result) - { - threadID = myThreadID; - _mm_mfence(); - } - } - return result; -} -bool BSReadWriteLock::TryLockForRead() -{ - SInt32 myThreadID = GetCurrentThreadId(); - - bool result = false; - if (threadID == myThreadID) - { - InterlockedIncrement(&lockValue); - result = true; - } - else - { - UInt32 lockCount = lockValue & kLockCountMask; - UInt32 lockResult = InterlockedCompareExchange(&lockValue, lockCount + 1, lockCount); - while ((lockResult & kLockWrite) == 0) - { - if (lockResult == lockCount) - break; - - lockCount = lockResult & kLockCountMask; - lockResult = InterlockedCompareExchange(&lockValue, lockCount + 1, lockCount); - } - - result = ~(lockResult >> 31) & 1; - } - - return result; -} - -void BSReadWriteLock::Unlock() -{ - SInt32 myThreadID = GetCurrentThreadId(); - if (threadID == myThreadID) - { - UInt32 lockCount = lockValue - 1; - if (lockValue == kLockWrite) - { - threadID = 0; - _mm_mfence(); - InterlockedExchange(&lockValue, 0); - } - else - { - InterlockedDecrement(&lockValue); - } - } - else - { - InterlockedDecrement(&lockValue); - } -} \ No newline at end of file diff --git a/f4se/f4se/GameTypes.h b/f4se/f4se/GameTypes.h index 1450af5..ca63b1a 100644 --- a/f4se/f4se/GameTypes.h +++ b/f4se/f4se/GameTypes.h @@ -2,6 +2,7 @@ #include "f4se_common/Utilities.h" #include "f4se/GameAPI.h" +#include "f4se/GameUtilities.h" class TESForm; @@ -53,18 +54,11 @@ class BSReadWriteLock public: BSReadWriteLock() : threadID(0), lockValue(0) {} - //void LockForRead(); - //void LockForWrite(); - DEFINE_MEMBER_FN_0(LockForRead, void, 0x01B10930); DEFINE_MEMBER_FN_0(LockForWrite, void, 0x01B109B0); - void LockForReadAndWrite(); - - bool TryLockForWrite(); - bool TryLockForRead(); - - void Unlock(); + DEFINE_MEMBER_FN_0(UnlockRead, void, 0x01B10BF0); + DEFINE_MEMBER_FN_0(UnlockWrite, void, 0x01B10C00); }; STATIC_ASSERT(sizeof(BSReadWriteLock) == 0x8); @@ -72,7 +66,7 @@ class BSReadLocker { public: BSReadLocker(BSReadWriteLock * lock) { m_lock = lock; m_lock->LockForRead(); } - ~BSReadLocker() { m_lock->Unlock(); } + ~BSReadLocker() { m_lock->UnlockRead(); } protected: BSReadWriteLock * m_lock; @@ -82,17 +76,7 @@ class BSWriteLocker { public: BSWriteLocker(BSReadWriteLock * lock) { m_lock = lock; m_lock->LockForWrite(); } - ~BSWriteLocker() { m_lock->Unlock(); } - -protected: - BSReadWriteLock * m_lock; -}; - -class BSReadAndWriteLocker -{ -public: - BSReadAndWriteLocker(BSReadWriteLock * lock) { m_lock = lock; m_lock->LockForReadAndWrite(); } - ~BSReadAndWriteLocker() { m_lock->Unlock(); } + ~BSWriteLocker() { m_lock->UnlockWrite(); } protected: BSReadWriteLock * m_lock; @@ -440,6 +424,13 @@ class tArray DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) }; +// Because aliasing via using doesn't work in this version of C++ +template +class BSTArray : public tArray +{ +public: +}; + template class tMutexArray : public tArray { @@ -718,7 +709,7 @@ class tList NodePos pos = GetNthNode(index); _Node* pTargetNode = pos.node; - _Node* newNode = (_Node*)FormHeap_Allocate(sizeof(newNode)); + _Node* newNode = (_Node*)FormHeap_Allocate(sizeof(_Node)); if (newNode && pTargetNode) { if (index == eListEnd) { pTargetNode->next = newNode; @@ -785,11 +776,6 @@ class tList return curIt; } - const _Node* FindString(char* str, Iterator prev) const - { - return Find(StringFinder_CI(str), prev); - } - template UInt32 CountIf(Op& op) const { @@ -1211,7 +1197,7 @@ class tHashSet } template - void ForEach(T& functor) + void ForEach(const T& functor) { if (!m_entries) return; @@ -1267,6 +1253,8 @@ class tHashSet template typename tHashSet::_Entry tHashSet::sentinel = tHashSet::_Entry(); +STATIC_ASSERT(sizeof(tHashSet) == 0x30); + template class SafeDataHolder { @@ -1278,3 +1266,727 @@ class SafeDataHolder void Lock(void) { m_lock.Lock(); } void Release(void) { m_lock.Release(); } }; + +#if 0 +// Because we cant use 'using' in this version +template +class BSTHashMap : public tHashSet +{ +public: +}; +#endif + +template +struct BSTTuple +{ +public: + BSTTuple() : + first(), + second() + {} + + BSTTuple(const T1& a_first, const T2& a_second) : + first(a_first), + second(a_second) + {} + + BSTTuple& operator=(const BSTTuple& a_rhs) + { + first = a_rhs.first; + second = a_rhs.second; + } + + + BSTTuple& operator=(BSTTuple&& a_rhs) + { + first = std::move(a_rhs.first); + second = std::move(a_rhs.second); + } + + T1 first; // 00 + T2 second; // ?? +}; + +template class Allocator, class Hash, class KeyEqual> +struct BSTScatterTable +{ +public: + typedef Traits traits_type; + typedef typename traits_type::key_type key_type; + typedef typename traits_type::mapped_type mapped_type; + typedef typename traits_type::value_type value_type; + typedef UInt32 size_type; + typedef Hash hasher; + typedef KeyEqual key_equal; + + struct BSTScatterTableEntry + { + public: + BSTScatterTableEntry() : + value(), + next(0) + {} + + + BSTScatterTableEntry(const BSTScatterTableEntry& a_rhs) : + value(a_rhs.value), + next(a_rhs.next) + {} + + + BSTScatterTableEntry(BSTScatterTableEntry&& a_rhs) : + value(std::move(a_rhs.value)), + next(std::move(a_rhs.next)) + { + a_rhs.next = 0; + } + + + BSTScatterTableEntry& operator=(const BSTScatterTableEntry& a_rhs) + { + value = a_rhs.value; + next = a_rhs.next; + return *this; + } + + + BSTScatterTableEntry& operator=(BSTScatterTableEntry&& a_rhs) + { + value = std::move(a_rhs.value); + next = std::move(a_rhs.next); + a_rhs.next = 0; + + return *this; + } + + + value_type value; // 00 + BSTScatterTableEntry* next; // ?? + }; + + + typedef BSTScatterTableEntry entry_type; + typedef Allocator allocator_type; + + static entry_type sentinel; + + template + struct iterator_base + { + public: + typedef std::ptrdiff_t difference_type; + typedef U value_type; + typedef U* pointer; + typedef U& reference; + typedef std::forward_iterator_tag iterator_category; + + + iterator_base() : + _entry(nullptr), + _end(nullptr) + {} + + + iterator_base(const iterator_base& a_rhs) : + _entry(a_rhs._entry), + _end(a_rhs._end) + {} + + + iterator_base(iterator_base&& a_rhs) : + _entry(std::move(a_rhs._entry)), + _end(a_rhs._end) + { + a_rhs._entry = a_rhs._end; + } + + + iterator_base(entry_type* a_entry, entry_type* a_end) : + _entry(a_entry), + _end(a_end) + { + while (_entry != _end && !_entry->next) { + ++_entry; + } + } + + + ~iterator_base() + {} + + + iterator_base& operator=(const iterator_base& a_rhs) + { + ASSERT(_end == a_rhs._end); + _entry = a_rhs._entry; + } + + + iterator_base& operator=(iterator_base&& a_rhs) + { + ASSERT(_end == a_rhs._end); + _entry = std::move(a_rhs._entry); + a_rhs._entry = a_rhs._end; + } + + + void swap(iterator_base& a_rhs) + { + ASSERT(_end == a_rhs._end); + std::swap(_entry, a_rhs._entry); + } + + + reference operator*() const + { + ASSERT(_entry != _end); + return _entry->value; + } + + + pointer operator->() const + { + return std::addressof(_entry->value); + } + + + bool operator==(const iterator_base& a_rhs) const + { + ASSERT(_end == a_rhs._end); + return _entry == a_rhs._entry; + } + + + bool operator!=(const iterator_base& a_rhs) const + { + ASSERT(_end == a_rhs._end); + return !operator==(a_rhs); + } + + + // prefix + iterator_base& operator++() + { + ASSERT(_entry != _end); + do { + ++_entry; + } while (_entry != _end && !_entry->next); + return *this; + } + + + // postfix + iterator_base operator++(int) + { + iterator_base tmp{ *this }; + operator++(); + return tmp; + } + + private: + entry_type* _entry; + entry_type* _end; + }; + + + typedef iterator_base iterator; + typedef iterator_base const_iterator; + + + BSTScatterTable() : + _pad00(0), + _pad08(0), + _capacity(0), + _freeCount(0), + _freeIdx(0), + _sentinel((const entry_type*)&sentinel), + _allocator() + {} + + DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) + + iterator begin() + { + return get_entries() ? make_iterator(get_entries()) : iterator(); + } + + + const_iterator begin() const + { + return get_entries() ? make_iterator(get_entries()) : const_iterator(); + } + + + const_iterator cbegin() const + { + return begin(); + } + + + iterator end() + { + return get_entries() ? make_iterator(get_entries() + _capacity) : iterator(); + } + + + const_iterator end() const + { + return get_entries() ? make_iterator(get_entries() + _capacity) : const_iterator(); + } + + + const_iterator cend() const + { + return end(); + } + + + bool empty() const + { + return !get_entries() || _freeCount == 0; + } + + + size_type size() const + { + return _capacity - _freeCount; + } + + + size_type max_size() const + { + return _allocator.max_size(); + } + + + std::pair insert(const value_type& a_value) + { + return insert_impl(false, a_value); + } + + + std::pair insert(value_type&& a_value) + { + return insert_impl(false, std::move(a_value)); + } + + + std::pair insert_or_assign(const value_type& a_value) + { + return insert_impl(true, a_value); + } + + + std::pair insert_or_assign(value_type&& a_value) + { + return insert_impl(true, std::move(a_value)); + } + + + size_type erase(const key_type& a_key) + { + if (!get_entries()) { // no entries + return 0; + } + + auto entry = calc_pos(a_key); + if (!entry->next) { // key not in table + return 0; + } + + entry_type* tail = 0; + while (!comp_key(get_key(entry->value), a_key)) { // find key in table + tail = entry; + entry = entry->next; + if (entry == _sentinel) { + return 0; + } + } + + entry->value.~value_type(); + + if (entry->next == _sentinel) { // if no chain + if (tail) { + tail->next = const_cast(_sentinel); + } + entry->next = 0; + } + else { // else move next entry into current + new (entry) entry_type(std::move(*entry->next)); + } + + ++_freeCount; + return 1; + } + + + iterator find(const key_type& a_key) + { + auto entry = find_impl(a_key); + return entry ? make_iterator(entry) : end(); + } + + + const_iterator find(const key_type& a_key) const + { + auto entry = find_impl(a_key); + return entry ? make_iterator(entry) : end(); + } + + + void reserve(size_type a_count) + { + if (a_count <= _capacity) { + return; + } + + UInt32 leftShifts = 0; + while ((a_count & static_cast(1 << 31)) == 0) { + a_count <<= 1; + ++leftShifts; + } + auto bitPos = 31 - leftShifts; + auto newCount = static_cast(1 << bitPos); + grow(newCount); + } + + + hasher hash_function() const + { + return hasher(); + } + + + key_equal key_eq() const + { + return key_equal(); + } + +private: + entry_type* find_impl(const key_type& a_key) const + { + if (!get_entries()) { + return nullptr; + } + + auto probe = calc_pos(a_key); // try ideal pos + if (!probe->next) { + return nullptr; // nothing there + } + + do { + if (comp_key(get_key(probe->value), a_key)) { + return probe; + } + else { + probe = probe->next; + } + } while (probe != _sentinel); // follow chain + + return nullptr; + } + + + template + std::pair insert_impl(bool a_overwrite, Arg&& a_value) + { + if (!get_entries() || !_freeCount) { + if (!grow()) { + return std::make_pair(end(), false); // maybe throw? + } + } + + auto idealEntry = calc_pos(get_key(a_value)); + if (!idealEntry->next) { // if slot empty + new (std::addressof(idealEntry->value)) value_type(std::forward(a_value)); + idealEntry->next = const_cast(_sentinel); + return std::make_pair(make_iterator(idealEntry), true); + } + + for (auto iter = idealEntry; iter != _sentinel; iter = iter->next) { + if (comp_key(get_key(iter->value), get_key(a_value))) { // if entry already in table + if (a_overwrite) { + iter->value.~value_type(); + new (std::addressof(iter->value)) value_type(std::forward(a_value)); + } + return std::make_pair(make_iterator(iter), false); + } + } + + auto freeEntry = get_free_entry(); + + auto takenIdealEntry = calc_pos(get_key(idealEntry->value)); + if (takenIdealEntry == idealEntry) { // if entry occupying our slot would've hashed here anyway + freeEntry->next = idealEntry->next; + idealEntry->next = freeEntry; + new (std::addressof(freeEntry->value)) value_type(std::forward(a_value)); + return std::make_pair(make_iterator(freeEntry), true); + } + + while (takenIdealEntry->next != idealEntry) { // find entry that links here + takenIdealEntry = takenIdealEntry->next; + } + + // move taken slot out, so we can move in + new (std::addressof(freeEntry->value)) value_type(std::move(idealEntry->value)); + freeEntry->next = idealEntry->next; + takenIdealEntry->next = freeEntry; + new (std::addressof(idealEntry->value)) value_type(std::forward(a_value)); + idealEntry->next = const_cast(_sentinel); + return std::make_pair(make_iterator(idealEntry), true); + } + + + iterator make_iterator(entry_type* a_entry) + { + return iterator(a_entry, get_entries() + _capacity); + } + + + const_iterator make_iterator(entry_type* a_entry) const + { + return const_iterator(a_entry, get_entries() + _capacity); + } + + + UInt32 calc_hash(const key_type& a_key) const + { + return hash_function()(a_key); + } + + + UInt32 calc_idx(const key_type& a_key) const + { + return calc_hash(a_key) & (_capacity - 1); // capacity is always a factor of 2, so this is a faster modulo + } + + + entry_type* calc_pos(const key_type& a_key) const + { + return const_cast(get_entries() + calc_idx(a_key)); + } + + + // assumes not empty + entry_type* get_free_entry() + { + entry_type* entry = nullptr; + do { + _freeIdx = (_capacity - 1) & (_freeIdx - 1); + entry = get_entries() + _freeIdx; + } while (entry->next); + + --_freeCount; + return entry; + } + + + bool comp_key(const key_type& a_lhs, const key_type& a_rhs) const + { + return key_eq()(a_lhs, a_rhs); + } + + + bool grow() + { + if (_capacity == (UInt32)1 << 31) { + return false; + } + + UInt32 newCapacity = _capacity ? _capacity << 1 : min_size(); + return grow(newCapacity); + } + + + bool grow(UInt32 a_newCapacity) + { + auto oldEntries = get_entries(); + auto begIter = begin(); + auto endIter = end(); + + auto newEntries = allocate(a_newCapacity); + if (!newEntries) { + return false; + } + else if (newEntries == oldEntries) { + _capacity = a_newCapacity; + return true; + } + else { + _capacity = a_newCapacity; + _freeCount = a_newCapacity; + _freeIdx = a_newCapacity; + set_entries(newEntries); + + while (begIter != endIter) { + insert(std::move(*begIter)); + ++begIter; + } + + deallocate(oldEntries); + return true; + } + } + + + const key_type& get_key(const value_type& a_value) const + { + traits_type traits; + return traits(a_value); + } + + + entry_type* allocate(std::size_t a_num) + { + return _allocator.allocate(a_num); + } + + + void deallocate(entry_type* a_ptr) + { + _allocator.deallocate(a_ptr); + } + + + entry_type* get_entries() const + { + return _allocator.get_entries(); + } + + + void set_entries(entry_type* a_entries) + { + _allocator.set_entries(a_entries); + } + + + size_type min_size() const + { + return _allocator.min_size(); + } + + // members + UInt64 _pad00; // 00 + UInt32 _pad08; // 08 + UInt32 _capacity; // 0C - this must be 2^n, or else terrible things will happen + UInt32 _freeCount; // 10 + UInt32 _freeIdx; // 14 + const entry_type* _sentinel; // 18 + allocator_type _allocator; // 20 +}; + + +template +struct BSTScatterTableTraits +{ +public: + typedef Key key_type; + typedef T mapped_type; + typedef BSTTuple value_type; + + + const key_type& operator()(const value_type& a_value) const + { + return a_value.first; + } +}; + +template +struct BSTScatterTableHeapAllocator +{ +public: + typedef T entry_type; + typedef UInt32 size_type; + + + BSTScatterTableHeapAllocator() : + _pad00(0), + _entries(0) + {} + + + entry_type* allocate(std::size_t a_num) + { + auto size = a_num * sizeof(entry_type); + auto mem = (entry_type*)Heap_Allocate(size); + memset(mem, 0, size); + return mem; + } + + + void deallocate(entry_type* a_ptr) + { + Heap_Free(a_ptr); + } + + + entry_type* get_entries() const + { + return _entries; + } + + + void set_entries(entry_type* a_entries) + { + _entries = a_entries; + } + + + size_type min_size() const + { + return static_cast(1) << 3; + } + + + size_type max_size() const + { + return static_cast(1) << 31; + } + +private: + // members + UInt64 _pad00; // 00 (20) + entry_type* _entries; // 08 (28) +}; +STATIC_ASSERT(sizeof(BSTScatterTableHeapAllocator) == 0x10); + + +template , class KeyEqual = std::equal_to> +class BSTHashMap : public BSTScatterTable, 8, BSTScatterTableHeapAllocator, Hash, KeyEqual> +{ +public: +}; +STATIC_ASSERT(sizeof(BSTHashMap) == 0x30); + + +template +struct BSTSetTraits +{ +public: + typedef Key key_type; + typedef void mapped_type; + typedef Key value_type; + + const key_type& operator()(const value_type& a_value) const + { + return a_value; + } +}; + + +template , class KeyEqual = std::equal_to> +class BSTSet : public BSTScatterTable, 8, BSTScatterTableHeapAllocator, Hash, KeyEqual> +{ +public: +}; +STATIC_ASSERT(sizeof(BSTSet) == 0x30); + +template class Allocator, class Hash, class KeyEqual> +typename BSTScatterTable::BSTScatterTableEntry BSTScatterTable::sentinel = BSTScatterTable::BSTScatterTableEntry(); + +template +class BSTOptional +{ + T AlignedBuffer; + bool hasValue; +}; diff --git a/f4se/f4se/GameUtilities.cpp b/f4se/f4se/GameUtilities.cpp index 7be6094..5af4999 100644 --- a/f4se/f4se/GameUtilities.cpp +++ b/f4se/f4se/GameUtilities.cpp @@ -2,3 +2,4 @@ RelocAddr <_CalculateCRC32_64> CalculateCRC32_64(0x01B10830); RelocAddr <_CalculateCRC32_32> CalculateCRC32_32(0x01B107A0); +RelocAddr <_CalculateCRC32_SIZE> CalculateCRC32_SIZE(0x01B10740); \ No newline at end of file diff --git a/f4se/f4se/GameUtilities.h b/f4se/f4se/GameUtilities.h index 37eb239..8f01528 100644 --- a/f4se/f4se/GameUtilities.h +++ b/f4se/f4se/GameUtilities.h @@ -7,3 +7,71 @@ extern RelocAddr <_CalculateCRC32_64> CalculateCRC32_64; typedef void (* _CalculateCRC32_32)(UInt32 * out, UInt32 data, UInt32 previous); extern RelocAddr <_CalculateCRC32_32> CalculateCRC32_32; + +typedef void(*_CalculateCRC32_SIZE)(UInt32 * out, const void* data, UInt32 size, UInt32 previous); +extern RelocAddr <_CalculateCRC32_SIZE> CalculateCRC32_SIZE; + +namespace BSHash +{ + template + struct CRC32Hash + { + public: + UInt32 operator()(const Key& a_key) const + { + UInt32 crc32; + CalculateCRC32_SIZE(crc32, &a_key, sizeof(Key), 0); + return crc32; + } + }; + + + template <> + struct CRC32Hash + { + public: + UInt32 operator()(const SInt32& a_key) const + { + UInt32 crc32; + CalculateCRC32_32(&crc32, (UInt32)a_key, 0); + return crc32; + } + }; + + + template <> + struct CRC32Hash + { + public: + UInt32 operator()(const UInt32& a_key) const + { + UInt32 crc32; + CalculateCRC32_32(&crc32, (UInt32)a_key, 0); + return crc32; + } + }; + + template <> + struct CRC32Hash + { + public: + UInt32 operator()(const SInt64& a_key) const + { + UInt32 crc32; + CalculateCRC32_64(&crc32, (UInt64)a_key, 0); + return crc32; + } + }; + + template <> + struct CRC32Hash + { + public: + UInt32 operator()(const UInt64& a_key) const + { + UInt32 crc32; + CalculateCRC32_64(&crc32, (UInt64)a_key, 0); + return crc32; + } + }; +} diff --git a/f4se/f4se/GameWorkshop.cpp b/f4se/f4se/GameWorkshop.cpp index cf6d24b..fd0d643 100644 --- a/f4se/f4se/GameWorkshop.cpp +++ b/f4se/f4se/GameWorkshop.cpp @@ -1,11 +1,13 @@ #include "f4se/GameWorkshop.h" -RelocAddr <_LinkPower> LinkPower_Internal(0x001F6E20); // Power related natives -RelocAddr <_LinkPower2> LinkPower2_Internal(0x00201B10); // Usually paired with LinkPower +RelocAddr PowerUtils::UpdateMovingWirelessItem(0x00201B10); // Usually paired with LinkPower RelocAddr <_GetObjectAtConnectPoint> GetObjectAtConnectPoint(0x001FF360); // Acquires objects that are touching attach points -RelocAddr <_LinkPower3> LinkPower3_Internal(0x001F6890); // Wire related calls -RelocAddr <_LinkPower4> LinkPower4_Internal(0x00204610); -RelocAddr <_SetWireEndpoints> SetWireEndpoints_Internal(0x00200E50); -RelocAddr <_FinalizeWireLink> FinalizeWireLink(0x00200B50); +RelocAddr TerminalUtils::EstablishTerminalLinks(0x00204610); +RelocAddr SplineUtils::ConnectSpline(0x00200E50); +RelocAddr SplineUtils::UpdateSpline(0x00200B50); + // B040A6E939E55D7B97BC6BE389FC17F455E63F06+83 -RelocAddr <_ScrapReference> ScrapReference(0x002084C0); +RelocAddr Workshop::ScrapReference(0x002084C0); + +RelocPtr> Workshop::hCurrentWorkshop(0x058DFDBC); +RelocAddr Workshop::FindNearestValidWorkshop(0x001F5610); \ No newline at end of file diff --git a/f4se/f4se/GameWorkshop.h b/f4se/f4se/GameWorkshop.h index db1edcc..6ab24cc 100644 --- a/f4se/f4se/GameWorkshop.h +++ b/f4se/f4se/GameWorkshop.h @@ -1,40 +1,117 @@ #pragma once #include "f4se/GameTypes.h" +#include "f4se/GameExtraData.h" +#include "f4se/GameHandle.h" +#include "f4se/NiTypes.h" class TESObjectREFR; -class LocationData; class BSExtraData; class NiPoint3; class bhkWorld; +class Actor; +class TESObjectCELL; +class TESWorldSpace; +class TESBoundObject; -typedef void(*_LinkPower)(BSExtraData* workshopExtraData, TESObjectREFR* akRef1, TESObjectREFR* akRef2, TESObjectREFR* akWireRef); // Wire optional -extern RelocAddr <_LinkPower> LinkPower_Internal; +namespace PowerUtils +{ +// 08 +struct GridConnection +{ + UInt32 connection; + UInt32 connector; +}; + +// 10 +struct GridSaveLoadData +{ + UInt32 node; + GridConnection connection; +}; + +class PowerGrid +{ +public: + BSTHashMap*> adjacencyMap; + BSTArray loadGameData; + UInt32 loadElement; + BSTArray currentlyPowered; + float capacity; + float load; +}; -typedef void(*_LinkPower2)(TESObjectREFR* akRef, BSExtraData* workshopExtraData); -extern RelocAddr <_LinkPower2> LinkPower2_Internal; +typedef void(*_UpdateMovingWirelessItem)(TESObjectREFR* akRef, BSExtraData* workshopExtraData); +extern RelocAddr <_UpdateMovingWirelessItem> UpdateMovingWirelessItem; -// Wire related calls -typedef void(*_LinkPower3)(BSExtraData* workshopExtraData, TESObjectREFR* akWireRef); -extern RelocAddr <_LinkPower3> LinkPower3_Internal; +} -typedef void(*_LinkPower4)(TESObjectREFR* akWireRef); -extern RelocAddr <_LinkPower4> LinkPower4_Internal; +namespace Workshop +{ +// 30 +class ContextData +{ +public: + ContextData(Actor * actor) + { + ctor(actor); + } + ~ContextData() { }; -typedef void(*_SetWireEndpoints)(TESObjectREFR* akRef1, SInt32 unk2, TESObjectREFR* akRef2, SInt32 unk4, TESObjectREFR* akWireRef); // unk2 and unk4 always 0 - Adds the ExtraData -extern RelocAddr <_SetWireEndpoints> SetWireEndpoints_Internal; + NiPoint3 lookPos; + NiPoint3 lookDir; + float zAngle; + TESObjectCELL * cell; // 20 + TESWorldSpace * worldspace; // 28 -typedef void(*_FinalizeWireLink)(LocationData* locationData, TESObjectREFR* akWireRef, TESObjectREFR* akRef1, int unk4, TESObjectREFR* akRef2, int unk6); // unk4 and unk6 always 0 -extern RelocAddr <_FinalizeWireLink> FinalizeWireLink; + DEFINE_MEMBER_FN_1(ctor, ContextData*, 0x001F8830, Actor * refr); +}; -typedef TESObjectREFR * (*_GetObjectAtConnectPoint)(TESObjectREFR * source, NiPoint3 * connectPos, bhkWorld * world, float unk1); -extern RelocAddr <_GetObjectAtConnectPoint> GetObjectAtConnectPoint; +// 08 +struct DeletedItemInfo +{ + UInt32 formID; + UInt32 count; +}; -struct MaterialsReturned +// 58 +class ExtraData : public BSExtraData { - TESForm * form; - UInt32 amount; +public: + PowerUtils::PowerGrid* currentPowerGrid; + BSTArray powerGrid; + BSTArray deletedItem; + SInt32 powerRating; + bool offGridItems; + + DEFINE_MEMBER_FN_1(AddItem, void, 0x001F6890, TESObjectREFR* akRef); + DEFINE_MEMBER_FN_3(AddConnection, void, 0x001F6E20, TESObjectREFR* akRef1, TESObjectREFR* akRef2, TESObjectREFR* akWireRef); }; -typedef void(*_ScrapReference)(LocationData* locationData, TESObjectREFR** akRef, tArray * materials); +extern RelocPtr> hCurrentWorkshop; + +typedef TESObjectREFR*(*_FindNearestValidWorkshop)(TESObjectREFR* akRef); +extern RelocAddr <_FindNearestValidWorkshop> FindNearestValidWorkshop; + +typedef void(*_ScrapReference)(Workshop::ContextData* contextData, NiPointer* akRef, BSTArray>* materials); extern RelocAddr <_ScrapReference> ScrapReference; +} + +namespace SplineUtils +{ +typedef void(*_ConnectSpline)(TESObjectREFR* akEndpoint1, SInt32 linkType1, TESObjectREFR* akEndpoint2, SInt32 linkType2, TESObjectREFR* akWireRef); // unk2 and unk4 always 0 - Adds the ExtraData +extern RelocAddr <_ConnectSpline> ConnectSpline; + +typedef void(*_UpdateSpline)(Workshop::ContextData* contextData, TESObjectREFR* akWireRef, TESObjectREFR* akEndpoint1, int linkType1, TESObjectREFR* akEndpoint2, int linkType2); +extern RelocAddr <_UpdateSpline> UpdateSpline; +} + +namespace TerminalUtils +{ +typedef void(*_EstablishTerminalLinks)(TESObjectREFR* akWireRef); +extern RelocAddr <_EstablishTerminalLinks> EstablishTerminalLinks; +} + +typedef TESObjectREFR * (*_GetObjectAtConnectPoint)(TESObjectREFR * source, NiPoint3 * connectPos, bhkWorld * world, float unk1); +extern RelocAddr <_GetObjectAtConnectPoint> GetObjectAtConnectPoint; + diff --git a/f4se/f4se/Hooks_Scaleform.cpp b/f4se/f4se/Hooks_Scaleform.cpp index 3688060..66204a5 100644 --- a/f4se/f4se/Hooks_Scaleform.cpp +++ b/f4se/f4se/Hooks_Scaleform.cpp @@ -332,7 +332,7 @@ class F4SEScaleform_MountImage : public GFxFunctionHandler bool result = false; if(texture) { - BSReadAndWriteLocker locker(&s_mountedTexturesLock); + BSWriteLocker locker(&s_mountedTexturesLock); auto & textures = s_mountedTextures[menuName]; auto sit = textures.find(texture); @@ -379,7 +379,7 @@ class F4SEScaleform_UnmountImage : public GFxFunctionHandler bool result = false; if(texture) { - BSReadAndWriteLocker locker(&s_mountedTexturesLock); + BSWriteLocker locker(&s_mountedTexturesLock); auto it = s_mountedTextures.find(menuName); if(it != s_mountedTextures.end()) { @@ -418,7 +418,7 @@ class F4SEOpenCloseHandler : public BSTEventSink // Unmount textures if the menu is being destroyed if(!evn->isOpen) { - BSReadAndWriteLocker locker(&s_mountedTexturesLock); + BSWriteLocker locker(&s_mountedTexturesLock); auto it = s_mountedTextures.find(evn->menuName.c_str()); if(it != s_mountedTextures.end()) { diff --git a/f4se/f4se/InternalSerialization.cpp b/f4se/f4se/InternalSerialization.cpp index d6dd367..a35086a 100644 --- a/f4se/f4se/InternalSerialization.cpp +++ b/f4se/f4se/InternalSerialization.cpp @@ -200,7 +200,7 @@ void Core_RevertCallback(const F4SESerializationInterface * intfc) F4SEObjectStorageInstance().ClearAndRelease(); // Unregister all custom menus - g_customMenuLock.LockForReadAndWrite(); + g_customMenuLock.LockForWrite(); for(auto & menuData : g_customMenuData) { BSFixedString menuName(menuData.first.c_str()); @@ -209,7 +209,7 @@ void Core_RevertCallback(const F4SESerializationInterface * intfc) } } g_customMenuData.clear(); - g_customMenuLock.Unlock(); + g_customMenuLock.UnlockWrite(); } void Core_SaveCallback(const F4SESerializationInterface * intfc) diff --git a/f4se/f4se/NiNodes.cpp b/f4se/f4se/NiNodes.cpp index 4394a2d..e72420b 100644 --- a/f4se/f4se/NiNodes.cpp +++ b/f4se/f4se/NiNodes.cpp @@ -2,7 +2,7 @@ NiNode * NiNode::Create(UInt16 children) { - NiNode * node = (NiNode*)Heap_Allocate(sizeof(NiNode)); + NiNode * node = (NiNode*)CALL_MEMBER_FN(g_mainHeap, Allocate)(sizeof(NiNode), 0x10, true); CALL_MEMBER_FN(node, ctor)(children); return node; } diff --git a/f4se/f4se/NiObjects.cpp b/f4se/f4se/NiObjects.cpp index d4eb61f..3e33592 100644 --- a/f4se/f4se/NiObjects.cpp +++ b/f4se/f4se/NiObjects.cpp @@ -1,5 +1,6 @@ #include "f4se/NiObjects.h" #include "f4se/NiExtraData.h" +#include "f4se/NiNodes.h" RelocAddr <_WorldToScreen> WorldToScreen_Internal(0x00AE2840); @@ -39,3 +40,22 @@ bool NiObjectNET::AddExtraData(NiExtraData * extraData) { return CALL_MEMBER_FN(this, Internal_AddExtraData)(extraData); } + +bool NiAVObject::Visit(const std::function& functor) +{ + if (functor(this)) + return true; + + NiPointer node(GetAsNiNode()); + if (node) { + for (UInt32 i = 0; i < node->m_children.m_emptyRunStart; i++) { + NiPointer object(node->m_children.m_data[i]); + if (object) { + if (object->Visit(functor)) + return true; + } + } + } + + return false; +} diff --git a/f4se/f4se/NiObjects.h b/f4se/f4se/NiObjects.h index d214bef..6a8ae80 100644 --- a/f4se/f4se/NiObjects.h +++ b/f4se/f4se/NiObjects.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "f4se/GameTypes.h" #include "f4se/NiTypes.h" @@ -201,24 +203,6 @@ class NiAVObject : public NiObjectNET DEFINE_MEMBER_FN(SetScenegraphChange, void, 0x01BA47C0); // Return true in the functor to halt traversal - template - bool Visit(T & functor) - { - if (functor(this)) - return true; - - NiPointer node(GetAsNiNode()); - if(node) { - for(UInt32 i = 0; i < node->m_children.m_emptyRunStart; i++) { - NiPointer object(node->m_children.m_data[i]); - if(object) { - if (object->Visit(functor)) - return true; - } - } - } - - return false; - } + bool Visit(const std::function& functor); }; STATIC_ASSERT(sizeof(NiAVObject) == 0x120); diff --git a/f4se/f4se/NiTypes.h b/f4se/f4se/NiTypes.h index 2695117..7020773 100644 --- a/f4se/f4se/NiTypes.h +++ b/f4se/f4se/NiTypes.h @@ -3,26 +3,29 @@ #include "f4se/GameAPI.h" // 8 -template +template class NiPointer { public: T * m_pObject; // 00 - inline NiPointer(T* pObject = (T*) 0) + inline NiPointer(const NiPointer& rhs) : + m_pObject(rhs.m_pObject) { - m_pObject = pObject; - if(m_pObject) m_pObject->IncRef(); + if (m_pObject) { + m_pObject->IncRef(); + } } - inline ~NiPointer() + inline NiPointer(T* pObject = (T*)0) { - if(m_pObject) m_pObject->DecRef(); + m_pObject = pObject; + if (m_pObject) m_pObject->IncRef(); } - inline operator bool() const + inline ~NiPointer() { - return m_pObject != nullptr; + if (m_pObject) m_pObject->DecRef(); } inline operator T *() const @@ -42,10 +45,10 @@ class NiPointer inline NiPointer & operator=(const NiPointer & rhs) { - if(m_pObject != rhs.m_pObject) + if (m_pObject != rhs.m_pObject) { - if(rhs) rhs.m_pObject->IncRef(); - if(m_pObject) m_pObject->DecRef(); + if (rhs) rhs.m_pObject->IncRef(); + if (m_pObject) m_pObject->DecRef(); m_pObject = rhs.m_pObject; } @@ -55,10 +58,10 @@ class NiPointer inline NiPointer & operator=(T * rhs) { - if(m_pObject != rhs) + if (m_pObject != rhs) { - if(rhs) rhs->IncRef(); - if(m_pObject) m_pObject->DecRef(); + if (rhs) rhs->IncRef(); + if (m_pObject) m_pObject->DecRef(); m_pObject = rhs; } @@ -85,6 +88,11 @@ class NiPointer { return m_pObject != ptr.m_pObject; } + + inline T* get() + { + return m_pObject; + } }; #define MAKE_NI_POINTER(x) class x; typedef NiPointer x##Ptr @@ -134,6 +142,8 @@ class __declspec(align(8)) NiPoint3A : NiPoint3 class NiColor { public: + NiColor() : r(0), g(0), b(0) { } + float r; // 00 float g; // 04 float b; // 08 @@ -143,6 +153,8 @@ class NiColor class NiColorA { public: + NiColorA() : r(0), g(0), b(0), a(0) { } + float r; // 00 float g; // 04 float b; // 08 diff --git a/f4se/f4se/PapyrusActor.cpp b/f4se/f4se/PapyrusActor.cpp index 4dd7804..5df79d0 100644 --- a/f4se/f4se/PapyrusActor.cpp +++ b/f4se/f4se/PapyrusActor.cpp @@ -101,7 +101,7 @@ namespace papyrusActor TESObjectREFR * GetFurnitureReference(Actor * actor) { - TESObjectREFR * refr = nullptr; + NiPointer refr; if(!actor) return nullptr; @@ -119,9 +119,14 @@ namespace papyrusActor else furnitureHandle = data08->furnitureHandle1; - LookupREFRByHandle(&furnitureHandle, &refr); + LookupREFRByHandle(furnitureHandle, refr); return refr; } + + bool IsProtected(Actor * actor) + { + return actor && (actor->uiFlags & 0x80000) != 0; + } } void papyrusActor::RegisterFuncs(VirtualMachine* vm) @@ -137,4 +142,7 @@ void papyrusActor::RegisterFuncs(VirtualMachine* vm) vm->RegisterFunction( new NativeFunction0("GetFurnitureReference", "Actor", papyrusActor::GetFurnitureReference, vm)); + + vm->RegisterFunction( + new NativeFunction0("IsProtected", "Actor", papyrusActor::IsProtected, vm)); } diff --git a/f4se/f4se/PapyrusEvents.h b/f4se/f4se/PapyrusEvents.h index d77db12..ca56a10 100644 --- a/f4se/f4se/PapyrusEvents.h +++ b/f4se/f4se/PapyrusEvents.h @@ -228,6 +228,7 @@ class ExternalEventParameters template class RegistrationMapHolder : public SafeDataHolder>>> { + typedef SafeDataHolder>>> Super; typedef std::set> RegSet; typedef std::map RegMap; @@ -244,12 +245,12 @@ class RegistrationMapHolder : public SafeDataHolderAddRef(handle); - Release(); + Super::Release(); } void Unregister(K & key, UInt64 handle, BSFixedString scriptName) @@ -261,12 +262,12 @@ class RegistrationMapHolder : public SafeDataHolderRelease(handle); - Release(); + Super::Release(); } void UnregisterAll(UInt64 handle, BSFixedString scriptName) @@ -278,43 +279,43 @@ class RegistrationMapHolder : public SafeDataHoldersecond.erase(reg)) policy->Release(handle); - Release(); + Super::Release(); } template void ForEach(K & key, F & functor) { - Lock(); + Super::Lock(); - RegMap::iterator handles = m_data.find(key); + typename RegMap::iterator handles = Super::m_data.find(key); - if (handles != m_data.end()) - for (RegSet::iterator iter = handles->second.begin(); iter != handles->second.end(); ++iter) + if (handles != Super::m_data.end()) + for (typename RegSet::iterator iter = handles->second.begin(); iter != handles->second.end(); ++iter) functor(*iter); - Release(); + Super::Release(); } void Clear(void) { - Lock(); - m_data.clear(); - Release(); + Super::Lock(); + Super::m_data.clear(); + Super::Release(); } bool Save(const F4SESerializationInterface * intfc, UInt32 type, UInt32 version) { intfc->OpenRecord(type, version); - Lock(); + Super::Lock(); - for (RegMap::iterator iter = m_data.begin(); iter != m_data.end(); ++iter) + for (typename RegMap::iterator iter = Super::m_data.begin(); iter != Super::m_data.end(); ++iter) { UInt32 numRegs = iter->second.size(); @@ -328,13 +329,13 @@ class RegistrationMapHolder : public SafeDataHolderWriteRecordData(&numRegs, sizeof(numRegs)); // Regs - for (RegSet::iterator elems = iter->second.begin(); elems != iter->second.end(); ++elems) + for (typename RegSet::iterator elems = iter->second.begin(); elems != iter->second.end(); ++elems) elems->Save(intfc, version); } intfc->OpenRecord('REGE', version); - Release(); + Super::Release(); return true; } @@ -390,12 +391,12 @@ class RegistrationMapHolder : public SafeDataHolderAddRef(reg.handle); - Release(); + Super::Release(); } else @@ -427,6 +428,7 @@ class RegistrationMapHolder : public SafeDataHolder class RegistrationSetHolder : public SafeDataHolder>> { + typedef SafeDataHolder>> Super; typedef std::set> RegSet; public: @@ -442,12 +444,12 @@ class RegistrationSetHolder : public SafeDataHolderAddRef(handle); - Release(); + Super::Release(); } void Unregister(UInt64 handle, BSFixedString scriptName) @@ -459,48 +461,48 @@ class RegistrationSetHolder : public SafeDataHolderRelease(handle); - Release(); + Super::Release(); } template void ForEach(F & functor) { - Lock(); + Super::Lock(); - for (RegSet::iterator iter = m_data.begin(); iter != m_data.end(); ++iter) + for (typename RegSet::iterator iter = Super::m_data.begin(); iter != Super::m_data.end(); ++iter) functor(*iter); - Release(); + Super::Release(); } void Clear(void) { - Lock(); - m_data.clear(); - Release(); + Super::Lock(); + Super::m_data.clear(); + Super::Release(); } bool Save(const F4SESerializationInterface * intfc, UInt32 type, UInt32 version) { intfc->OpenRecord(type, version); - Lock(); + Super::Lock(); - UInt32 numRegs = m_data.size(); + UInt32 numRegs = Super::m_data.size(); // Reg count intfc->WriteRecordData(&numRegs, sizeof(numRegs)); // Regs - for (RegSet::iterator iter = m_data.begin(); iter != m_data.end(); ++iter) + for (typename RegSet::iterator iter = Super::m_data.begin(); iter != Super::m_data.end(); ++iter) iter->Save(intfc, version); - Release(); + Super::Release(); return true; } @@ -540,12 +542,12 @@ class RegistrationSetHolder : public SafeDataHolderAddRef(reg.handle); - Release(); + Super::Release(); } else diff --git a/f4se/f4se/PapyrusGame.cpp b/f4se/f4se/PapyrusGame.cpp index 50e7fb6..3705dc0 100644 --- a/f4se/f4se/PapyrusGame.cpp +++ b/f4se/f4se/PapyrusGame.cpp @@ -18,9 +18,9 @@ namespace papyrusGame TESObjectREFR * GetCurrentConsoleRef(StaticFunctionTag * base) { UInt32 handle = (*g_consoleHandle); - TESObjectREFR * refr = NULL; + NiPointer refr; if(handle != 0 && handle != (*g_invalidRefHandle)) { - LookupREFRByHandle(&handle, &refr); + LookupREFRByHandle(handle, refr); return refr; } diff --git a/f4se/f4se/PapyrusObjectReference.cpp b/f4se/f4se/PapyrusObjectReference.cpp index 0bd2bd6..2e12496 100644 --- a/f4se/f4se/PapyrusObjectReference.cpp +++ b/f4se/f4se/PapyrusObjectReference.cpp @@ -146,7 +146,7 @@ namespace papyrusObjectReference { } // Workshop ref isn't a workshop! - BSExtraData* extraDataWorkshop = workshopRef->extraDataList->GetByType(ExtraDataType::kExtraData_WorkshopExtraData); + Workshop::ExtraData* extraDataWorkshop = static_cast(workshopRef->extraDataList->GetByType(ExtraDataType::kExtraData_WorkshopExtraData)); if(!extraDataWorkshop) { return nullptr; } @@ -167,20 +167,26 @@ namespace papyrusObjectReference { // Set the wire's linked ref to the workshop SetLinkedRef_Native(wireRef, workshopRef, keyword); - LocationData locData(*g_player); - FinalizeWireLink(&locData, wireRef, refB, 0, refA, 0); - SetWireEndpoints_Internal(refA, 0, refB, 0, wireRef); + BSPointerHandle currentWorkshop = *Workshop::hCurrentWorkshop; + + *Workshop::hCurrentWorkshop = workshopRef; + + Workshop::ContextData contextData(*g_player); + SplineUtils::UpdateSpline(&contextData, wireRef, refB, 0, refA, 0); + SplineUtils::ConnectSpline(refA, 0, refB, 0, wireRef); ExtraBendableSplineParams * splineParams = (ExtraBendableSplineParams*)wireRef->extraDataList->GetByType(kExtraData_BendableSplineParams); if(splineParams) { splineParams->thickness = 1.5f; } - LinkPower3_Internal(extraDataWorkshop, wireRef); - LinkPower_Internal(extraDataWorkshop, refA, refB, wireRef); - LinkPower2_Internal(refA, extraDataWorkshop); - LinkPower2_Internal(refB, extraDataWorkshop); - LinkPower4_Internal(wireRef); + extraDataWorkshop->AddItem(wireRef); + extraDataWorkshop->AddConnection(refA, refB, wireRef); + PowerUtils::UpdateMovingWirelessItem(refA, extraDataWorkshop); + PowerUtils::UpdateMovingWirelessItem(refB, extraDataWorkshop); + TerminalUtils::EstablishTerminalLinks(wireRef); + + *Workshop::hCurrentWorkshop = currentWorkshop; return wireRef; } @@ -345,7 +351,7 @@ namespace papyrusObjectReference { results.Push(&item.form); } - inventory->inventoryLock.Unlock(); + inventory->inventoryLock.UnlockRead(); } return results; @@ -487,7 +493,7 @@ namespace papyrusObjectReference { } // Workshop ref isn't a workshop! - BSExtraData* extraDataWorkshop = workshopRef->extraDataList->GetByType(ExtraDataType::kExtraData_WorkshopExtraData); + Workshop::ExtraData* extraDataWorkshop = static_cast(workshopRef->extraDataList->GetByType(ExtraDataType::kExtraData_WorkshopExtraData)); if(!extraDataWorkshop) { return false; } @@ -496,6 +502,10 @@ namespace papyrusObjectReference { if(!extraData) { return false; } + + BSPointerHandle previousWorkshop = *Workshop::hCurrentWorkshop; + + *Workshop::hCurrentWorkshop = workshopRef; for(UInt32 i = 0; i < extraData->points.count; i++) { @@ -527,8 +537,8 @@ namespace papyrusObjectReference { if(connected) { try // Probably wont make a difference but doesnt hurt to try { - LinkPower_Internal(extraDataWorkshop, refr, connected, nullptr); - LinkPower2_Internal(connected, extraDataWorkshop); + extraDataWorkshop->AddConnection(refr, connected, nullptr); + PowerUtils::UpdateMovingWirelessItem(connected, extraDataWorkshop); } catch (...) { @@ -539,7 +549,9 @@ namespace papyrusObjectReference { } } - LinkPower2_Internal(refr, extraDataWorkshop); + PowerUtils::UpdateMovingWirelessItem(refr, extraDataWorkshop); + + *Workshop::hCurrentWorkshop = previousWorkshop; return true; } @@ -555,7 +567,7 @@ namespace papyrusObjectReference { } struct CompareMaterial { - bool operator()(const std::pair lhs, const std::pair rhs) { + bool operator()(const std::pair lhs, const std::pair rhs) const { return lhs.first->source < rhs.first->source; } }; @@ -588,7 +600,7 @@ namespace papyrusObjectReference { if(geometry) { NiPointer shaderProperty = ni_cast(geometry->shaderProperty, BSShaderProperty); - if(shaderProperty) + if(shaderProperty.get()) { const char * shaderPath = shaderProperty->m_name.c_str(); std::string fullPath(shaderPath ? shaderPath : ""); @@ -669,24 +681,42 @@ namespace papyrusObjectReference { return true; } -#ifdef _DEBUG - void ScrapLatent(UInt32 stackId, TESObjectREFR * refr) + bool ScrapLatent(UInt32 stackId, TESObjectREFR * refr, TESObjectREFR* akWorkshopRef) { - LocationData locData(*g_player); - ScrapReference(&locData, &refr, nullptr); + Workshop::ContextData contextData(*g_player); + + BSPointerHandle currentWorkshop = *Workshop::hCurrentWorkshop; + + // Didn't specify a workshop + if (!akWorkshopRef) + { + // We are not in workshop mode, find a workshop + if (!currentWorkshop) + akWorkshopRef = Workshop::FindNearestValidWorkshop(refr); + else // Use the workshop from workshop mode + akWorkshopRef = currentWorkshop.get(); + } + + if (!akWorkshopRef) + return false; + + *Workshop::hCurrentWorkshop = akWorkshopRef; + NiPointer refrPointer(refr); + Workshop::ScrapReference(&contextData, &refrPointer, nullptr); + *Workshop::hCurrentWorkshop = currentWorkshop; + return true; } - DECLARE_DELAY_FUNCTOR(F4SEScrapFunctor, 0, ScrapLatent, TESObjectREFR, void); + DECLARE_DELAY_FUNCTOR(F4SEScrapFunctor, 1, ScrapLatent, TESObjectREFR, bool, TESObjectREFR*); - bool Scrap(VirtualMachine * vm, UInt32 stackId, TESObjectREFR* refr) + bool Scrap(VirtualMachine * vm, UInt32 stackId, TESObjectREFR* refr, TESObjectREFR* akWorkshopRef) { if(!refr) return false; - F4SEDelayFunctorManagerInstance().Enqueue(new F4SEScrapFunctor(ScrapLatent, vm, stackId, refr)); + F4SEDelayFunctorManagerInstance().Enqueue(new F4SEScrapFunctor(ScrapLatent, vm, stackId, refr, akWorkshopRef)); return true; } -#endif } void papyrusObjectReference::RegisterFuncs(VirtualMachine* vm) @@ -697,10 +727,7 @@ void papyrusObjectReference::RegisterFuncs(VirtualMachine* vm) f4seObjRegistry.RegisterClass(); f4seObjRegistry.RegisterClass(); f4seObjRegistry.RegisterClass(); - -#ifdef _DEBUG f4seObjRegistry.RegisterClass(); -#endif vm->RegisterFunction( new NativeFunction0>("GetAllMods", "ObjectReference", papyrusObjectReference::GetAllMods, vm)); @@ -741,10 +768,8 @@ void papyrusObjectReference::RegisterFuncs(VirtualMachine* vm) vm->SetFunctionFlags("ObjectReference", "TransmitConnectedPower", IFunction::kFunctionFlag_NoWait); vm->SetFunctionFlags("ObjectReference", "ApplyMaterialSwap", IFunction::kFunctionFlag_NoWait); -#ifdef _DEBUG vm->RegisterFunction( - new LatentNativeFunction0("Scrap", "ObjectReference", papyrusObjectReference::Scrap, vm)); + new LatentNativeFunction1("Scrap", "ObjectReference", papyrusObjectReference::Scrap, vm)); vm->SetFunctionFlags("ObjectReference", "Scrap", IFunction::kFunctionFlag_NoWait); -#endif } diff --git a/f4se/f4se/PapyrusPerk.cpp b/f4se/f4se/PapyrusPerk.cpp index e66870a..8420727 100644 --- a/f4se/f4se/PapyrusPerk.cpp +++ b/f4se/f4se/PapyrusPerk.cpp @@ -8,12 +8,12 @@ namespace papyrusPerk { bool IsPlayable(BGSPerk* thisPerk) { - return thisPerk ? (thisPerk->playable >= 0) : false; + return thisPerk ? (thisPerk->playable != 0) : false; } bool IsHidden(BGSPerk* thisPerk) { - return thisPerk ? (thisPerk->hidden >= 0) : false; + return thisPerk ? (thisPerk->hidden != 0) : false; } UInt32 GetLevel(BGSPerk* thisPerk) diff --git a/f4se/f4se/PapyrusUI.cpp b/f4se/f4se/PapyrusUI.cpp index 680a22f..06e8d05 100644 --- a/f4se/f4se/PapyrusUI.cpp +++ b/f4se/f4se/PapyrusUI.cpp @@ -22,7 +22,7 @@ namespace papyrusUI { if(!(*g_ui)->IsMenuRegistered(menuName) && !menuData.IsNone()) { - BSReadAndWriteLocker locker(&g_customMenuLock); + BSWriteLocker locker(&g_customMenuLock); g_customMenuData[menuName.c_str()].menuPath = menuPath; g_customMenuData[menuName.c_str()].rootPath = rootPath; menuData.Get("menuFlags", &g_customMenuData[menuName.c_str()].menuFlags); diff --git a/f4se/f4se/PluginAPI.h b/f4se/f4se/PluginAPI.h index 6c97960..efae958 100644 --- a/f4se/f4se/PluginAPI.h +++ b/f4se/f4se/PluginAPI.h @@ -8,6 +8,7 @@ class ITaskDelegate; class F4SEDelayFunctorManager; class F4SEObjectRegistry; class F4SEPersistentObjectStorage; +struct PluginInfo; enum { @@ -23,6 +24,7 @@ enum kInterface_Serialization, kInterface_Task, kInterface_Object, + kInterface_Trampoline, kInterface_Max, }; @@ -40,6 +42,10 @@ struct F4SEInterface // returns the F4SE build's release index UInt32 (* GetReleaseIndex)(void); + + // Minimum F4SE version 0.6.22 + // returns the plugin info structure for a plugin by name, only valid to be called after PostLoad message + const PluginInfo* (*GetPluginInfo)(const char* name); }; @@ -212,6 +218,19 @@ struct F4SEObjectInterface F4SEPersistentObjectStorage & (* GetPersistentObjectStorage)(); }; +struct F4SETrampolineInterface +{ + enum + { + kInterfaceVersion = 1 + }; + + UInt32 interfaceVersion; + + void* (*AllocateFromBranchPool)(PluginHandle plugin, size_t size); + void* (*AllocateFromLocalPool)(PluginHandle plugin, size_t size); +}; + struct PluginInfo { enum diff --git a/f4se/f4se/PluginManager.cpp b/f4se/f4se/PluginManager.cpp index 091d9c8..1cebd38 100644 --- a/f4se/f4se/PluginManager.cpp +++ b/f4se/f4se/PluginManager.cpp @@ -3,11 +3,134 @@ #include "GameAPI.h" #include "f4se_common/Utilities.h" #include "f4se_common/f4se_version.h" +#include "f4se_common/BranchTrampoline.h" + +#include +#include +#include +#include + +namespace +{ + class ScopeExit + { + public: + ScopeExit(std::function fn) : + m_fn(std::move(fn)) + { + assert(m_fn); + } + + ~ScopeExit() + { + if (m_fn) + { + m_fn(); + } + } + + private: + std::function m_fn; + + ScopeExit(const ScopeExit&); + ScopeExit(ScopeExit&&); + + ScopeExit& operator=(const ScopeExit&); + ScopeExit& operator=(ScopeExit&&); + }; + + std::string CheckModNotFound(const char* a_plugin) + { + assert(a_plugin != nullptr); + + const auto file = LoadLibraryExA( + a_plugin, + NULL, + LOAD_LIBRARY_AS_DATAFILE); + if (file == NULL) + { + return "failed to load resource with error code " + std::to_string(GetLastError()); + } + ScopeExit _file([&]() { FreeLibrary(file); }); + + const auto base = reinterpret_cast( + reinterpret_cast(file) & + ~(static_cast(0xFFFF))); // https://devblogs.microsoft.com/oldnewthing/20051006-09/?p=33883 + const auto dosHeader = reinterpret_cast(base); + const auto ntHeader = reinterpret_cast(base + dosHeader->e_lfanew); + const auto sections = IMAGE_FIRST_SECTION(ntHeader); + const auto adjustRVA = [&](std::size_t rva) -> DWORD + { + for (WORD i = 0; i < ntHeader->FileHeader.NumberOfSections; ++i) + { + const auto section = sections[i]; + if (section.VirtualAddress <= rva && rva < section.VirtualAddress + section.Misc.VirtualSize) + { + return rva - section.VirtualAddress + section.PointerToRawData; + } + } + return 0; + }; + + const auto importDirectory = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + if (importDirectory.Size == 0) + { + return "no import entry to enumerate"; + } + + const auto imports = reinterpret_cast(base + adjustRVA(importDirectory.VirtualAddress)); + for (auto iter = imports; iter->Characteristics != 0; ++iter) + { + const auto name = reinterpret_cast(base + adjustRVA(iter->Name)); + if (name && name[0] != '\0') + { + const auto handle = LoadLibraryA(name); + if (handle == NULL) + { + return std::string("failed to load ") + name; + } + else + { + FreeLibrary(handle); + } + } + } + + return ""; + } + + void* AllocateFromPool(PluginAllocator& alloc, const char* name, PluginHandle plugin, size_t size) + { + assert(name != nullptr); + + const auto mem = alloc.Allocate(size); + if (mem) { + static std::mutex lock; + const std::lock_guard l(lock); // global log isn't thread-safe + _DMESSAGE("plugin %u allocated %u bytes from %s pool", plugin, size, name); + } + + return mem; + } + + void* AllocateFromBranchPool(PluginHandle plugin, size_t size) + { + return AllocateFromPool(g_branchPluginAllocator, "branch", plugin, size); + } + + void* AllocateFromLocalPool(PluginHandle plugin, size_t size) + { + return AllocateFromPool(g_localPluginAllocator, "local", plugin, size); + } +} PluginManager g_pluginManager; +PluginAllocator g_branchPluginAllocator; +PluginAllocator g_localPluginAllocator; PluginManager::LoadedPlugin * PluginManager::s_currentLoadingPlugin = NULL; PluginHandle PluginManager::s_currentPluginHandle = 0; +bool PluginManager::s_hideTrampolineInterface = false; static const F4SEInterface g_F4SEInterface = { @@ -25,7 +148,8 @@ static const F4SEInterface g_F4SEInterface = PluginManager::QueryInterface, PluginManager::GetPluginHandle, - PluginManager::GetReleaseIndex + PluginManager::GetReleaseIndex, + PluginManager::GetPluginInfo }; static const F4SEMessagingInterface g_F4SEMessagingInterface = @@ -96,6 +220,13 @@ static const F4SEObjectInterface g_F4SEObjectInterface = F4SEObjectStorageInstance }; +static const F4SETrampolineInterface g_F4SETrampolineInterface = +{ + F4SETrampolineInterface::kInterfaceVersion, + AllocateFromBranchPool, + AllocateFromLocalPool +}; + PluginManager::PluginManager() { // @@ -193,6 +324,12 @@ void * PluginManager::QueryInterface(UInt32 id) case kInterface_Object: result = (void *)&g_F4SEObjectInterface; break; + case kInterface_Trampoline: + if(s_hideTrampolineInterface) + _WARNING("hiding trampoline interface from buggy plugin"); + else + result = (void *)&g_F4SETrampolineInterface; + break; default: _WARNING("unknown QueryInterface %08X", id); break; @@ -216,6 +353,12 @@ UInt32 PluginManager::GetReleaseIndex( void ) return F4SE_VERSION_RELEASEIDX; } + +const PluginInfo* PluginManager::GetPluginInfo(const char* name) +{ + return g_pluginManager.GetInfoByName(name); +} + bool PluginManager::FindPluginDirectory(void) { bool result = false; @@ -248,8 +391,9 @@ void PluginManager::InstallPlugins(void) s_currentLoadingPlugin = &plugin; s_currentPluginHandle = m_plugins.size() + 1; // +1 because 0 is reserved for internal use + s_hideTrampolineInterface = false; - plugin.handle = (HMODULE)LoadLibrary(pluginPath.c_str()); + plugin.handle = LoadLibraryA(pluginPath.c_str()); if(plugin.handle) { bool success = false; @@ -310,7 +454,23 @@ void PluginManager::InstallPlugins(void) } else { - _ERROR("couldn't load plugin %s (Error %d)", pluginPath.c_str(), GetLastError()); + std::string post; + const auto err = GetLastError(); + switch (err) { + case ERROR_MOD_NOT_FOUND: + post = CheckModNotFound(pluginPath.c_str()); + break; + } + + if (!post.empty()) + { + post = ": " + post; + } + + _ERROR("couldn't load plugin %s (Error %d%s)", + pluginPath.c_str(), + err, + post.c_str()); } } @@ -362,8 +522,9 @@ const char * PluginManager::SafeCallLoadPlugin(LoadedPlugin * plugin, const F4SE enum { - kCompat_BlockFromRuntime = 1 << 0, - kCompat_BlockFromEditor = 1 << 1, + kCompat_BlockFromRuntime = 1 << 0, + kCompat_BlockFromEditor = 1 << 1, + kCompat_HideTrampolineInterface = 1 << 2, }; struct MinVersionEntry @@ -376,6 +537,7 @@ struct MinVersionEntry static const MinVersionEntry kMinVersionList[] = { + { "High FPS Physics Fix", 0x0000000F, "overallocates its trampoline request and crashes", kCompat_HideTrampolineInterface }, { NULL, 0, NULL } }; @@ -410,6 +572,12 @@ const char * PluginManager::CheckPluginCompatibility(LoadedPlugin * plugin) return iter->reason; } #endif + + if(iter->compatFlags & kCompat_HideTrampolineInterface) + { + // don't block from loading, just hide the interface + s_hideTrampolineInterface = true; + } } break; diff --git a/f4se/f4se/PluginManager.h b/f4se/f4se/PluginManager.h index 39f2c7a..8a461fc 100644 --- a/f4se/f4se/PluginManager.h +++ b/f4se/f4se/PluginManager.h @@ -1,5 +1,8 @@ #pragma once +#include +#include +#include #include #include @@ -22,9 +25,10 @@ class PluginManager UInt32 GetNumPlugins(void); - static void * QueryInterface(UInt32 id); - static PluginHandle GetPluginHandle(void); - static UInt32 GetReleaseIndex(void); + static void * QueryInterface(UInt32 id); + static PluginHandle GetPluginHandle(void); + static UInt32 GetReleaseIndex(void); + static const PluginInfo* GetPluginInfo(const char* name); static bool Dispatch_Message(PluginHandle sender, UInt32 messageType, void * data, UInt32 dataLen, const char* receiver); static bool RegisterListener(PluginHandle listener, const char* sender, F4SEMessagingInterface::EventCallback handler); @@ -55,9 +59,51 @@ class PluginManager static LoadedPlugin * s_currentLoadingPlugin; static PluginHandle s_currentPluginHandle; + + static bool s_hideTrampolineInterface; +}; + +// a non-owning, thread-safe allocator for a block of memory +class PluginAllocator +{ +public: + PluginAllocator() + :m_first(nullptr), m_last(nullptr) { } + + UInt8* Allocate(size_t n) + { + const Locker l(m_lock); + + UInt8* result = nullptr; + if (m_first + n < m_last) + { + result = m_first; + m_first += n; + } + + return result; + } + + void Initialize(void* memory, size_t size) + { + const Locker l(m_lock); + assert(!m_first && !m_last); + m_first = static_cast(memory); + m_last = m_first + size; + } + +private: + typedef std::mutex Lock; + typedef std::lock_guard Locker; + + Lock m_lock; + UInt8* m_first; + UInt8* m_last; }; extern PluginManager g_pluginManager; +extern PluginAllocator g_branchPluginAllocator; +extern PluginAllocator g_localPluginAllocator; extern const F4SESerializationInterface g_F4SESerializationInterface; extern const F4SEPapyrusInterface g_F4SEPapyrusInterface; diff --git a/f4se/f4se/ScaleformCallbacks.h b/f4se/f4se/ScaleformCallbacks.h index 3242b74..538a3e2 100644 --- a/f4se/f4se/ScaleformCallbacks.h +++ b/f4se/f4se/ScaleformCallbacks.h @@ -4,6 +4,7 @@ #include "f4se/ScaleformTypes.h" #include "f4se/ScaleformMovie.h" +#include "f4se/ScaleformValue.h" #include #include @@ -84,4 +85,4 @@ class SWFToCodeFunctionHandler : public GFxFunctionHandler virtual void RegisterFunctions() = 0; // 02 DEFINE_MEMBER_FN_2(RegisterNativeFunction, void, 0x02110390, const char * name, UInt32 index); -}; \ No newline at end of file +}; diff --git a/f4se/f4se/ScaleformValue.cpp b/f4se/f4se/ScaleformValue.cpp index 4beea1b..662c1e4 100644 --- a/f4se/f4se/ScaleformValue.cpp +++ b/f4se/f4se/ScaleformValue.cpp @@ -1,10 +1,6 @@ #include "f4se/ScaleformValue.h" #include "f4se/GameEvents.h" -RelocAddr <_GetFilterColorByType> GetFilterColorByType(0x020F2C90); -RelocAddr <_ApplyColorFilter> ApplyColorFilter(0x020F2990); -RelocAddr <_SetDefaultColors> SetDefaultColors(0x020F2BE0); - RelocAddr <_GetExtDisplayInfo> GetExtDisplayInfo(0x0210DBE0); RelocAddr <_SetExtDisplayInfoAlpha> SetExtDisplayInfoAlpha(0x0210DEF0); RelocAddr <_SetExtDisplayInfo> SetExtDisplayInfo(0x0210DD70); @@ -238,27 +234,26 @@ bool GFxValue::GotoLabeledFrame(const char * frameLabel, bool stop) void BSGFxShaderFXTarget::SetFilterColor(bool isHostile) { - UInt32 type = kColorNormal; - FilterColor color; + HUDColorTypes type = kHUDColorTypes_GameplayHUDColor; + NiColor color; if(isHostile) - type = kColorWarning; - colorType = type; + type = kHUDColorTypes_WarningColor; + HUDColorType = type; - GetFilterColorByType(this, &color); - ApplyColorFilter(this, &color, 1.0f); + GetColorMultipliersFromType(&color); + EnableColorMultipliers(&color, 1.0f); } EventResult BSGFxShaderFXTarget::ReceiveEvent(ApplyColorUpdateEvent * evn, void * dispatcher) { - FilterColor color; - if(((colorFlags >> 1) & 1) && colorType != kColorUnk7) + NiColor color; + if(((shaderFX.enabledStates >> 1) & 1) && HUDColorType != kHUDColorTypes_CustomColor) { - FilterColor * filtered = GetFilterColorByType(this, &color); - ApplyColorFilter(this, filtered, blue); + EnableColorMultipliers(GetColorMultipliersFromType(&color), shaderFX.colorBrightness); } - if((colorFlags & 1) && unkAC != 4) + if((shaderFX.enabledStates & 1) && backgroundColorType != kHUDColorTypes_PowerArmorColorOnly) { - SetDefaultColors(this); + ApplyBackgroundColorFromType(); } return kEvent_Continue; }; diff --git a/f4se/f4se/ScaleformValue.h b/f4se/f4se/ScaleformValue.h index 23dad96..6cc2dcc 100644 --- a/f4se/f4se/ScaleformValue.h +++ b/f4se/f4se/ScaleformValue.h @@ -3,6 +3,7 @@ #include "f4se_common/Utilities.h" #include "f4se/GameEvents.h" #include "f4se/ScaleformTypes.h" +#include "f4se/NiTypes.h" class GFxMovieView; class GFxMovieRoot; @@ -255,14 +256,14 @@ class BSGFxObject : public GFxValue class BSGFxDisplayObject : public BSGFxObject { public: - BSGFxDisplayObject() : parent(nullptr), unk48(0), unk4C(0) { } + BSGFxDisplayObject() : parent(nullptr) { } BSGFxDisplayObject(GFxValue * value) : BSGFxObject(value), parent(nullptr) { GFxValue width, height; GetMember("width", &width); GetMember("height", &height); - unk48 = width.GetNumber(); - unk4C = height.GetNumber(); + initialState.originalWidth = width.GetNumber(); + initialState.originalHeight = height.GetNumber(); } virtual ~BSGFxDisplayObject() { @@ -279,18 +280,65 @@ class BSGFxDisplayObject : public BSGFxObject GFxValue::DisplayInfo displayInfo2; // F0 }; - GFxValue * parent; // 40 - float unk48; // 48 - float unk4C; // 4C + struct InitialDisplayState + { + InitialDisplayState() : originalWidth(0), originalHeight(0) {} + + float originalWidth; + float originalHeight; + }; + + BSGFxDisplayObject* parent; // 40 + InitialDisplayState initialState; // 48 }; STATIC_ASSERT(sizeof(BSGFxDisplayObject) == 0x50); STATIC_ASSERT(offsetof(BSGFxDisplayObject::BSDisplayInfo, displayInfo2) == 0xF0); class BSGFxShaderFXTarget; -struct FilterColor +// 38 +struct UIShaderColors { - float r, g, b; + UIShaderColors() : colorBrightness(0), enabledStates(0) + { + backgroundQuad.m_top = 0; + backgroundQuad.m_bottom = 0; + backgroundQuad.m_left = 0; + backgroundQuad.m_right = 0; + } + + enum Flags + { + kBackgroundQuad = (1 << 0), + kColorMultiplier = (1 << 1), + kVerticalGradient = (1 << 2), + kUseAlphaForDropshadow = (1 << 3) + }; + + NiRect backgroundQuad; // 00 + NiColorA backgroundColor; // 10 + NiColorA colorMultipliers; // 20 + float colorBrightness; // 30 + int enabledStates; // 34 +}; + +// 48 +struct UIShaderFXInfo +{ + NiRect renderQuad; // 00 + UIShaderColors shaderFX; // 10 +}; + +enum HUDColorTypes +{ + kHUDColorTypes_NoColorMultiplier = 0, + kHUDColorTypes_MenuNoColorBackground, + kHUDColorTypes_GameplayHUDColor, + kHUDColorTypes_PlayerSetColor, + kHUDColorTypes_PowerArmorColorOnly, + kHUDColorTypes_WarningColor, + kHUDColorTypes_AltWarningColor, + kHUDColorTypes_CustomColor }; // B0 @@ -300,47 +348,20 @@ class BSGFxShaderFXTarget : public BSGFxDisplayObject, public: BSGFxShaderFXTarget() { } BSGFxShaderFXTarget(GFxValue * source) : BSGFxDisplayObject(source), - unk58(0), unk60(0), unk68(0), unk6C(0), unk70(0), unk74(0), unk78(0), unk7C(0), red(0), - green(0), blue(0), multiplier(0), unk94(0), unk98(0), unkA0(0), colorType(0), unkAC(0) { }//{ CALL_MEMBER_FN(this, Impl_ctor)(source); } + HUDColorType(kHUDColorTypes_NoColorMultiplier), backgroundColorType(kHUDColorTypes_NoColorMultiplier) { }//{ CALL_MEMBER_FN(this, Impl_ctor)(source); } virtual ~BSGFxShaderFXTarget();// { CALL_MEMBER_FN(this, Impl_dtor)(); }; - virtual void Unk_01(void * unk1, void * unk2) + virtual void AppendShaderFXInfos(BSTArray* colorFX, BSTArray* backgroundFX) { - Impl_Fn1(unk1, unk2); + Impl_AppendShaderFXInfos(colorFX, backgroundFX); }; virtual EventResult ReceiveEvent(ApplyColorUpdateEvent * evn, void * dispatcher); - enum ColorTypes - { - kColorUnk1 = 0, - kColorUnk2, - kColorNormal, - kColorUnk3, - kColorUnk4, - kColorWarning, - kColorUnk6, - kColorUnk7 - }; - - UInt64 unk58; // 58 - UInt64 unk60; // 60 - float unk68; // 68 - float unk6C; // 6C - float unk70; // 70 - float unk74; // 74 - float unk78; // 78 - float unk7C; // 7C - float red; // 80 - float green; // 84 - float blue; // 88 - UInt32 colorFlags; // 8C - float multiplier; // 90 - UInt32 unk94; // 94 - UInt64 unk98; // 98 - UInt64 unkA0; // A0 - UInt32 colorType; // A8 - UInt32 unkAC; // AC + UIShaderColors shaderFX; // 38 + BSTArray shaderFXObjects; // + HUDColorTypes HUDColorType; // + HUDColorTypes backgroundColorType; DEFINE_STATIC_HEAP(Heap_Allocate, Heap_Free) @@ -348,23 +369,16 @@ class BSGFxShaderFXTarget : public BSGFxDisplayObject, // 98B654B565F35633CBE8804A5CBF84646AE30A1B+9 DEFINE_MEMBER_FN_1(Impl_ctor, BSGFxShaderFXTarget *, 0x020F1770, GFxValue * source); + DEFINE_MEMBER_FN_2(Impl_ctor_2, BSGFxShaderFXTarget*, 0x020F1890, GFxValue* source, const char* memberName); + DEFINE_MEMBER_FN_2(Impl_ctor_3, BSGFxShaderFXTarget*, 0x020F19B0, GFxMovieView* source, const char* memberName); DEFINE_MEMBER_FN_0(Impl_dtor, void, 0x020F16D0); - DEFINE_MEMBER_FN_2(Impl_Fn1, void, 0x020F1BF0, void * unk1, void * unk2); + DEFINE_MEMBER_FN_2(Impl_AppendShaderFXInfos, void, 0x020F1BF0, BSTArray* colorFX, BSTArray* backgroundFX); + DEFINE_MEMBER_FN_1(GetColorMultipliersFromType, NiColor*, 0x020F2C90, NiColor* result); // This function acquires the HUD color by type e.g. normal, PA, hostile + DEFINE_MEMBER_FN_2(EnableColorMultipliers, void, 0x020F2990, const NiColor* color, float brightness); // Sets explicit component filter color + DEFINE_MEMBER_FN_0(ApplyBackgroundColorFromType, void, 0x020F2BE0); }; -STATIC_ASSERT(offsetof(BSGFxShaderFXTarget, red) == 0x80); STATIC_ASSERT(sizeof(BSGFxShaderFXTarget) == 0xB0); -// This function acquires the HUD color by type e.g. normal, PA, hostile -typedef FilterColor * (* _GetFilterColorByType)(BSGFxShaderFXTarget * component, FilterColor * color); -extern RelocAddr <_GetFilterColorByType> GetFilterColorByType; - -// Sets explicit component filter color -typedef void (* _ApplyColorFilter)(BSGFxShaderFXTarget * component, FilterColor * color, float unk1); -extern RelocAddr <_ApplyColorFilter> ApplyColorFilter; - -typedef void (* _SetDefaultColors)(BSGFxShaderFXTarget * component); -extern RelocAddr <_SetDefaultColors> SetDefaultColors; - typedef void * (* _GetExtDisplayInfo)(BSGFxDisplayObject::BSDisplayInfo * dInfo, BSGFxDisplayObject * target); extern RelocAddr <_GetExtDisplayInfo> GetExtDisplayInfo; diff --git a/f4se/f4se/Serialization.cpp b/f4se/f4se/Serialization.cpp index a67e86c..2c1db71 100644 --- a/f4se/f4se/Serialization.cpp +++ b/f4se/f4se/Serialization.cpp @@ -305,7 +305,7 @@ namespace Serialization bool ResolveHandle(UInt64 handle, UInt64 * handleOut) { - UInt8 modID = handle >> 24; + UInt32 modID = (handle & 0xFF000000) >> 24; if (modID == 0xFF) { diff --git a/f4se/f4se/f4se.cpp b/f4se/f4se/f4se.cpp index 9f14e57..62e89e1 100644 --- a/f4se/f4se/f4se.cpp +++ b/f4se/f4se/f4se.cpp @@ -3,7 +3,10 @@ #include "f4se_common/Relocation.h" #include "f4se_common/BranchTrampoline.h" #include "f4se_common/SafeWrite.h" +#include +#include #include +#include #include "common/IFileStream.h" #include "Hooks_ObScript.h" #include "Hooks_Papyrus.h" @@ -32,6 +35,32 @@ void WaitForDebugger(void) Sleep(1000 * 2); } +bool ShouldWaitForDebugger() +{ + const char* env = "F4SE_WAITFORDEBUGGER"; + const auto printErr = [=]() + { + const DWORD err = GetLastError(); + if (err != ERROR_ENVVAR_NOT_FOUND) + _ERROR("failed to get %s with error code %u", env, err); + }; + + std::vector buf; + const DWORD len = GetEnvironmentVariableA(env, buf.data(), 0); + if (len == 0) { + printErr(); + return false; + } + + buf.resize(len, '\0'); + if (GetEnvironmentVariableA(env, buf.data(), buf.size()) == 0) { + printErr(); + return false; + } + + return std::strcmp(buf.data(), "1") == 0; +} + static bool isInit = false; void F4SE_Initialize(void) @@ -55,24 +84,35 @@ void F4SE_Initialize(void) _MESSAGE("imagebase = %016I64X", GetModuleHandle(NULL)); _MESSAGE("reloc mgr imagebase = %016I64X", RelocationManager::s_baseAddr); -#ifdef _DEBUG - SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); + if (ShouldWaitForDebugger()) + { + SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); + WaitForDebugger(); + } - WaitForDebugger(); -#endif + const size_t poolSize = 1024 * 64; + const size_t reserveSize = 512; - if(!g_branchTrampoline.Create(1024 * 64)) + if(!g_branchTrampoline.Create(poolSize)) { _ERROR("couldn't create branch trampoline. this is fatal. skipping remainder of init process."); return; } - if(!g_localTrampoline.Create(1024 * 64, g_moduleHandle)) + if(!g_localTrampoline.Create(poolSize, g_moduleHandle)) { _ERROR("couldn't create codegen buffer. this is fatal. skipping remainder of init process."); return; } + const auto initAlloc = [=](PluginAllocator& alloc, BranchTrampoline& trampoline) + { + const auto size = poolSize - reserveSize; + alloc.Initialize(trampoline.Allocate(size), size); + }; + initAlloc(g_branchPluginAllocator, g_branchTrampoline); + initAlloc(g_localPluginAllocator, g_localTrampoline); + Hooks_Debug_Init(); Hooks_ObScript_Init(); Hooks_Papyrus_Init(); @@ -97,6 +137,15 @@ void F4SE_Initialize(void) Hooks_Threads_Commit(); Hooks_Camera_Commit(); + const auto printAlloc = [=](BranchTrampoline& pool, const char* name) + { + const auto allocated = reserveSize - pool.Remain(); + assert(allocated <= reserveSize); + _DMESSAGE("F4SE allocated %u bytes from %s pool", allocated, name); + }; + printAlloc(g_branchTrampoline, "branch"); + printAlloc(g_localTrampoline, "local"); + Init_CoreSerialization_Callbacks(); FlushInstructionCache(GetCurrentProcess(), NULL, 0); diff --git a/f4se/f4se/f4se.vcxproj b/f4se/f4se/f4se.vcxproj index 0d41874..56560a3 100644 --- a/f4se/f4se/f4se.vcxproj +++ b/f4se/f4se/f4se.vcxproj @@ -36,6 +36,7 @@ + @@ -147,6 +148,7 @@ + @@ -269,7 +271,7 @@ MultiByte - DynamicLibrary + StaticLibrary false v141 true diff --git a/f4se/f4se/f4se.vcxproj.filters b/f4se/f4se/f4se.vcxproj.filters index 7704ebf..f71f0ad 100644 --- a/f4se/f4se/f4se.vcxproj.filters +++ b/f4se/f4se/f4se.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -323,6 +323,9 @@ papyrus\functions + + api + @@ -702,5 +705,8 @@ papyrus\functions + + api + \ No newline at end of file diff --git a/f4se/f4se_common/f4se_version.h b/f4se/f4se_common/f4se_version.h index b4e70e5..1b55465 100644 --- a/f4se/f4se_common/f4se_version.h +++ b/f4se/f4se_common/f4se_version.h @@ -4,10 +4,10 @@ // these have to be macros so they can be used in the .rc #define F4SE_VERSION_INTEGER 0 #define F4SE_VERSION_INTEGER_MINOR 6 -#define F4SE_VERSION_INTEGER_BETA 21 -#define F4SE_VERSION_VERSTRING "0, 0, 6, 21" -#define F4SE_VERSION_PADDEDSTRING "0023" -#define F4SE_VERSION_RELEASEIDX 23 +#define F4SE_VERSION_INTEGER_BETA 23 +#define F4SE_VERSION_VERSTRING "0, 0, 6, 23" +#define F4SE_VERSION_PADDEDSTRING "0025" +#define F4SE_VERSION_RELEASEIDX 25 #define MAKE_EXE_VERSION_EX(major, minor, build, sub) ((((major) & 0xFF) << 24) | (((minor) & 0xFF) << 16) | (((build) & 0xFFF) << 4) | ((sub) & 0xF)) #define MAKE_EXE_VERSION(major, minor, build) MAKE_EXE_VERSION_EX(major, minor, build, 0) @@ -73,6 +73,6 @@ // information about the state of the game at the time of release #define F4SE_TARGETING_BETA_VERSION 0 #define CURRENT_RELEASE_RUNTIME RUNTIME_VERSION_1_10_163 -#define CURRENT_RELEASE_F4SE_STR "0.6.21" +#define CURRENT_RELEASE_F4SE_STR "0.6.23" #endif /* __F4SE_VERSION_H__ */ diff --git a/f4se/f4se_readme.txt b/f4se/f4se_readme.txt index 173e82e..8e7ba8d 100644 --- a/f4se/f4se_readme.txt +++ b/f4se/f4se_readme.txt @@ -1,4 +1,4 @@ -Fallout 4 Script Extender v0.6.21 +Fallout 4 Script Extender v0.6.23 by Ian Patterson, Stephen Abel, and Brendan Borthwick (ianpatt, behippo, and plb) The Fallout 4 Script Extender, or F4SE for short, is a modder's resource that expands the scripting capabilities of Fallout 4. It does so without modifying the executable files on disk, so there are no permanent side effects. diff --git a/f4se/f4se_whatsnew.txt b/f4se/f4se_whatsnew.txt index a557a12..f664029 100644 --- a/f4se/f4se_whatsnew.txt +++ b/f4se/f4se_whatsnew.txt @@ -1,3 +1,17 @@ +0.6.23 +- workaround for bug in High FPS Physics Fix + +0.6.22 +- fix ConstructibleComponent.object type +- add a basic diagnosis for the dreaded error code 126 +- new interface for plugins to allocate from F4SE's branch-accessable pools +- new interface for plugins to query other plugins PluginInfo struct +- add new command line option "waitfordebugger" to explicitly wait for a debugger to attach regardless of build config +- add Actor.IsProtected +- fix Perk.IsPlayable, Perk.IsHidden +- fix setting process priority in f4se_loader +- fix resolving handles referring to ESL plugins + 0.6.21 - fix Game.GetInstalledLightPlugins when more than 255 plugins are installed From 7e502aec4ce23a7109acfc04ea32519f9c0a5d75 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Tue, 26 Jul 2022 04:27:07 -0700 Subject: [PATCH 37/66] Fix check for potential null pointer access --- CBPSSE/ActorUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 1e449de..475d3b6 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -106,7 +106,7 @@ const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, logger.Error("Actor is not valid"); return actorUtils::EquippedArmor{ nullptr, nullptr }; } - if (!actor->equipData && !actor->equipData->slots) { + if (!actor->equipData || !actor->equipData->slots) { logger.Error("Actor has no equipData"); return actorUtils::EquippedArmor{ nullptr, nullptr }; } From 7896289df3633c590077f3529f867cdc6145d3a2 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Wed, 27 Jul 2022 17:07:23 -0700 Subject: [PATCH 38/66] Backwards compatibility for detectArmor --- CBPSSE/config.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 32ab9f0..c1b3ded 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -132,6 +132,35 @@ bool LoadConfig() { // Read sections auto sections = configReader.Sections(); + + // Backwards compatibility for detectArmor style method + { + auto prioritySection = sections.find("Priority"); + if (configReader.GetBoolean("General", "detectArmor", false) && (prioritySection == sections.end() || configReader.Section(*prioritySection).empty())) { + priorityNameMappings["A"] = 0; + configArmorOverrideMap[0].slots.emplace(11); + configArmorOverrideMap[0].isWhitelist = false; + + //Read armorIgnore + auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); + { + size_t commaPos; + do { + commaPos = armorIgnoreStr.find_first_of(","); + auto token = armorIgnoreStr.substr(0, commaPos); + UInt32 formID; + std::stringstream ss; + ss << std::hex << token; + ss >> formID; + configArmorOverrideMap[0].armors.emplace(formID); + armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); + + //logger.Info(" %s, %s, %d, %d\n", token.c_str(), whitelistName.c_str(), commaPos >= 0, colonPos < 0); + } while (commaPos != -1); + } + } + } + for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) { // Split for override section check From b7a8bed239bc3e8c2d20bcd6623eb8d6f76d49c5 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 28 Jul 2022 00:39:27 -0700 Subject: [PATCH 39/66] Compatibility fix for "disabled" breast bones in [Attach.A] from detectArmor method --- CBPSSE/config.cpp | 57 +++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index c1b3ded..2cb1af0 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -132,32 +132,32 @@ bool LoadConfig() { // Read sections auto sections = configReader.Sections(); + auto prioritySection = sections.find("Priority"); + bool detectArmorCompat = configReader.GetBoolean("General", "detectArmor", false) && (prioritySection == sections.end() || configReader.Section(*prioritySection).empty()); // Backwards compatibility for detectArmor style method - { - auto prioritySection = sections.find("Priority"); - if (configReader.GetBoolean("General", "detectArmor", false) && (prioritySection == sections.end() || configReader.Section(*prioritySection).empty())) { - priorityNameMappings["A"] = 0; - configArmorOverrideMap[0].slots.emplace(11); - configArmorOverrideMap[0].isWhitelist = false; - - //Read armorIgnore - auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); - { - size_t commaPos; - do { - commaPos = armorIgnoreStr.find_first_of(","); - auto token = armorIgnoreStr.substr(0, commaPos); - UInt32 formID; - std::stringstream ss; - ss << std::hex << token; - ss >> formID; - configArmorOverrideMap[0].armors.emplace(formID); - armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); - - //logger.Info(" %s, %s, %d, %d\n", token.c_str(), whitelistName.c_str(), commaPos >= 0, colonPos < 0); - } while (commaPos != -1); - } + if (detectArmorCompat) { + priorityNameMappings["A"] = 0; + configArmorOverrideMap[0].slots.emplace(11); + usedSlots.emplace(11); + configArmorOverrideMap[0].isWhitelist = false; + + //Read armorIgnore + auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); + { + size_t commaPos; + do { + commaPos = armorIgnoreStr.find_first_of(","); + auto token = armorIgnoreStr.substr(0, commaPos); + UInt32 formID; + std::stringstream ss; + ss << std::hex << token; + ss >> formID; + configArmorOverrideMap[0].armors.emplace(formID); + armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); + + //logger.Info(" %s, %s, %d, %d\n", token.c_str(), whitelistName.c_str(), commaPos >= 0, colonPos < 0); + } while (commaPos != -1); } } @@ -396,6 +396,15 @@ bool LoadConfig() { bonesSet = std::set(boneNames.begin(), boneNames.end()); boneNames.assign(bonesSet.begin(), bonesSet.end()); + // "Delete" bones specified in [Attach] but not [Attach.A] for compatibility with presets that remove breast bone jiggle when chest armor equipped + if (detectArmorCompat) { + for (auto boneName : boneNames) { + if (configArmorOverrideMap[0].config.find(boneName) == configArmorOverrideMap[0].config.end()) { + configArmorOverrideMap[0].config[boneName]; + } + } + } + logger.Error("Finished CBP Config\n"); return reloadActors; } From 08261b6facd8b2e4d204e8c291f8c090770ae7a7 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 28 Jul 2022 02:01:30 -0700 Subject: [PATCH 40/66] Fix to keep malformed armor from being added when by armorIgnore --- CBPSSE/config.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 2cb1af0..2bc52b2 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -149,14 +149,14 @@ bool LoadConfig() { do { commaPos = armorIgnoreStr.find_first_of(","); auto token = armorIgnoreStr.substr(0, commaPos); - UInt32 formID; - std::stringstream ss; - ss << std::hex << token; - ss >> formID; - configArmorOverrideMap[0].armors.emplace(formID); - armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); - //logger.Info(" %s, %s, %d, %d\n", token.c_str(), whitelistName.c_str(), commaPos >= 0, colonPos < 0); + try { + UInt32 formID = std::stoul(token); + configArmorOverrideMap[0].armors.emplace(formID); + } + catch (const std::exception&) {} + + armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); } while (commaPos != -1); } } From 1c5825d843964eb93d717299411d697f3730af5c Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 28 Jul 2022 02:03:26 -0700 Subject: [PATCH 41/66] Fix config being mistakenly overridden when whitelist==false --- CBPSSE/ActorUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 475d3b6..82c7808 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -166,7 +166,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { } } - if (!data.isWhitelist && armorFormID == data.armors.end()) { + if (!data.isWhitelist && armorFormID == data.armors.end() && !equippedList.empty()) { for (auto val : data.config) { if (data.config[val.first].empty()) { baseConfig.erase(val.first); From a19b53fbd50318787d2b3e7d797a22038d995734 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 28 Jul 2022 02:18:36 -0700 Subject: [PATCH 42/66] Cache configs rather than building every frame --- CBPSSE/ActorUtils.cpp | 21 ++++++++++++++++++++- CBPSSE/config.cpp | 7 ++++++- CBPSSE/config.h | 3 +++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 82c7808..7c5e113 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -127,8 +127,26 @@ const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, } config_t actorUtils::BuildConfigForActor(Actor* actor) { - config_t baseConfig = config; + std::multiset key; + for (auto slot : usedSlots) { + UInt64 data = 0; + auto equipped = actorUtils::GetActorEquippedArmor(actor, slot); + if (equipped.armor) { + data |= equipped.armor->formID; + data = data << 32; + } + if (equipped.model) { + data |= equipped.model->formID; + } + key.emplace(data); + } + auto found = cachedConfigs.find(key); + if (found != cachedConfigs.end()) { + return found->second; + } + + config_t baseConfig = config; for (auto overrideConfigIter = configArmorOverrideMap.rbegin(); overrideConfigIter != configArmorOverrideMap.rend(); ++overrideConfigIter) { auto data = overrideConfigIter->second; @@ -178,5 +196,6 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { } } + cachedConfigs[key] = baseConfig; return baseConfig; } \ No newline at end of file diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 2bc52b2..2d943f3 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -29,6 +29,8 @@ bool useWhitelist = false; config_t config; config_t configArmor; std::map configArmorOverrideMap; +std::unordered_set usedSlots; +std::map, config_t> cachedConfigs; // TODO data structure these whitelist_t whitelist; @@ -100,6 +102,8 @@ bool LoadConfig() { boneNames.clear(); config.clear(); configArmorOverrideMap.clear(); + usedSlots.clear(); + cachedConfigs.clear(); // Note: Using INIReader results in a slight double read INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); @@ -272,6 +276,7 @@ bool LoadConfig() { } configArmorOverrideMap[armorPriority].slots.emplace(slot - 30); + usedSlots.emplace(slot - 30); } while (commaPos != std::string::npos); configArmorOverrideMap[armorPriority].isWhitelist = configReader.GetBoolean(*sectionsIter, "whitelist", false); @@ -396,7 +401,7 @@ bool LoadConfig() { bonesSet = std::set(boneNames.begin(), boneNames.end()); boneNames.assign(bonesSet.begin(), bonesSet.end()); - // "Delete" bones specified in [Attach] but not [Attach.A] for compatibility with presets that remove breast bone jiggle when chest armor equipped + // "Delete" bones specified in [Attach] but not [Attach.A] from the latter for compatibility with presets that remove breast bone jiggle when chest armor equipped if (detectArmorCompat) { for (auto boneName : boneNames) { if (configArmorOverrideMap[0].config.find(boneName) == configArmorOverrideMap[0].config.end()) { diff --git a/CBPSSE/config.h b/CBPSSE/config.h index fad52aa..0fd9d68 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include @@ -36,6 +37,8 @@ extern config_t config; extern std::map configArmorOverrideMap; extern whitelist_t whitelist; extern std::vector raceWhitelist; +extern std::unordered_set usedSlots; +extern std::map, config_t> cachedConfigs; bool LoadConfig(); void DumpWhitelistToLog(); \ No newline at end of file From 309252bb2487fd29d0d53c6566aa5cce21c6a5dd Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 28 Jul 2022 02:31:44 -0700 Subject: [PATCH 43/66] Superfluous variable --- CBPSSE/config.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 2d943f3..25eff54 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -27,7 +27,6 @@ bool npcOnly = false; bool useWhitelist = false; config_t config; -config_t configArmor; std::map configArmorOverrideMap; std::unordered_set usedSlots; std::map, config_t> cachedConfigs; From 9c9e7ef06251ae2e236134feddeef13baa72dc98 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 28 Jul 2022 02:58:26 -0700 Subject: [PATCH 44/66] Rename whitelist key in Armor sections to invert to avoid confusion other things named whitelist --- CBPSSE/ActorUtils.cpp | 4 ++-- CBPSSE/config.cpp | 6 +++--- CBPSSE/config.h | 2 +- f4se/f4se/f4se.vcxproj | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 7c5e113..93e615e 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -163,7 +163,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { bool breakOutside = false; for (auto equipped : equippedList) { if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) { - if (data.isWhitelist) { + if (data.isInverted) { for (auto val : data.config) { if (data.config[val.first].empty()) { baseConfig.erase(val.first); @@ -184,7 +184,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { } } - if (!data.isWhitelist && armorFormID == data.armors.end() && !equippedList.empty()) { + if (!data.isInverted && armorFormID == data.armors.end() && !equippedList.empty()) { for (auto val : data.config) { if (data.config[val.first].empty()) { baseConfig.erase(val.first); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 25eff54..e77102d 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -143,7 +143,7 @@ bool LoadConfig() { priorityNameMappings["A"] = 0; configArmorOverrideMap[0].slots.emplace(11); usedSlots.emplace(11); - configArmorOverrideMap[0].isWhitelist = false; + configArmorOverrideMap[0].isInverted = false; //Read armorIgnore auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); @@ -278,13 +278,13 @@ bool LoadConfig() { usedSlots.emplace(slot - 30); } while (commaPos != std::string::npos); - configArmorOverrideMap[armorPriority].isWhitelist = configReader.GetBoolean(*sectionsIter, "whitelist", false); + configArmorOverrideMap[armorPriority].isInverted = configReader.GetBoolean(*sectionsIter, "invert", false); // Get section contents auto sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& key = valuesIter.first; - if (key == "whitelist" || key == "slots") { + if (key == "invert" || key == "slots") { continue; } diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 0fd9d68..5579540 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -20,7 +20,7 @@ typedef std::unordered_map config_t; typedef std::unordered_map> whitelist_t; struct armorOverrideData { - bool isWhitelist; + bool isInverted; std::unordered_set slots; std::unordered_set armors; config_t config; diff --git a/f4se/f4se/f4se.vcxproj b/f4se/f4se/f4se.vcxproj index 0d41874..b7cbf25 100644 --- a/f4se/f4se/f4se.vcxproj +++ b/f4se/f4se/f4se.vcxproj @@ -269,7 +269,7 @@ MultiByte - DynamicLibrary + StaticLibrary false v141 true From 3da8acf9f1cfabe9e9613191a5bd3453a0037fe7 Mon Sep 17 00:00:00 2001 From: Cyberslas Date: Thu, 28 Jul 2022 03:03:33 -0700 Subject: [PATCH 45/66] Undo mistakenly committed change to project file --- f4se/f4se/f4se.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/f4se/f4se/f4se.vcxproj b/f4se/f4se/f4se.vcxproj index b7cbf25..0d41874 100644 --- a/f4se/f4se/f4se.vcxproj +++ b/f4se/f4se/f4se.vcxproj @@ -269,7 +269,7 @@ MultiByte - StaticLibrary + DynamicLibrary false v141 true From 612626c9d20b05d62c43b4849383a3cfc157f534 Mon Sep 17 00:00:00 2001 From: ericncream Date: Tue, 2 Aug 2022 04:44:20 -0700 Subject: [PATCH 46/66] Rename inverted to invertedFilter. --- CBPSSE/ActorUtils.cpp | 4 ++-- CBPSSE/config.cpp | 6 +++--- CBPSSE/config.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 93e615e..744c3fd 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -163,7 +163,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { bool breakOutside = false; for (auto equipped : equippedList) { if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) { - if (data.isInverted) { + if (data.isFilterInverted) { for (auto val : data.config) { if (data.config[val.first].empty()) { baseConfig.erase(val.first); @@ -184,7 +184,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { } } - if (!data.isInverted && armorFormID == data.armors.end() && !equippedList.empty()) { + if (!data.isFilterInverted && armorFormID == data.armors.end() && !equippedList.empty()) { for (auto val : data.config) { if (data.config[val.first].empty()) { baseConfig.erase(val.first); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index e77102d..ac58ad0 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -143,7 +143,7 @@ bool LoadConfig() { priorityNameMappings["A"] = 0; configArmorOverrideMap[0].slots.emplace(11); usedSlots.emplace(11); - configArmorOverrideMap[0].isInverted = false; + configArmorOverrideMap[0].isFilterInverted = false; //Read armorIgnore auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); @@ -278,13 +278,13 @@ bool LoadConfig() { usedSlots.emplace(slot - 30); } while (commaPos != std::string::npos); - configArmorOverrideMap[armorPriority].isInverted = configReader.GetBoolean(*sectionsIter, "invert", false); + configArmorOverrideMap[armorPriority].isFilterInverted = configReader.GetBoolean(*sectionsIter, "invertFilter", false); // Get section contents auto sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& key = valuesIter.first; - if (key == "invert" || key == "slots") { + if (key == "invertFilter" || key == "slots") { continue; } diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 5579540..445eb77 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -20,7 +20,7 @@ typedef std::unordered_map config_t; typedef std::unordered_map> whitelist_t; struct armorOverrideData { - bool isInverted; + bool isFilterInverted; std::unordered_set slots; std::unordered_set armors; config_t config; From 5985e26c065bf4ec971d635afc4748e62aa4c30c Mon Sep 17 00:00:00 2001 From: ericncream Date: Wed, 3 Aug 2022 04:35:21 -0700 Subject: [PATCH 47/66] Remove armorIgnore. --- CBPSSE/config.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index ac58ad0..6332a08 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -144,24 +144,6 @@ bool LoadConfig() { configArmorOverrideMap[0].slots.emplace(11); usedSlots.emplace(11); configArmorOverrideMap[0].isFilterInverted = false; - - //Read armorIgnore - auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); - { - size_t commaPos; - do { - commaPos = armorIgnoreStr.find_first_of(","); - auto token = armorIgnoreStr.substr(0, commaPos); - - try { - UInt32 formID = std::stoul(token); - configArmorOverrideMap[0].armors.emplace(formID); - } - catch (const std::exception&) {} - - armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); - } while (commaPos != -1); - } } for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) { From ede5f196b0dba211956bd436d8c70b5a91a76db6 Mon Sep 17 00:00:00 2001 From: ericncream Date: Thu, 11 Aug 2022 04:13:33 -0700 Subject: [PATCH 48/66] Various performance tweaks: Things are only added to SimObj if they are valid and whitelisted. Thing::Update will grab skeleton obj directly. UpdateConfig doesn't happen all the time now. --- CBPSSE/ActorUtils.cpp | 51 ++++++++++++++++---- CBPSSE/ActorUtils.h | 2 + CBPSSE/SimObj.cpp | 105 +++++++++++++++++++++++++----------------- CBPSSE/SimObj.h | 22 +++++++-- CBPSSE/Thing.cpp | 70 ++++++++++++++++------------ CBPSSE/Thing.h | 10 ++-- CBPSSE/scan.cpp | 21 +++++++-- 7 files changed, 188 insertions(+), 93 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 744c3fd..0210a9e 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -5,8 +5,13 @@ #include "f4se/GameExtraData.h" #include "f4se/GameObjects.h" +#include #include "f4se/GameRTTI.h" +#include +#include +#include + std::string actorUtils::GetActorRaceEID(Actor* actor) { if (!IsActorValid(actor)) { logger.Info("GetActorRaceEID: no actor!\n"); @@ -16,6 +21,28 @@ std::string actorUtils::GetActorRaceEID(Actor* actor) { return std::string(actor->race->editorId.c_str()); } +NiAVObject* actorUtils::GetBaseSkeleton(Actor* actor) { + BSFixedString skeletonNif_name("skeleton.nif"); + + if (!actorUtils::IsActorValid(actor)) { + logger.Error("%s: No valid actor\n", __func__); + return NULL; + } + auto loadedState = actor->unkF0; + if (!loadedState || !loadedState->rootNode) { + logger.Error("%s:No loaded state for actor %08x\n", __func__, actor->formID); + return NULL; + } + auto obj = loadedState->rootNode->GetObjectByName(&skeletonNif_name); + + if (!obj) { + logger.Error("%s: Couldn't get name for loaded state for actor %08x\n", __func__, actor->formID); + return NULL; + } + + return obj; +} + bool actorUtils::IsActorMale(Actor *actor) { if (!IsActorValid(actor)) { @@ -51,7 +78,7 @@ bool actorUtils::IsActorInPowerArmor(Actor* actor) { bool actorUtils::IsActorTrackable(Actor* actor) { if (!IsActorValid(actor)) { - logger.Info("IsActorTrackable: actor is not valid.\n"); + logger.Info("%s: actor %x is not trackable.\n", __func__, actor->formID); return false; } @@ -65,17 +92,17 @@ bool actorUtils::IsActorTrackable(Actor* actor) { bool actorUtils::IsActorValid(Actor* actor) { if (!actor) { - logger.Info("IsActorValid: actor is null\n"); + logger.Info("%s: actor %x is null\n", __func__, actor->formID); return false; } if (actor->flags & TESForm::kFlag_IsDeleted) { - logger.Info("IsActorValid: actor has deleted flag\n"); + logger.Info("%s: actor %x has deleted flag\n", __func__, actor->formID); return false; } if (actor && actor->unkF0 && actor->unkF0->rootNode) { return true; } - logger.Info("IsActorValid: actor is not valid state\n"); + logger.Info("%s: actor %x is not in a valid state\n", __func__, actor->formID); return false; } @@ -84,15 +111,21 @@ bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { logger.Info("IsBoneInWhitelist: actor is not valid.\n"); return false; } - + bool result; auto raceEID = actorUtils::GetActorRaceEID(actor); if (whitelist.find(boneName) != whitelist.end()) { auto racesMap = whitelist.at(boneName); if (racesMap.find(raceEID) != racesMap.end()) { - if (IsActorMale(actor)) - return racesMap.at(raceEID).male; - else - return racesMap.at(raceEID).female; + if (IsActorMale(actor)) { + result = racesMap.at(raceEID).male; + //logger.Info("%s: %s male is %d for actor %x.\n", __func__, boneName.c_str(), result, actor->formID); + return result; + } + else { + result = racesMap.at(raceEID).female; + //logger.Info("%s: %s female is %d for actor %x.\n", __func__, boneName.c_str(), result, actor->formID); + return result; + } } } return false; diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index 6ba34b9..81ca9af 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -9,10 +9,12 @@ namespace actorUtils { }; std::string GetActorRaceEID(Actor* actor); + NiAVObject* GetBaseSkeleton(Actor* actor); bool IsActorInPowerArmor(Actor* actor); bool IsActorMale(Actor* actor); bool IsActorTrackable(Actor* actor); bool IsActorValid(Actor* actor); + bool IsBoneValid(Actor* actor, BSFixedString* boneName); bool IsBoneInWhitelist(Actor* actor, std::string boneName); const EquippedArmor GetActorEquippedArmor(Actor* actor, UInt32 slot); diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 8f7e747..17b17c4 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -8,6 +8,7 @@ #include "PapyrusOCBP.h" #include "SimObj.h" +using actorUtils::GetBaseSkeleton; using actorUtils::IsBoneInWhitelist; using actorUtils::IsActorInPowerArmor; @@ -17,16 +18,42 @@ std::vector boneNames; SimObj::SimObj(Actor *actor, config_t &config) : things(4) { id = actor->formID; + gender = Gender::Unassigned; + raceEid = std::string(""); } SimObj::~SimObj() { } +bool SimObj::AddBonesToThings(Actor* actor, std::vector& boneNames) { + logger.Error("%s\n", __func__); + if (!actor) { + return false; + } + auto loadedData = actor->unkF0; + + if (loadedData && loadedData->rootNode) { + for (std::string b : boneNames) { + if (!useWhitelist || (IsBoneInWhitelist(actor, b) && useWhitelist)) { + logger.Error("%s: adding Bone %s for actor %08x\n", __func__, b.c_str(), actor->formID); + BSFixedString cs(b.c_str()); + auto bone = loadedData->rootNode->GetObjectByName(&cs); + auto findBone = things.find(b); + if (!bone) { + logger.Info("%s: Failed to find Bone %s for actor %08x\n", __func__, b.c_str(), actor->formID); + } + else if (findBone == things.end()) { + //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); + things.emplace(b, Thing(bone, cs)); + } + } + } + } + return true; +} bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &config) { -// logger.error("bind\n"); - if (!actor) { return false; } @@ -35,29 +62,42 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c bound = true; things.clear(); - for (std::string b : boneNames) { - const char* bone_c_str = b.c_str(); - BSFixedString cs(bone_c_str); - auto bone = loadedData->rootNode->GetObjectByName(&cs); - if (!bone) { - logger.Info("Failed to find Bone %s for actor %08x\n", b.c_str(), actor->formID); - } else { - //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); - things.emplace(b, Thing(bone, cs)); - } + + if (actorUtils::IsActorMale(actor)) { + gender = Gender::Male; + } + else { + gender = Gender::Female; } - UpdateConfig(actor, std::vector(), config); + + raceEid = actorUtils::GetActorRaceEID(actor); + + AddBonesToThings(actor, boneNames); + UpdateConfig(config); return true; } return false; } +SimObj::Gender SimObj::GetGender() { + return gender; +} + +std::string SimObj::GetRaceEID() { + return raceEid; +} + +void SimObj::Reset() { + bound = false; + things.clear(); +} + void SimObj::Update(Actor *actor) { if (!bound) return; - logger.Error("SimObj::Update\n"); + for (auto &t : things) { - logger.Info("SimObj update: doing thing %s\n", t.first.c_str()); + //logger.Info("SimObj update: doing thing %s\n", t.first.c_str()); // Might be a better way to do this if (boneIgnores.find(actor->formID) != boneIgnores.end()) { @@ -69,38 +109,19 @@ void SimObj::Update(Actor *actor) { } } - if (!useWhitelist || (IsBoneInWhitelist(actor, t.first) && useWhitelist) && - !IsActorInPowerArmor(actor)) - { - logger.Error("SimObj::Update - calling Thing::Update\n"); - t.second.Update(actor); + if (!useWhitelist || + (IsBoneInWhitelist(actor, t.first) && useWhitelist) && + !IsActorInPowerArmor(actor) && + NULL != GetBaseSkeleton(actor)) { + t.second.UpdateThing(actor); } } - //logger.error("end SimObj update\n"); } -bool SimObj::UpdateConfig(Actor* actor, std::vector& boneNames, config_t& config) { - logger.Error("SimObj::UpdateConfig\n"); - if (!actor) { - return false; - } - auto loadedData = actor->unkF0; - if (loadedData && loadedData->rootNode) { - for (std::string b : boneNames) { - logger.Error("SimObj::UpdateConfig - adding bone %s\n", b.c_str()); - BSFixedString cs(b.c_str()); - auto bone = loadedData->rootNode->GetObjectByName(&cs); - auto findBone = things.find(b); - if (!bone) { - logger.Info("Failed to find Bone %s for actor %08x\n", b.c_str(), actor->formID); - } - else if (findBone == things.end()) { - //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); - things.emplace(b, Thing(bone, cs)); - } - } - } +bool SimObj::UpdateConfig(config_t& config) { + logger.Error("%s\n", __func__); for (auto &thing : things) { + //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); thing.second.UpdateConfig(config[std::string(thing.first)]); } return true; diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index a5a04fd..1bb0694 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -7,18 +7,32 @@ #include "config.h" class SimObj { - UInt32 id = 0; - bool bound = false; + public: + enum class Gender + { + Male, + Female, + Unassigned + }; + std::unordered_map things; SimObj(Actor *actor, config_t &config); SimObj() {} ~SimObj(); + bool AddBonesToThings(Actor* actor, std::vector& boneNames); bool Bind(Actor *actor, std::vector &boneNames, config_t &config); + Gender GetGender(); + std::string GetRaceEID(); + void Reset(); void Update(Actor *actor); - bool UpdateConfig(Actor* actor, std::vector& boneNames, config_t& config); + bool UpdateConfig(config_t& config); bool IsBound() { return bound; } - +private: + UInt32 id = 0; + bool bound = false; + Gender gender; + std::string raceEid; }; extern std::vector boneNames; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 763cf55..b8d5a9e 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -119,39 +119,39 @@ template int sgn(T val) { return (T(0) < val) - (val < T(0)); } -NiAVObject* Thing::IsActorValid(Actor* actor) { +NiAVObject* Thing::IsThingActorValid(Actor* actor) { if (!actorUtils::IsActorValid(actor)) { - logger.Error("No valid actor in Thing::Update\n"); + logger.Error("%s: No valid actor in Thing::Update\n", __func__); return NULL; } auto loadedState = actor->unkF0; if (!loadedState || !loadedState->rootNode) { - logger.Error("No loaded state for actor %08x\n", actor->formID); + logger.Error("%s: No loaded state for actor %08x\n", __func__, actor->formID); return NULL; } auto obj = loadedState->rootNode->GetObjectByName(&boneName); if (!obj) { - logger.Error("Couldn't get name for loaded state for actor %08x\n", actor->formID); + logger.Error("%s: Couldn't get name for loaded state for actor %08x\n", __func__, actor->formID); return NULL; } if (!obj->m_parent) { - logger.Error("Couldn't get bone %s parent for actor %08x\n", boneName.c_str(), actor->formID); + logger.Error("%s: Couldn't get bone %s parent for actor %08x\n", __func__, boneName.c_str(), actor->formID); return NULL; } return obj; } -void Thing::Update(Actor *actor) { +void Thing::UpdateThing(Actor *actor) { /*LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; LARGE_INTEGER frequency; QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&startingTime);*/ - auto obj = IsActorValid(actor); + auto obj = IsThingActorValid(actor); if (!obj) { return; } @@ -194,58 +194,68 @@ void Thing::Update(Actor *actor) { auto origLocalRot_iter = origLocalRot.find(boneName.c_str()); if (origLocalPos_iter == origLocalPos.end()) { + origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; +#ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); logger.Error("firstRun pos Set: \n"); - origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; ShowPos(obj->m_localTransform.pos); +#endif } else { auto actorPosMap = origLocalPos.at(boneName.c_str()); auto actor_iter = actorPosMap.find(actor->formID); if (actor_iter == actorPosMap.end()) { + origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; +#ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); logger.Error("firstRun pos Set: \n"); - origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; ShowPos(obj->m_localTransform.pos); +#endif } } if (origLocalRot_iter == origLocalRot.end()) { + origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; +#ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); logger.Error("firstRun rot Set:\n"); - origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; ShowRot(obj->m_localTransform.rot); +#endif } else { auto actorRotMap = origLocalRot.at(boneName.c_str()); auto actor_iter = actorRotMap.find(actor->formID); if (actor_iter == actorRotMap.end()) { + origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; +#ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); logger.Error("firstRun rot Set: \n"); - origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; ShowRot(obj->m_localTransform.rot); +#endif } } - - auto skeletonObj = obj; - NiAVObject * comObj; - bool skeletonFound = false; - while (skeletonObj->m_parent) - { - if (skeletonObj->m_parent->m_name == BSFixedString(COM_boneName)) { - comObj = skeletonObj->m_parent; - } - else if (skeletonObj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { - skeletonObj = skeletonObj->m_parent; - skeletonFound = true; - break; - } - skeletonObj = skeletonObj->m_parent; - } - if (skeletonFound == false) { - logger.Error("Couldn't find skeleton for actor %08x\n", actor->formID); + auto skeletonObj = actorUtils::GetBaseSkeleton(actor); + if (skeletonObj == NULL) { + logger.Error("%s: Didn't find thing %s's base skeleton.nif for actor %08x \n", __func__, boneName.c_str(), actor->formID); return; } + //auto skeletonObj = obj; + //bool skeletonFound = false; + //while (skeletonObj->m_parent) + //{ + // if (skeletonObj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { + // skeletonObj = skeletonObj->m_parent; + // skeletonFound = true; + // break; + // } + // skeletonObj = skeletonObj->m_parent; + //} + //if (skeletonFound == false) { + // logger.Error("%s: Couldn't find thing %s's skeleton.nif for actor %08x\n", __func__, boneName.c_str(), actor->formID); + // return; + //} + + #if DEBUG logger.Error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); ShowRot(skeletonObj->m_worldTransform.rot); @@ -286,7 +296,7 @@ void Thing::Update(Actor *actor) { #endif if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { - logger.Error("transform reset\n"); + logger.Error("%s: bone %s transform reset for actor %x\n", __func__, boneName.c_str(), actor->formID); obj->m_localTransform.pos = origLocalPos[boneName.c_str()][actor->formID]; oldWorldPos = target; velocity = NiPoint3(0, 0, 0); diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index d4b3771..0be1864 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -1,7 +1,7 @@ #pragma once -#include -#include -#include +#include +#include +#include #include #include "config.h" @@ -61,9 +61,9 @@ class Thing { Thing(NiAVObject *obj, BSFixedString &name); ~Thing(); - NiAVObject* IsActorValid(Actor* actor); + NiAVObject* IsThingActorValid(Actor* actor); void Reset(Actor* actor); - void Update(Actor* actor); + void UpdateThing(Actor* actor); void UpdateConfig(configEntry_t& centry); void ShowPos(NiPoint3& p); diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 1855905..2ae3f11 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -46,6 +46,7 @@ using actorUtils::IsActorMale; using actorUtils::IsActorTrackable; using actorUtils::IsActorValid; using actorUtils::BuildConfigForActor; +using actorUtils::GetActorRaceEID; extern F4SETaskInterface *g_task; @@ -108,7 +109,7 @@ inline void safe_delete(T*& in) { -std::unordered_map actors; +std::unordered_map actors; // Map of Actor (stored as form ID) to its Simulation Object TESObjectCELL *curCell = nullptr; @@ -190,7 +191,8 @@ void UpdateActors() { //logger.error("Sim Object not found in tracked actors\n"); } else { - objIterator->second.UpdateConfig(a.actor, boneNames, config); + objIterator->second.AddBonesToThings(a.actor, boneNames); + objIterator->second.UpdateConfig(config); } } @@ -211,8 +213,21 @@ void UpdateActors() { config_t composedConfig = BuildConfigForActor(a.actor); auto &simObj = objIterator->second; + + + SimObj::Gender gender = IsActorMale(a.actor) ? SimObj::Gender::Male : SimObj::Gender::Female; + + // Pointer comparison is good enough? + // OR check if gender and/or race have changed + if (simObj.IsBound()) { + if (gender != simObj.GetGender() || + GetActorRaceEID(a.actor) != simObj.GetRaceEID()) { + logger.Info("%s: Reset sim object\n", __func__); + simObj.Reset(); + } + } + if (simObj.IsBound()) { - simObj.UpdateConfig(a.actor, boneNames, composedConfig); simObj.Update(a.actor); } else { From da2b5e48384589da9f91dc8c2b599fb6bd11d563 Mon Sep 17 00:00:00 2001 From: ericncream Date: Thu, 11 Aug 2022 04:13:52 -0700 Subject: [PATCH 49/66] Revert "Remove armorIgnore." This reverts commit 5985e26c065bf4ec971d635afc4748e62aa4c30c. --- CBPSSE/config.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 6332a08..ac58ad0 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -144,6 +144,24 @@ bool LoadConfig() { configArmorOverrideMap[0].slots.emplace(11); usedSlots.emplace(11); configArmorOverrideMap[0].isFilterInverted = false; + + //Read armorIgnore + auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); + { + size_t commaPos; + do { + commaPos = armorIgnoreStr.find_first_of(","); + auto token = armorIgnoreStr.substr(0, commaPos); + + try { + UInt32 formID = std::stoul(token); + configArmorOverrideMap[0].armors.emplace(formID); + } + catch (const std::exception&) {} + + armorIgnoreStr = armorIgnoreStr.substr(commaPos + 1); + } while (commaPos != -1); + } } for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) { From 398c6af0421fb1dec5947b6ca8ad8acf173b50ae Mon Sep 17 00:00:00 2001 From: ericncream Date: Wed, 17 Aug 2022 04:02:55 -0700 Subject: [PATCH 50/66] Reformatted brackets --- CBPSSE/ActorEntry.h | 5 +- CBPSSE/ActorUtils.cpp | 151 ++++++++++++++++-------- CBPSSE/ActorUtils.h | 8 +- CBPSSE/PapyrusOCBP.cpp | 56 ++++----- CBPSSE/SimObj.cpp | 80 ++++++++----- CBPSSE/SimObj.h | 9 +- CBPSSE/Thing.cpp | 106 +++++++++++------ CBPSSE/Thing.h | 9 +- CBPSSE/config.cpp | 262 ++++++++++++++++++++++++++--------------- CBPSSE/config.h | 9 +- CBPSSE/log.cpp | 48 ++++---- CBPSSE/log.h | 7 +- CBPSSE/main.cpp | 147 +++++++++++------------ CBPSSE/scan.cpp | 109 +++++++++++------ 14 files changed, 614 insertions(+), 392 deletions(-) diff --git a/CBPSSE/ActorEntry.h b/CBPSSE/ActorEntry.h index b75963d..3bc159b 100644 --- a/CBPSSE/ActorEntry.h +++ b/CBPSSE/ActorEntry.h @@ -1,8 +1,9 @@ #pragma once #include "f4se/GameReferences.h" -class ActorEntry { - public: +class ActorEntry +{ +public: UInt32 id; Actor* actor; }; \ No newline at end of file diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 0210a9e..fa2b964 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -12,8 +12,10 @@ #include #include -std::string actorUtils::GetActorRaceEID(Actor* actor) { - if (!IsActorValid(actor)) { +std::string actorUtils::GetActorRaceEID(Actor* actor) +{ + if (!IsActorValid(actor)) + { logger.Info("GetActorRaceEID: no actor!\n"); return ""; } @@ -21,21 +23,25 @@ std::string actorUtils::GetActorRaceEID(Actor* actor) { return std::string(actor->race->editorId.c_str()); } -NiAVObject* actorUtils::GetBaseSkeleton(Actor* actor) { +NiAVObject* actorUtils::GetBaseSkeleton(Actor* actor) +{ BSFixedString skeletonNif_name("skeleton.nif"); - if (!actorUtils::IsActorValid(actor)) { + if (!actorUtils::IsActorValid(actor)) + { logger.Error("%s: No valid actor\n", __func__); return NULL; } auto loadedState = actor->unkF0; - if (!loadedState || !loadedState->rootNode) { + if (!loadedState || !loadedState->rootNode) + { logger.Error("%s:No loaded state for actor %08x\n", __func__, actor->formID); return NULL; } auto obj = loadedState->rootNode->GetObjectByName(&skeletonNif_name); - if (!obj) { + if (!obj) + { logger.Error("%s: Couldn't get name for loaded state for actor %08x\n", __func__, actor->formID); return NULL; } @@ -43,9 +49,10 @@ NiAVObject* actorUtils::GetBaseSkeleton(Actor* actor) { return obj; } -bool actorUtils::IsActorMale(Actor *actor) +bool actorUtils::IsActorMale(Actor* actor) { - if (!IsActorValid(actor)) { + if (!IsActorValid(actor)) + { logger.Info("IsActorMale: no actor!\n"); return false; } @@ -60,13 +67,16 @@ bool actorUtils::IsActorMale(Actor *actor) return false; } -bool actorUtils::IsActorInPowerArmor(Actor* actor) { +bool actorUtils::IsActorInPowerArmor(Actor* actor) +{ bool isInPowerArmor = false; - if (!IsActorValid(actor)) { + if (!IsActorValid(actor)) + { logger.Info("IsActorInPowerArmor: no actor!\n"); return true; // will force game to not call Update } - if (!actor->extraDataList) { + if (!actor->extraDataList) + { logger.Info("IsActorInPowerArmor: no extraDataList!\n"); return true; // will force game to not call Update } @@ -76,52 +86,64 @@ bool actorUtils::IsActorInPowerArmor(Actor* actor) { return isInPowerArmor; } -bool actorUtils::IsActorTrackable(Actor* actor) { - if (!IsActorValid(actor)) { +bool actorUtils::IsActorTrackable(Actor* actor) +{ + if (!IsActorValid(actor)) + { logger.Info("%s: actor %x is not trackable.\n", __func__, actor->formID); return false; } bool inRaceWhitelist = find(raceWhitelist.begin(), raceWhitelist.end(), actorUtils::GetActorRaceEID(actor)) != raceWhitelist.end(); return (!playerOnly || (actor->formID == 0x14 && playerOnly)) && - (!maleOnly || (IsActorMale(actor) && maleOnly)) && - (!femaleOnly || (!IsActorMale(actor) && femaleOnly)) && - (!npcOnly || (actor->formID != 0x14 && npcOnly)) && - (!useWhitelist || (inRaceWhitelist && useWhitelist)); + (!maleOnly || (IsActorMale(actor) && maleOnly)) && + (!femaleOnly || (!IsActorMale(actor) && femaleOnly)) && + (!npcOnly || (actor->formID != 0x14 && npcOnly)) && + (!useWhitelist || (inRaceWhitelist && useWhitelist)); } -bool actorUtils::IsActorValid(Actor* actor) { - if (!actor) { +bool actorUtils::IsActorValid(Actor* actor) +{ + if (!actor) + { logger.Info("%s: actor %x is null\n", __func__, actor->formID); return false; } - if (actor->flags & TESForm::kFlag_IsDeleted) { + if (actor->flags & TESForm::kFlag_IsDeleted) + { logger.Info("%s: actor %x has deleted flag\n", __func__, actor->formID); return false; } - if (actor && actor->unkF0 && actor->unkF0->rootNode) { + if (actor && actor->unkF0 && actor->unkF0->rootNode) + { return true; } logger.Info("%s: actor %x is not in a valid state\n", __func__, actor->formID); return false; } -bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { - if (!IsActorValid(actor)) { +bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) +{ + if (!IsActorValid(actor)) + { logger.Info("IsBoneInWhitelist: actor is not valid.\n"); return false; } bool result; auto raceEID = actorUtils::GetActorRaceEID(actor); - if (whitelist.find(boneName) != whitelist.end()) { + if (whitelist.find(boneName) != whitelist.end()) + { auto racesMap = whitelist.at(boneName); - if (racesMap.find(raceEID) != racesMap.end()) { - if (IsActorMale(actor)) { + if (racesMap.find(raceEID) != racesMap.end()) + { + if (IsActorMale(actor)) + { result = racesMap.at(raceEID).male; //logger.Info("%s: %s male is %d for actor %x.\n", __func__, boneName.c_str(), result, actor->formID); return result; } - else { + else + { result = racesMap.at(raceEID).female; //logger.Info("%s: %s female is %d for actor %x.\n", __func__, boneName.c_str(), result, actor->formID); return result; @@ -131,15 +153,18 @@ bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) { return false; } -const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, UInt32 slot) { +const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, UInt32 slot) +{ bool isEquipped = false; bool isArmorIgnored = false; - if (!actorUtils::IsActorValid(actor)) { + if (!actorUtils::IsActorValid(actor)) + { logger.Error("Actor is not valid"); return actorUtils::EquippedArmor{ nullptr, nullptr }; } - if (!actor->equipData || !actor->equipData->slots) { + if (!actor->equipData || !actor->equipData->slots) + { logger.Error("Actor has no equipData"); return actorUtils::EquippedArmor{ nullptr, nullptr }; } @@ -147,8 +172,10 @@ const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, isEquipped = actor->equipData->slots[slot].item; // Check if armor is ignored - if (isEquipped) { - if (!actor->equipData->slots[slot].item) { + if (isEquipped) + { + if (!actor->equipData->slots[slot].item) + { logger.Error("slot %d item check failed.", slot); // redundant check but just in case return actorUtils::EquippedArmor{ nullptr, nullptr }; @@ -159,49 +186,64 @@ const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, return actorUtils::EquippedArmor{ nullptr, nullptr }; } -config_t actorUtils::BuildConfigForActor(Actor* actor) { +config_t actorUtils::BuildConfigForActor(Actor* actor) +{ std::multiset key; - for (auto slot : usedSlots) { + for (auto slot : usedSlots) + { UInt64 data = 0; auto equipped = actorUtils::GetActorEquippedArmor(actor, slot); - if (equipped.armor) { + if (equipped.armor) + { data |= equipped.armor->formID; data = data << 32; } - if (equipped.model) { + if (equipped.model) + { data |= equipped.model->formID; } key.emplace(data); } auto found = cachedConfigs.find(key); - if (found != cachedConfigs.end()) { + if (found != cachedConfigs.end()) + { return found->second; } config_t baseConfig = config; - for (auto overrideConfigIter = configArmorOverrideMap.rbegin(); overrideConfigIter != configArmorOverrideMap.rend(); ++overrideConfigIter) { + for (auto overrideConfigIter = configArmorOverrideMap.rbegin(); overrideConfigIter != configArmorOverrideMap.rend(); ++overrideConfigIter) + { auto data = overrideConfigIter->second; std::vector equippedList; - for (auto slot : data.slots) { + for (auto slot : data.slots) + { auto equipped = actorUtils::GetActorEquippedArmor(actor, slot); - if (equipped.armor && equipped.model) { + if (equipped.armor && equipped.model) + { equippedList.push_back(equipped); } } auto armorFormID = data.armors.begin(); - for (; armorFormID != data.armors.end(); ++armorFormID) { + for (; armorFormID != data.armors.end(); ++armorFormID) + { bool breakOutside = false; - for (auto equipped : equippedList) { - if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) { - if (data.isFilterInverted) { - for (auto val : data.config) { - if (data.config[val.first].empty()) { + for (auto equipped : equippedList) + { + if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) + { + if (data.isFilterInverted) + { + for (auto val : data.config) + { + if (data.config[val.first].empty()) + { baseConfig.erase(val.first); } - else { + else + { baseConfig[val.first] = val.second; } } @@ -212,17 +254,22 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { } } - if (breakOutside) { + if (breakOutside) + { break; } } - if (!data.isFilterInverted && armorFormID == data.armors.end() && !equippedList.empty()) { - for (auto val : data.config) { - if (data.config[val.first].empty()) { + if (!data.isFilterInverted && armorFormID == data.armors.end() && !equippedList.empty()) + { + for (auto val : data.config) + { + if (data.config[val.first].empty()) + { baseConfig.erase(val.first); } - else { + else + { baseConfig[val.first] = val.second; } } diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index 81ca9af..58db4a0 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -2,12 +2,14 @@ #include "f4se/GameReferences.h" #include "config.h" -namespace actorUtils { - struct EquippedArmor { +namespace actorUtils +{ + struct EquippedArmor + { const TESForm* armor; const TESForm* model; }; - + std::string GetActorRaceEID(Actor* actor); NiAVObject* GetBaseSkeleton(Actor* actor); bool IsActorInPowerArmor(Actor* actor); diff --git a/CBPSSE/PapyrusOCBP.cpp b/CBPSSE/PapyrusOCBP.cpp index c69166b..d9acfdb 100644 --- a/CBPSSE/PapyrusOCBP.cpp +++ b/CBPSSE/PapyrusOCBP.cpp @@ -21,38 +21,40 @@ std::unordered_map> boneIgnores; namespace papyrusOCBP { - void SetBoneToggle(StaticFunctionTag*, Actor* actor, bool toggle, BSFixedString boneName) - { - boneIgnores[actor->formID][std::string(boneName.c_str())] = toggle; - } - - bool GetBoneToggle(StaticFunctionTag*, Actor* actor, BSFixedString boneName) - { - if (boneIgnores.find(actor->formID) != boneIgnores.end()) { - auto actorsBoneIgns = boneIgnores.at(actor->formID); - if (actorsBoneIgns.find(std::string(boneName.c_str())) != actorsBoneIgns.end()) { - return actorsBoneIgns.at(std::string(boneName.c_str())); - } - } - - return false; - } - - void ClearBoneToggles(StaticFunctionTag*) - { - boneIgnores.clear(); - } + void SetBoneToggle(StaticFunctionTag*, Actor* actor, bool toggle, BSFixedString boneName) + { + boneIgnores[actor->formID][std::string(boneName.c_str())] = toggle; + } + + bool GetBoneToggle(StaticFunctionTag*, Actor* actor, BSFixedString boneName) + { + if (boneIgnores.find(actor->formID) != boneIgnores.end()) + { + auto actorsBoneIgns = boneIgnores.at(actor->formID); + if (actorsBoneIgns.find(std::string(boneName.c_str())) != actorsBoneIgns.end()) + { + return actorsBoneIgns.at(std::string(boneName.c_str())); + } + } + + return false; + } + + void ClearBoneToggles(StaticFunctionTag*) + { + boneIgnores.clear(); + } } void papyrusOCBP::RegisterFuncs(VirtualMachine* vm) { - vm->RegisterFunction( - new NativeFunction3("SetBoneToggle", "OCBP_API", papyrusOCBP::SetBoneToggle, vm)); + vm->RegisterFunction( + new NativeFunction3("SetBoneToggle", "OCBP_API", papyrusOCBP::SetBoneToggle, vm)); - vm->RegisterFunction( - new NativeFunction2("GetBoneToggle", "OCBP_API", papyrusOCBP::GetBoneToggle, vm)); + vm->RegisterFunction( + new NativeFunction2("GetBoneToggle", "OCBP_API", papyrusOCBP::GetBoneToggle, vm)); - vm->RegisterFunction( - new NativeFunction0("ClearBoneToggles", "OCBP_API", papyrusOCBP::ClearBoneToggles, vm)); + vm->RegisterFunction( + new NativeFunction0("ClearBoneToggles", "OCBP_API", papyrusOCBP::ClearBoneToggles, vm)); } \ No newline at end of file diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 17b17c4..45a0934 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -15,34 +15,43 @@ using actorUtils::IsActorInPowerArmor; // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory std::vector boneNames; -SimObj::SimObj(Actor *actor, config_t &config) - : things(4) { +SimObj::SimObj(Actor* actor, config_t& config) + : things(4) +{ id = actor->formID; gender = Gender::Unassigned; raceEid = std::string(""); } -SimObj::~SimObj() { +SimObj::~SimObj() +{ } -bool SimObj::AddBonesToThings(Actor* actor, std::vector& boneNames) { +bool SimObj::AddBonesToThings(Actor* actor, std::vector& boneNames) +{ logger.Error("%s\n", __func__); - if (!actor) { + if (!actor) + { return false; } auto loadedData = actor->unkF0; - if (loadedData && loadedData->rootNode) { - for (std::string b : boneNames) { - if (!useWhitelist || (IsBoneInWhitelist(actor, b) && useWhitelist)) { + if (loadedData && loadedData->rootNode) + { + for (std::string b : boneNames) + { + if (!useWhitelist || (IsBoneInWhitelist(actor, b) && useWhitelist)) + { logger.Error("%s: adding Bone %s for actor %08x\n", __func__, b.c_str(), actor->formID); BSFixedString cs(b.c_str()); auto bone = loadedData->rootNode->GetObjectByName(&cs); auto findBone = things.find(b); - if (!bone) { + if (!bone) + { logger.Info("%s: Failed to find Bone %s for actor %08x\n", __func__, b.c_str(), actor->formID); } - else if (findBone == things.end()) { + else if (findBone == things.end()) + { //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); things.emplace(b, Thing(bone, cs)); } @@ -52,21 +61,25 @@ bool SimObj::AddBonesToThings(Actor* actor, std::vector& boneNames) return true; } -bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &config) +bool SimObj::Bind(Actor* actor, std::vector& boneNames, config_t& config) { - if (!actor) { + if (!actor) + { return false; } auto loadedData = actor->unkF0; - if (loadedData && loadedData->rootNode) { + if (loadedData && loadedData->rootNode) + { bound = true; things.clear(); - - if (actorUtils::IsActorMale(actor)) { + + if (actorUtils::IsActorMale(actor)) + { gender = Gender::Male; } - else { + else + { gender = Gender::Female; } @@ -79,31 +92,39 @@ bool SimObj::Bind(Actor *actor, std::vector& boneNames, config_t &c return false; } -SimObj::Gender SimObj::GetGender() { +SimObj::Gender SimObj::GetGender() +{ return gender; } -std::string SimObj::GetRaceEID() { +std::string SimObj::GetRaceEID() +{ return raceEid; } -void SimObj::Reset() { +void SimObj::Reset() +{ bound = false; things.clear(); } -void SimObj::Update(Actor *actor) { +void SimObj::Update(Actor* actor) +{ if (!bound) return; - for (auto &t : things) { + for (auto& t : things) + { //logger.Info("SimObj update: doing thing %s\n", t.first.c_str()); - + // Might be a better way to do this - if (boneIgnores.find(actor->formID) != boneIgnores.end()) { + if (boneIgnores.find(actor->formID) != boneIgnores.end()) + { auto actorBoneMap = boneIgnores.at(actor->formID); - if (actorBoneMap.find(t.first) != actorBoneMap.end()) { - if (actorBoneMap.at(t.first)) { + if (actorBoneMap.find(t.first) != actorBoneMap.end()) + { + if (actorBoneMap.at(t.first)) + { continue; } } @@ -112,15 +133,18 @@ void SimObj::Update(Actor *actor) { if (!useWhitelist || (IsBoneInWhitelist(actor, t.first) && useWhitelist) && !IsActorInPowerArmor(actor) && - NULL != GetBaseSkeleton(actor)) { + NULL != GetBaseSkeleton(actor)) + { t.second.UpdateThing(actor); } } } -bool SimObj::UpdateConfig(config_t& config) { +bool SimObj::UpdateConfig(config_t& config) +{ logger.Error("%s\n", __func__); - for (auto &thing : things) { + for (auto& thing : things) + { //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); thing.second.UpdateConfig(config[std::string(thing.first)]); } diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index 1bb0694..7b0daa2 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -6,7 +6,8 @@ #include "Thing.h" #include "config.h" -class SimObj { +class SimObj +{ public: enum class Gender @@ -17,15 +18,15 @@ class SimObj { }; std::unordered_map things; - SimObj(Actor *actor, config_t &config); + SimObj(Actor* actor, config_t& config); SimObj() {} ~SimObj(); bool AddBonesToThings(Actor* actor, std::vector& boneNames); - bool Bind(Actor *actor, std::vector &boneNames, config_t &config); + bool Bind(Actor* actor, std::vector& boneNames, config_t& config); Gender GetGender(); std::string GetRaceEID(); void Reset(); - void Update(Actor *actor); + void Update(Actor* actor); bool UpdateConfig(config_t& config); bool IsBound() { return bound; } private: diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index b8d5a9e..b00c3ce 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -19,11 +19,13 @@ const char* COM_boneName = "COM"; pos_map Thing::origLocalPos; rot_map Thing::origLocalRot; -void Thing::ShowPos(NiPoint3& p) { +void Thing::ShowPos(NiPoint3& p) +{ logger.Info("%8.4f %8.4f %8.4f\n", p.x, p.y, p.z); } -void Thing::ShowRot(NiMatrix43& r) { +void Thing::ShowRot(NiMatrix43& r) +{ logger.Info("%8.4f %8.4f %8.4f %8.4f\n", r.data[0][0], r.data[0][1], r.data[0][2], r.data[0][3]); logger.Info("%8.4f %8.4f %8.4f %8.4f\n", r.data[1][0], r.data[1][1], r.data[1][2], r.data[1][3]); logger.Info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); @@ -31,17 +33,20 @@ void Thing::ShowRot(NiMatrix43& r) { Thing::Thing(NiAVObject* obj, BSFixedString& name) : boneName(name) - , velocity(NiPoint3(0, 0, 0)) { - + , velocity(NiPoint3(0, 0, 0)) +{ + // Set initial positions oldWorldPos = obj->m_worldTransform.pos; time = clock(); } -Thing::~Thing() { +Thing::~Thing() +{ } -void Thing::UpdateConfig(configEntry_t & centry) { +void Thing::UpdateConfig(configEntry_t& centry) +{ stiffness = centry["stiffness"]; stiffness2 = centry["stiffness2"]; damping = centry["damping"]; @@ -77,7 +82,7 @@ void Thing::UpdateConfig(configEntry_t & centry) { if (centry.find("timeStep") != centry.end()) timeStep = centry["timeStep"]; - else + else timeStep = 0.016f; gravityBias = centry["gravityBias"]; @@ -91,21 +96,25 @@ void Thing::UpdateConfig(configEntry_t & centry) { absRotX = centry["absRotX"] != 0.0; } -static float clamp(float val, float min, float max) { +static float clamp(float val, float min, float max) +{ if (val < min) return min; else if (val > max) return max; return val; } -void Thing::Reset(Actor *actor) { +void Thing::Reset(Actor* actor) +{ auto loadedState = actor->unkF0; - if (!loadedState || !loadedState->rootNode) { + if (!loadedState || !loadedState->rootNode) + { logger.Error("No loaded state for actor %08x\n", actor->formID); return; } auto obj = loadedState->rootNode->GetObjectByName(&boneName); - if (!obj) { + if (!obj) + { logger.Error("Couldn't get name for loaded state for actor %08x\n", actor->formID); return; } @@ -115,28 +124,34 @@ void Thing::Reset(Actor *actor) { } // Returns -template int sgn(T val) { +template int sgn(T val) +{ return (T(0) < val) - (val < T(0)); } -NiAVObject* Thing::IsThingActorValid(Actor* actor) { - if (!actorUtils::IsActorValid(actor)) { +NiAVObject* Thing::IsThingActorValid(Actor* actor) +{ + if (!actorUtils::IsActorValid(actor)) + { logger.Error("%s: No valid actor in Thing::Update\n", __func__); return NULL; } auto loadedState = actor->unkF0; - if (!loadedState || !loadedState->rootNode) { + if (!loadedState || !loadedState->rootNode) + { logger.Error("%s: No loaded state for actor %08x\n", __func__, actor->formID); return NULL; } auto obj = loadedState->rootNode->GetObjectByName(&boneName); - if (!obj) { + if (!obj) + { logger.Error("%s: Couldn't get name for loaded state for actor %08x\n", __func__, actor->formID); return NULL; } - if (!obj->m_parent) { + if (!obj->m_parent) + { logger.Error("%s: Couldn't get bone %s parent for actor %08x\n", __func__, boneName.c_str(), actor->formID); return NULL; } @@ -144,7 +159,8 @@ NiAVObject* Thing::IsThingActorValid(Actor* actor) { return obj; } -void Thing::UpdateThing(Actor *actor) { +void Thing::UpdateThing(Actor* actor) +{ /*LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; LARGE_INTEGER frequency; @@ -152,7 +168,8 @@ void Thing::UpdateThing(Actor *actor) { QueryPerformanceCounter(&startingTime);*/ auto obj = IsThingActorValid(actor); - if (!obj) { + if (!obj) + { return; } @@ -185,7 +202,7 @@ void Thing::UpdateThing(Actor *actor) { // logger.Error("Calculated m_worldTransform.rot Matrix:\n"); // ShowRot(sceneObj->m_localTransform.rot * sceneObj->m_parent->m_worldTransform.rot); //} - sceneObj = sceneObj->m_parent; + sceneObj = sceneObj->m_parent; } #endif @@ -193,7 +210,8 @@ void Thing::UpdateThing(Actor *actor) { auto origLocalPos_iter = origLocalPos.find(boneName.c_str()); auto origLocalRot_iter = origLocalRot.find(boneName.c_str()); - if (origLocalPos_iter == origLocalPos.end()) { + if (origLocalPos_iter == origLocalPos.end()) + { origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; #ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); @@ -201,10 +219,12 @@ void Thing::UpdateThing(Actor *actor) { ShowPos(obj->m_localTransform.pos); #endif } - else { + else + { auto actorPosMap = origLocalPos.at(boneName.c_str()); auto actor_iter = actorPosMap.find(actor->formID); - if (actor_iter == actorPosMap.end()) { + if (actor_iter == actorPosMap.end()) + { origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; #ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); @@ -213,7 +233,8 @@ void Thing::UpdateThing(Actor *actor) { #endif } } - if (origLocalRot_iter == origLocalRot.end()) { + if (origLocalRot_iter == origLocalRot.end()) + { origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; #ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); @@ -221,10 +242,12 @@ void Thing::UpdateThing(Actor *actor) { ShowRot(obj->m_localTransform.rot); #endif } - else { + else + { auto actorRotMap = origLocalRot.at(boneName.c_str()); auto actor_iter = actorRotMap.find(actor->formID); - if (actor_iter == actorRotMap.end()) { + if (actor_iter == actorRotMap.end()) + { origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; #ifdef DEBUG logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); @@ -234,7 +257,8 @@ void Thing::UpdateThing(Actor *actor) { } } auto skeletonObj = actorUtils::GetBaseSkeleton(actor); - if (skeletonObj == NULL) { + if (skeletonObj == NULL) + { logger.Error("%s: Didn't find thing %s's base skeleton.nif for actor %08x \n", __func__, boneName.c_str(), actor->formID); return; } @@ -262,7 +286,7 @@ void Thing::UpdateThing(Actor *actor) { //ShowPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif NiMatrix43 targetRot = skeletonObj->m_localTransform.rot.Transpose(); - NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos[boneName.c_str()][actor->formID]) + obj->m_parent->m_worldTransform.pos; + NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos[boneName.c_str()][actor->formID]) + obj->m_parent->m_worldTransform.pos; // Offset to move Center of Mass make rotational motion more significant NiPoint3 target = (targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; @@ -295,13 +319,16 @@ void Thing::UpdateThing(Actor *actor) { ShowPos(diff); #endif - if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) { + if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) + { logger.Error("%s: bone %s transform reset for actor %x\n", __func__, boneName.c_str(), actor->formID); obj->m_localTransform.pos = origLocalPos[boneName.c_str()][actor->formID]; oldWorldPos = target; velocity = NiPoint3(0, 0, 0); time = clock(); - } else { + } + else + { diff *= timeTick / (float)deltaT; NiPoint3 posDelta = NiPoint3(0, 0, 0); @@ -317,7 +344,8 @@ void Thing::UpdateThing(Actor *actor) { ShowPos(force); #endif - do { + do + { // Assume mass is 1, so Accelleration is Force, can vary mass by changinf force //velocity = (velocity + (force * timeStep)) * (1 - (damping * timeStep)); velocity = (velocity + (force * timeStep)) - (velocity * (damping * timeStep)); @@ -355,9 +383,9 @@ void Thing::UpdateThing(Actor *actor) { // Convert the world translations into local coordinates NiMatrix43 rotateLinear; - rotateLinear.SetEulerAngles(rotateLinearX* DEG_TO_RAD, - rotateLinearY* DEG_TO_RAD, - rotateLinearZ* DEG_TO_RAD); + rotateLinear.SetEulerAngles(rotateLinearX * DEG_TO_RAD, + rotateLinearY * DEG_TO_RAD, + rotateLinearZ * DEG_TO_RAD); NiMatrix43 invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; @@ -395,11 +423,11 @@ void Thing::UpdateThing(Actor *actor) { #endif // scale positions from config NiPoint3 newLocalPos = NiPoint3((localDiff.x) + origLocalPos[boneName.c_str()][actor->formID].x, - (localDiff.y) + origLocalPos[boneName.c_str()][actor->formID].y, - (localDiff.z) + origLocalPos[boneName.c_str()][actor->formID].z + (localDiff.y) + origLocalPos[boneName.c_str()][actor->formID].y, + (localDiff.z) + origLocalPos[boneName.c_str()][actor->formID].z ); obj->m_localTransform.pos = newLocalPos; - + if (absRotX) rotDiff.x = fabs(rotDiff.x); rotDiff.x *= rotationalX; @@ -419,8 +447,8 @@ void Thing::UpdateThing(Actor *actor) { // Do rotation. NiMatrix43 rotateRotation; rotateRotation.SetEulerAngles(rotateRotationX * DEG_TO_RAD, - rotateRotationY * DEG_TO_RAD, - rotateRotationZ * DEG_TO_RAD); + rotateRotationY * DEG_TO_RAD, + rotateRotationZ * DEG_TO_RAD); NiMatrix43 standardRot; diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 0be1864..0ec1c80 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -8,7 +8,8 @@ typedef std::unordered_map> pos_map; typedef std::unordered_map> rot_map; -class Thing { +class Thing +{ BSFixedString boneName; NiPoint3 oldWorldPos; float oldRotZ; @@ -58,14 +59,14 @@ class Thing { static pos_map origLocalPos; static rot_map origLocalRot; - Thing(NiAVObject *obj, BSFixedString &name); + Thing(NiAVObject* obj, BSFixedString& name); ~Thing(); NiAVObject* IsThingActorValid(Actor* actor); void Reset(Actor* actor); - void UpdateThing(Actor* actor); + void UpdateThing(Actor* actor); void UpdateConfig(configEntry_t& centry); - + void ShowPos(NiPoint3& p); void ShowRot(NiMatrix43& r); }; \ No newline at end of file diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index ac58ad0..83fc5c4 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -40,12 +40,15 @@ UInt32 GetFormIDFromString(std::string const& configString) size_t colonPos = configString.find_first_of(":"); auto pluginFormID = configString.substr(0, colonPos); std::string pluginName = ""; - if (colonPos != std::string::npos && colonPos < configString.length() - 1) { + if (colonPos != std::string::npos && colonPos < configString.length() - 1) + { pluginName = configString.substr(colonPos + 1); } - for (auto digit : pluginFormID) { - if (!std::isxdigit(digit)) { + for (auto digit : pluginFormID) + { + if (!std::isxdigit(digit)) + { logger.Error("Invalid FormID %s, invalid hex character %c\n", pluginFormID.c_str(), digit); return -1; } @@ -53,11 +56,13 @@ UInt32 GetFormIDFromString(std::string const& configString) UInt32 formID = std::stoul(pluginFormID, nullptr, 16); - if (!pluginName.empty()) { + if (!pluginName.empty()) + { auto dataHandler = *g_dataHandler; auto modInfo = dataHandler->LookupLoadedModByName(pluginName.c_str()); - if (!modInfo) { + if (!modInfo) + { logger.Error("Plugin with name %s does not exist\n", pluginName.c_str()); return -1; } @@ -65,7 +70,8 @@ UInt32 GetFormIDFromString(std::string const& configString) bool isLightPlugin = modInfo->recordFlags & modInfo->kRecordFlags_ESL; size_t maxPartialIdLength = isLightPlugin ? 3 : 6; - if (pluginFormID.length() > maxPartialIdLength) { + if (pluginFormID.length() > maxPartialIdLength) + { logger.Error("Invalid FormID %s, too many characters when%s plugin name specified\n", pluginFormID.c_str(), isLightPlugin ? " light" : ""); return -1; } @@ -73,8 +79,10 @@ UInt32 GetFormIDFromString(std::string const& configString) UInt32 loadIndex = isLightPlugin ? dataHandler->GetLoadedLightModIndex(pluginName.c_str()) : dataHandler->GetLoadedModIndex(pluginName.c_str()); formID |= loadIndex << 24; } - else { - if (pluginFormID.length() > 8) { + else + { + if (pluginFormID.length() > 8) + { logger.Error("Invalid FormID %s, too many characters\n", pluginFormID.c_str()); return -1; } @@ -83,7 +91,8 @@ UInt32 GetFormIDFromString(std::string const& configString) return formID; } -bool LoadConfig() { +bool LoadConfig() +{ logger.Info("loadConfig\n"); config_t configOverrides; @@ -106,30 +115,33 @@ bool LoadConfig() { // Note: Using INIReader results in a slight double read INIReader configReader("Data\\F4SE\\Plugins\\ocbp.ini"); - if (configReader.ParseError() < 0) { + if (configReader.ParseError() < 0) + { logger.Error("Can't load 'ocbp.ini'\n"); } logger.Error("Reading CBP Config\n"); // Read general settings playerOnly = configReader.GetBoolean("General", "playerOnly", false); - npcOnly = configReader.GetBoolean("General", "npcOnly", false); + npcOnly = configReader.GetBoolean("General", "npcOnly", false); useWhitelist = configReader.GetBoolean("General", "useWhitelist", false); - if (useWhitelist) { + if (useWhitelist) + { maleOnly = false; femaleOnly = false; } - else { + else + { femaleOnly = configReader.GetBoolean("General", "femaleOnly", false); maleOnly = configReader.GetBoolean("General", "maleOnly", false); } reloadActors = (playerOnly ^ playerOnlyOld) || - (femaleOnly ^ femaleOnlyOld) || - (maleOnly ^ maleOnlyOld) || - (npcOnly ^ npcOnlyOld) || - (useWhitelist ^ useWhitelistOld); + (femaleOnly ^ femaleOnlyOld) || + (maleOnly ^ maleOnlyOld) || + (npcOnly ^ npcOnlyOld) || + (useWhitelist ^ useWhitelistOld); configReloadCount = configReader.GetInteger("Tuning", "rate", 0); @@ -139,7 +151,8 @@ bool LoadConfig() { bool detectArmorCompat = configReader.GetBoolean("General", "detectArmor", false) && (prioritySection == sections.end() || configReader.Section(*prioritySection).empty()); // Backwards compatibility for detectArmor style method - if (detectArmorCompat) { + if (detectArmorCompat) + { priorityNameMappings["A"] = 0; configArmorOverrideMap[0].slots.emplace(11); usedSlots.emplace(11); @@ -149,11 +162,13 @@ bool LoadConfig() { auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); { size_t commaPos; - do { + do + { commaPos = armorIgnoreStr.find_first_of(","); auto token = armorIgnoreStr.substr(0, commaPos); - try { + try + { UInt32 formID = std::stoul(token); configArmorOverrideMap[0].armors.emplace(formID); } @@ -164,7 +179,8 @@ bool LoadConfig() { } } - for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) { + for (auto sectionsIter = sections.begin(); sectionsIter != sections.end(); ++sectionsIter) + { // Split for override section check auto overrideStr = std::string("Override:"); @@ -179,37 +195,46 @@ bool LoadConfig() { auto armorStr = std::string("Armor."); auto splitArmorStr = std::mismatch(armorStr.begin(), armorStr.end(), sectionsIter->begin()); - if (*sectionsIter == std::string("Attach")) { + if (*sectionsIter == std::string("Attach")) + { // Get section contents auto sectionMap = configReader.Section(*sectionsIter); - for (auto& valuesIter : sectionMap) { + for (auto& valuesIter : sectionMap) + { auto& boneName = valuesIter.first; auto& attachName = valuesIter.second; boneNames.push_back(boneName); // Find specified bone section and insert map values into config - if (sections.find(attachName) != sections.end()) { + if (sections.find(attachName) != sections.end()) + { auto attachMapSection = configReader.Section(attachName); - for (auto& attachIter : attachMapSection) { + for (auto& attachIter : attachMapSection) + { auto& keyName = attachIter.first; config[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); } } } } - else if (splitSubAttachStr.first == subAttachStr.end()) { + else if (splitSubAttachStr.first == subAttachStr.end()) + { auto attachSubname = std::string(splitSubAttachStr.second, sectionsIter->end()); UInt32 attachPriority; auto mapEntry = priorityNameMappings.find(attachSubname); - if (mapEntry != priorityNameMappings.end()) { + if (mapEntry != priorityNameMappings.end()) + { attachPriority = mapEntry->second; } - else { + else + { std::string priorityMapping = configReader.Get("Priority", attachSubname, ""); - try { + try + { attachPriority = std::stoul(priorityMapping); } - catch (const std::exception&) { + catch (const std::exception&) + { continue; } @@ -217,38 +242,47 @@ bool LoadConfig() { } auto sectionMap = configReader.Section(*sectionsIter); - for (auto& valuesIter : sectionMap) { + for (auto& valuesIter : sectionMap) + { auto& boneName = valuesIter.first; auto& attachName = valuesIter.second; boneNames.push_back(boneName); // Find specified bone section and insert map values into config_t in configArmorOverrideMap at attachPriority - if (attachName.empty()) { + if (attachName.empty()) + { // "Touch" the map to add empty entry for bone in config_t to signal deletion later when building config from overrides configArmorOverrideMap[attachPriority].config[boneName]; - } - else if (sections.find(attachName) != sections.end()) { + } + else if (sections.find(attachName) != sections.end()) + { auto attachMapSection = configReader.Section(attachName); - for (auto& attachIter : attachMapSection) { + for (auto& attachIter : attachMapSection) + { auto& keyName = attachIter.first; configArmorOverrideMap[attachPriority].config[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); } } } } - else if (splitArmorStr.first == armorStr.end()) { + else if (splitArmorStr.first == armorStr.end()) + { auto armorSubname = std::string(splitArmorStr.second, sectionsIter->end()); UInt32 armorPriority; auto mapEntry = priorityNameMappings.find(armorSubname); - if (mapEntry != priorityNameMappings.end()) { + if (mapEntry != priorityNameMappings.end()) + { armorPriority = mapEntry->second; } - else { + else + { std::string priorityMapping = configReader.Get("Priority", armorSubname, ""); - try { + try + { armorPriority = std::stoul(priorityMapping); } - catch (const std::exception&) { + catch (const std::exception&) + { continue; } @@ -256,21 +290,25 @@ bool LoadConfig() { } std::string slots = configReader.Get(*sectionsIter, "slots", ""); - if (slots.empty()) { + if (slots.empty()) + { continue; } size_t commaPos; - do { + do + { commaPos = slots.find_first_of(","); auto token = slots.substr(0, commaPos); slots = slots.substr(commaPos + 1); UInt32 slot; - try { + try + { slot = std::stoul(token); } - catch (const std::exception&) { + catch (const std::exception&) + { continue; } @@ -282,48 +320,57 @@ bool LoadConfig() { // Get section contents auto sectionMap = configReader.Section(*sectionsIter); - for (auto& valuesIter : sectionMap) { + for (auto& valuesIter : sectionMap) + { auto& key = valuesIter.first; - if (key == "invertFilter" || key == "slots") { + if (key == "invertFilter" || key == "slots") + { continue; } auto formID = GetFormIDFromString(valuesIter.second); - if (formID == -1) { + if (formID == -1) + { continue; } configArmorOverrideMap[armorPriority].armors.emplace(formID); } } - else if (*sectionsIter == std::string("Whitelist") && useWhitelist) { + else if (*sectionsIter == std::string("Whitelist") && useWhitelist) + { whitelist.clear(); raceWhitelist.clear(); // Get section contents auto sectionMap = configReader.Section(*sectionsIter); - for (auto& valuesIter : sectionMap) { - auto& boneName = valuesIter.first; + for (auto& valuesIter : sectionMap) + { + auto& boneName = valuesIter.first; auto& whitelistName = valuesIter.second; size_t commaPos; - do { + do + { commaPos = whitelistName.find_first_of(","); auto token = whitelistName.substr(0, commaPos); size_t colonPos = token.find_last_of(":"); auto raceName = token.substr(0, colonPos); auto genderStr = token.substr(colonPos + 1); - if (colonPos == -1) { + if (colonPos == -1) + { whitelist[boneName][token].male = true; whitelist[boneName][token].female = true; raceWhitelist.push_back(token); } - else if (genderStr == "male") { + else if (genderStr == "male") + { whitelist[boneName][raceName].male = true; raceWhitelist.push_back(raceName); } - else if (genderStr == "female") { + else if (genderStr == "female") + { whitelist[boneName][raceName].female = true; raceWhitelist.push_back(raceName); } @@ -333,17 +380,20 @@ bool LoadConfig() { } while (commaPos != -1); } } - else if (splitOverrideStr.first == overrideStr.end()) { + else if (splitOverrideStr.first == overrideStr.end()) + { // If section name is prefixed with "Override:", grab other half of name for bone auto boneName = std::string(splitOverrideStr.second, sectionsIter->end()); // Get section contents - auto sectionMap = configReader.Section(*sectionsIter); - for (auto &valuesIt : sectionMap) { + auto sectionMap = configReader.Section(*sectionsIter); + for (auto& valuesIt : sectionMap) + { configOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); } } - else if (splitSubOverrideStr.first == subOverrideStr.end()) { + else if (splitSubOverrideStr.first == subOverrideStr.end()) + { // If section name is prefixed with "Override.", grab other half for priority and name of bone auto overrideSection = std::string(splitSubOverrideStr.second, sectionsIter->end()); @@ -353,15 +403,19 @@ bool LoadConfig() { UInt32 overridePriority; auto mapEntry = priorityNameMappings.find(overrideSubname); - if (mapEntry != priorityNameMappings.end()) { + if (mapEntry != priorityNameMappings.end()) + { overridePriority = mapEntry->second; } - else { + else + { std::string priorityMapping = configReader.Get("Priority", overrideSubname, ""); - try { + try + { overridePriority = std::stoul(priorityMapping); } - catch (const std::exception&) { + catch (const std::exception&) + { continue; } @@ -370,26 +424,34 @@ bool LoadConfig() { // Get section contents auto sectionMap = configReader.Section(*sectionsIter); - for (auto& valuesIt : sectionMap) { + for (auto& valuesIt : sectionMap) + { configArmorBoneOverrides[overridePriority][boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); } } } // replace configs with override settings (if any) - for (auto &boneIter : configOverrides) { - if (config.count(boneIter.first) > 0) { - for (auto settingIter : boneIter.second) { + for (auto& boneIter : configOverrides) + { + if (config.count(boneIter.first) > 0) + { + for (auto settingIter : boneIter.second) + { config[boneIter.first][settingIter.first] = settingIter.second; } } } // replace armor configs with override settings (if any) - for (auto conf : configArmorBoneOverrides) { - for (auto& boneIter : conf.second) { - if (configArmorOverrideMap[conf.first].config.count(boneIter.first) > 0) { - for (auto settingIter : boneIter.second) { + for (auto conf : configArmorBoneOverrides) + { + for (auto& boneIter : conf.second) + { + if (configArmorOverrideMap[conf.first].config.count(boneIter.first) > 0) + { + for (auto settingIter : boneIter.second) + { configArmorOverrideMap[conf.first].config[boneIter.first][settingIter.first] = settingIter.second; } } @@ -401,9 +463,12 @@ bool LoadConfig() { boneNames.assign(bonesSet.begin(), bonesSet.end()); // "Delete" bones specified in [Attach] but not [Attach.A] from the latter for compatibility with presets that remove breast bone jiggle when chest armor equipped - if (detectArmorCompat) { - for (auto boneName : boneNames) { - if (configArmorOverrideMap[0].config.find(boneName) == configArmorOverrideMap[0].config.end()) { + if (detectArmorCompat) + { + for (auto boneName : boneNames) + { + if (configArmorOverrideMap[0].config.find(boneName) == configArmorOverrideMap[0].config.end()) + { configArmorOverrideMap[0].config[boneName]; } } @@ -417,40 +482,49 @@ void DumpConfigToLog() { // Log contents of config logger.Info("***** Config Dump *****\n"); - for (auto section : config) { + for (auto section : config) + { logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) { + for (auto setting : section.second) + { logger.Info("%s=%f\n", setting.first.c_str(), setting.second); } } logger.Info("***** ConfigArmorOverride Dump *****\n"); - for (auto conf : configArmorOverrideMap) + for (auto conf : configArmorOverrideMap) + { + logger.Info("** Slot-Armor Map priority %ul **\n", conf.first); + logger.Info("[Slots]\n"); + for (auto slot : conf.second.slots) { - logger.Info("** Slot-Armor Map priority %ul **\n", conf.first); - logger.Info("[Slots]\n"); - for (auto slot : conf.second.slots) { - logger.Info("%ul\n", slot); - } - logger.Info("[Armors]\n"); - for (auto formID : conf.second.armors) { - logger.Info("%ul\n", formID); - } - logger.Info("** Config priority %ul **\n", conf.first); - for (auto section : conf.second.config) { - logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) { - logger.Info("%s=%f\n", setting.first.c_str(), setting.second); - } + logger.Info("%ul\n", slot); + } + logger.Info("[Armors]\n"); + for (auto formID : conf.second.armors) + { + logger.Info("%ul\n", formID); + } + logger.Info("** Config priority %ul **\n", conf.first); + for (auto section : conf.second.config) + { + logger.Info("[%s]\n", section.first.c_str()); + for (auto setting : section.second) + { + logger.Info("%s=%f\n", setting.first.c_str(), setting.second); } } + } } -void DumpWhitelistToLog() { +void DumpWhitelistToLog() +{ logger.Info("***** Whitelist Dump *****\n"); - for (auto section : whitelist) { + for (auto section : whitelist) + { logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) { + for (auto setting : section.second) + { logger.Info("%s= female: %d, male: %d\n", setting.first.c_str(), setting.second.female, setting.second.male); } } diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 445eb77..85ccf1c 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -7,10 +7,12 @@ #include "f4se/GameReferences.h" -class Configuration { +class Configuration +{ }; -struct whitelistSex { +struct whitelistSex +{ bool male; bool female; }; @@ -19,7 +21,8 @@ typedef std::unordered_map configEntry_t; typedef std::unordered_map config_t; typedef std::unordered_map> whitelist_t; -struct armorOverrideData { +struct armorOverrideData +{ bool isFilterInverted; std::unordered_set slots; std::unordered_set armors; diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index 21a7a4e..56a2331 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -6,36 +6,42 @@ // TODO make better macro //#define LOG_ON -CbpLogger::CbpLogger(const char *fname) { +CbpLogger::CbpLogger(const char* fname) +{ #ifdef LOG_ON - handle = fopen(fname, "a"); - if (handle) { - fprintf(handle, "CBP Log initialized\n"); - } + handle = fopen(fname, "a"); + if (handle) + { + fprintf(handle, "CBP Log initialized\n"); + } #endif } -void CbpLogger::Info(const char *fmt...) { +void CbpLogger::Info(const char* fmt...) +{ #ifdef LOG_ON - if (handle) { - va_list argptr; - va_start(argptr, fmt); - vfprintf(handle, fmt, argptr); - va_end(argptr); - fflush(handle); - } + if (handle) + { + va_list argptr; + va_start(argptr, fmt); + vfprintf(handle, fmt, argptr); + va_end(argptr); + fflush(handle); + } #endif } -void CbpLogger::Error(const char *fmt...) { +void CbpLogger::Error(const char* fmt...) +{ #ifdef LOG_ON - if (handle) { - va_list argptr; - va_start(argptr, fmt); - vfprintf(handle, fmt, argptr); - va_end(argptr); - fflush(handle); - } + if (handle) + { + va_list argptr; + va_start(argptr, fmt); + vfprintf(handle, fmt, argptr); + va_end(argptr); + fflush(handle); + } #endif } diff --git a/CBPSSE/log.h b/CBPSSE/log.h index 9dabeda..b13e337 100644 --- a/CBPSSE/log.h +++ b/CBPSSE/log.h @@ -1,13 +1,14 @@ #pragma once #include -class CbpLogger { +class CbpLogger +{ public: CbpLogger(const char* fname); - void Info(const char* args...); + void Info(const char* args...); void Error(const char* args...); - FILE *handle; + FILE* handle; }; extern CbpLogger logger; \ No newline at end of file diff --git a/CBPSSE/main.cpp b/CBPSSE/main.cpp index 11d11fd..e3b7759 100644 --- a/CBPSSE/main.cpp +++ b/CBPSSE/main.cpp @@ -13,12 +13,12 @@ bool RegisterFuncs(VirtualMachine* vm); PluginHandle g_pluginHandle = kPluginHandle_Invalid; -F4SEMessagingInterface * g_messagingInterface = NULL; +F4SEMessagingInterface* g_messagingInterface = NULL; //F4SEScaleformInterface * g_scaleform = NULL; //F4SESerializationInterface * g_serialization = NULL; -F4SETaskInterface * g_task = nullptr; -F4SEPapyrusInterface * g_papyrus = nullptr; +F4SETaskInterface* g_task = nullptr; +F4SEPapyrusInterface* g_papyrus = nullptr; //IDebugLog gLog("Data\\F4SE\\Plugins\\hook.log"); @@ -26,71 +26,71 @@ void DoHook(); -void MessageHandler(F4SEMessagingInterface::Message * msg) +void MessageHandler(F4SEMessagingInterface::Message* msg) { switch (msg->type) { - case F4SEMessagingInterface::kMessage_GameDataReady: - { - logger.Info("kMessage_GameDataReady\n"); - // Load initial config - logger.Error("Loading Config"); - LoadConfig(); - logger.Error("Hooking Game"); - DoHook(); - logger.Error("CBP Load Complete\n"); - } - break; - case F4SEMessagingInterface::kMessage_GameLoaded: - { - logger.Info("kMessage_GameLoaded\n"); - } - break; - case F4SEMessagingInterface::kMessage_NewGame: - { - logger.Info("kMessage_NewGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PreLoadGame: - { - logger.Info("kMessage_PreLoadGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostLoad: - { - logger.Info("kMessage_PostLoad\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostPostLoad: - { - logger.Info("kMessage_PostPostLoad\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostLoadGame: - { - logger.Info("kMessage_PostLoadGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PreSaveGame: - { - logger.Info("kMessage_PreSaveGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_PostSaveGame: - { - logger.Info("kMessage_PostSaveGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_DeleteGame: - { - logger.Info("kMessage_DeleteGame\n"); - } - break; - case F4SEMessagingInterface::kMessage_InputLoaded: - { - logger.Info("kMessage_InputLoaded\n"); - } - break; + case F4SEMessagingInterface::kMessage_GameDataReady: + { + logger.Info("kMessage_GameDataReady\n"); + // Load initial config + logger.Error("Loading Config"); + LoadConfig(); + logger.Error("Hooking Game"); + DoHook(); + logger.Error("CBP Load Complete\n"); + } + break; + case F4SEMessagingInterface::kMessage_GameLoaded: + { + logger.Info("kMessage_GameLoaded\n"); + } + break; + case F4SEMessagingInterface::kMessage_NewGame: + { + logger.Info("kMessage_NewGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PreLoadGame: + { + logger.Info("kMessage_PreLoadGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostLoad: + { + logger.Info("kMessage_PostLoad\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostPostLoad: + { + logger.Info("kMessage_PostPostLoad\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostLoadGame: + { + logger.Info("kMessage_PostLoadGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PreSaveGame: + { + logger.Info("kMessage_PreSaveGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_PostSaveGame: + { + logger.Info("kMessage_PostSaveGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_DeleteGame: + { + logger.Info("kMessage_DeleteGame\n"); + } + break; + case F4SEMessagingInterface::kMessage_InputLoaded: + { + logger.Info("kMessage_InputLoaded\n"); + } + break; } } @@ -99,7 +99,7 @@ void MessageHandler(F4SEMessagingInterface::Message * msg) extern "C" { - bool F4SEPlugin_Query(const F4SEInterface * f4se, PluginInfo * info) + bool F4SEPlugin_Query(const F4SEInterface* f4se, PluginInfo* info) { logger.Info("OCBP Physics F4SE Plugin\n"); logger.Error("Query called\n"); @@ -135,11 +135,11 @@ extern "C" return true; } - bool F4SEPlugin_Load(const F4SEInterface * f4se) + bool F4SEPlugin_Load(const F4SEInterface* f4se) { logger.Error("CBP Loading\n"); - g_task = (F4SETaskInterface *)f4se->QueryInterface(kInterface_Task); + g_task = (F4SETaskInterface*)f4se->QueryInterface(kInterface_Task); if (!g_task) { logger.Error("Couldn't get Task interface\n"); @@ -148,16 +148,16 @@ extern "C" if (g_papyrus) g_papyrus->Register(RegisterFuncs); - + g_messagingInterface = (F4SEMessagingInterface*)f4se->QueryInterface(kInterface_Messaging); if (!g_messagingInterface) { logger.Error("Couldn't get messaging interface"); return false; } - - g_messagingInterface->RegisterListener(g_pluginHandle, "F4SE", MessageHandler); - + + g_messagingInterface->RegisterListener(g_pluginHandle, "F4SE", MessageHandler); + return true; } }; @@ -172,6 +172,7 @@ BOOL WINAPI DllMain( _In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved -) { +) +{ return true; } \ No newline at end of file diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 2ae3f11..1762f06 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -48,10 +48,11 @@ using actorUtils::IsActorValid; using actorUtils::BuildConfigForActor; using actorUtils::GetActorRaceEID; -extern F4SETaskInterface *g_task; +extern F4SETaskInterface* g_task; //void UpdateWorldDataToChild(NiAVObject) -void DumpTransform(NiTransform t) { +void DumpTransform(NiTransform t) +{ Console_Print("%8.2f %8.2f %8.2f", t.rot.data[0][0], t.rot.data[0][1], t.rot.data[0][2]); Console_Print("%8.2f %8.2f %8.2f", t.rot.data[1][0], t.rot.data[1][1], t.rot.data[1][2]); Console_Print("%8.2f %8.2f %8.2f", t.rot.data[2][0], t.rot.data[2][1], t.rot.data[2][2]); @@ -61,17 +62,21 @@ void DumpTransform(NiTransform t) { } -bool visitObjects(NiAVObject *parent, std::function functor, int depth = 0) { +bool visitObjects(NiAVObject* parent, std::function functor, int depth = 0) +{ if (!parent) return false; - NiNode * node = parent->GetAsNiNode(); - if (node) { + NiNode* node = parent->GetAsNiNode(); + if (node) + { if (functor(parent, depth)) return true; - for (UInt32 i = 0; i < node->m_children.m_emptyRunStart; i++) { - NiAVObject * object = node->m_children.m_data[i]; - if (object) { - if (visitObjects(object, functor, depth+1)) + for (UInt32 i = 0; i < node->m_children.m_emptyRunStart; i++) + { + NiAVObject* object = node->m_children.m_data[i]; + if (object) + { + if (visitObjects(object, functor, depth + 1)) return true; } } @@ -82,14 +87,16 @@ bool visitObjects(NiAVObject *parent, std::function fun return false; } -std::string spaces(int n) { - auto s = std::string(n , ' '); +std::string spaces(int n) +{ + auto s = std::string(n, ' '); return s; } -bool printStuff(NiAVObject *avObj, int depth) { +bool printStuff(NiAVObject* avObj, int depth) +{ std::string sss = spaces(depth); - const char *ss = sss.c_str(); + const char* ss = sss.c_str(); //logger.info("%savObj Name = %s, RTTI = %s\n", ss, avObj->m_name, avObj->GetRTTI()->name); //NiNode *node = avObj->GetAsNiNode(); @@ -100,8 +107,10 @@ bool printStuff(NiAVObject *avObj, int depth) { } template -inline void safe_delete(T*& in) { - if (in) { +inline void safe_delete(T*& in) +{ + if (in) + { delete in; in = NULL; } @@ -110,10 +119,11 @@ inline void safe_delete(T*& in) { std::unordered_map actors; // Map of Actor (stored as form ID) to its Simulation Object -TESObjectCELL *curCell = nullptr; +TESObjectCELL* curCell = nullptr; -void UpdateActors() { +void UpdateActors() +{ //LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; //LARGE_INTEGER frequency; @@ -131,34 +141,43 @@ void UpdateActors() { auto cell = player->parentCell; if (!cell) goto FAILED; - if (cell != curCell) { + if (cell != curCell) + { logger.Error("cell change %d\n", cell->formID); curCell = cell; actors.clear(); actorEntries.clear(); - } else { + } + else + { // Attempt to get cell's objects - for (int i = 0; i < cell->objectList.count; i++) { + for (int i = 0; i < cell->objectList.count; i++) + { auto ref = cell->objectList[i]; - if (ref) { + if (ref) + { // Attempt to get actors auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); - if (actor && actor->unkF0) { + if (actor && actor->unkF0) + { // Find if actors is already being tracked auto soIt = actors.find(actor->formID); - if (soIt == actors.end() && IsActorTrackable(actor)) { + if (soIt == actors.end() && IsActorTrackable(actor)) + { //logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", // actor->formID, actor->parentCell->formID, // actor->race->editorId.c_str(), // IsActorMale(actor)); // Make SimObj and place new element in Things auto obj = SimObj(actor, config); - if (IsActorValid(actor)) { + if (IsActorValid(actor)) + { actors.emplace(actor->formID, obj); actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } - else if (IsActorValid(actor)) { + else if (IsActorValid(actor)) + { actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } @@ -182,55 +201,67 @@ void UpdateActors() { // Reload config static int count = 0; - if (configReloadCount && count++ > configReloadCount) { + if (configReloadCount && count++ > configReloadCount) + { count = 0; auto reloadActors = LoadConfig(); - for (auto& a : actorEntries) { + for (auto& a : actorEntries) + { auto objIterator = actors.find(a.id); - if (objIterator == actors.end()) { + if (objIterator == actors.end()) + { //logger.error("Sim Object not found in tracked actors\n"); } - else { + else + { objIterator->second.AddBonesToThings(a.actor, boneNames); objIterator->second.UpdateConfig(config); } } // Clear actors - if (reloadActors) { + if (reloadActors) + { actors.clear(); actorEntries.clear(); } } //logger.error("Updating %d entities\n", actorEntries.size()); - for (auto &a : actorEntries) { + for (auto& a : actorEntries) + { auto objIterator = actors.find(a.id); - if (objIterator == actors.end()) { + if (objIterator == actors.end()) + { //logger.error("Sim Object not found in tracked actors\n"); } - else { + else + { config_t composedConfig = BuildConfigForActor(a.actor); - - auto &simObj = objIterator->second; + + auto& simObj = objIterator->second; SimObj::Gender gender = IsActorMale(a.actor) ? SimObj::Gender::Male : SimObj::Gender::Female; // Pointer comparison is good enough? // OR check if gender and/or race have changed - if (simObj.IsBound()) { + if (simObj.IsBound()) + { if (gender != simObj.GetGender() || - GetActorRaceEID(a.actor) != simObj.GetRaceEID()) { + GetActorRaceEID(a.actor) != simObj.GetRaceEID()) + { logger.Info("%s: Reset sim object\n", __func__); simObj.Reset(); } } - if (simObj.IsBound()) { + if (simObj.IsBound()) + { simObj.Update(a.actor); } - else { + else + { simObj.Bind(a.actor, boneNames, composedConfig); } } From 8851c959d20d3823ae0a592e9a77add1a37f0285 Mon Sep 17 00:00:00 2001 From: ericncream Date: Sun, 28 Aug 2022 05:37:33 -0700 Subject: [PATCH 51/66] Mixed cbp 1.5 updates and some refactoring. --- CBPSSE/ActorUtils.cpp | 65 ++++++++++++------- CBPSSE/ActorUtils.h | 1 - CBPSSE/SimObj.cpp | 19 +++--- CBPSSE/SimObj.h | 9 ++- CBPSSE/Thing.cpp | 141 ++++++++++++++++++++++-------------------- CBPSSE/Thing.h | 20 ++++++ CBPSSE/config.cpp | 5 ++ CBPSSE/config.h | 7 ++- CBPSSE/log.cpp | 2 +- CBPSSE/log.h | 3 + CBPSSE/scan.cpp | 114 ++++++++++++++++++---------------- 11 files changed, 226 insertions(+), 160 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index fa2b964..81bc15e 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -189,10 +189,14 @@ const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, config_t actorUtils::BuildConfigForActor(Actor* actor) { std::multiset key; + for (auto slot : usedSlots) { + // Check if actor has config's slot equipped UInt64 data = 0; auto equipped = actorUtils::GetActorEquippedArmor(actor, slot); + + // If there were any slots found, store it if (equipped.armor) { data |= equipped.armor->formID; @@ -202,71 +206,86 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { data |= equipped.model->formID; } - key.emplace(data); + if (data) + { + key.emplace(data); + } } + // Search cached configs for already existing config auto found = cachedConfigs.find(key); if (found != cachedConfigs.end()) { return found->second; } + // Otherwise, build the actor's config config_t baseConfig = config; for (auto overrideConfigIter = configArmorOverrideMap.rbegin(); overrideConfigIter != configArmorOverrideMap.rend(); ++overrideConfigIter) { - auto data = overrideConfigIter->second; + auto orData = overrideConfigIter->second; std::vector equippedList; - for (auto slot : data.slots) + + // Make a list of actor's slots that are config's slots + for (auto slot : orData.slots) { - auto equipped = actorUtils::GetActorEquippedArmor(actor, slot); + EquippedArmor equipped = actorUtils::GetActorEquippedArmor(actor, slot); if (equipped.armor && equipped.model) { equippedList.push_back(equipped); } } - auto armorFormID = data.armors.begin(); - for (; armorFormID != data.armors.end(); ++armorFormID) + + auto armorFormID = orData.armors.begin(); + // whitelist filter + if (orData.isFilterInverted) { - bool breakOutside = false; - for (auto equipped : equippedList) + for (; armorFormID != orData.armors.end(); ++armorFormID) { - if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) + bool breakOutside = false; + // Check config's filter IDs against found slot's IDs + for (auto equipped : equippedList) { - if (data.isFilterInverted) + if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) { - for (auto val : data.config) + for (auto val : orData.config) { - if (data.config[val.first].empty()) + // for each bone, if it is empty, we need to disable it, + // otherwise the configEntry is good. + if (orData.config[val.first].empty()) { - baseConfig.erase(val.first); + // This is ok because we're doing this to a premade copy sequentially + baseConfig.unsafe_erase(val.first); } else { baseConfig[val.first] = val.second; } } + + breakOutside = true; + break; } + } - breakOutside = true; + if (breakOutside) + { break; } } - - if (breakOutside) - { - break; - } } - if (!data.isFilterInverted && armorFormID == data.armors.end() && !equippedList.empty()) + // blacklist filter + if (!orData.isFilterInverted && armorFormID == orData.armors.end() && !equippedList.empty()) { - for (auto val : data.config) + for (auto val : orData.config) { - if (data.config[val.first].empty()) + if (orData.config[val.first].empty()) { - baseConfig.erase(val.first); + // This is ok because we're doing this to a premade copy sequentially + baseConfig.unsafe_erase(val.first); } else { diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index 58db4a0..51e776e 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -16,7 +16,6 @@ namespace actorUtils bool IsActorMale(Actor* actor); bool IsActorTrackable(Actor* actor); bool IsActorValid(Actor* actor); - bool IsBoneValid(Actor* actor, BSFixedString* boneName); bool IsBoneInWhitelist(Actor* actor, std::string boneName); const EquippedArmor GetActorEquippedArmor(Actor* actor, UInt32 slot); diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 45a0934..86ace90 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -15,7 +15,7 @@ using actorUtils::IsActorInPowerArmor; // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory std::vector boneNames; -SimObj::SimObj(Actor* actor, config_t& config) +SimObj::SimObj(Actor* actor) : things(4) { id = actor->formID; @@ -53,7 +53,7 @@ bool SimObj::AddBonesToThings(Actor* actor, std::vector& boneNames) else if (findBone == things.end()) { //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); - things.emplace(b, Thing(bone, cs)); + things.insert(std::make_pair(b, Thing(bone, cs))); } } } @@ -86,7 +86,7 @@ bool SimObj::Bind(Actor* actor, std::vector& boneNames, config_t& c raceEid = actorUtils::GetActorRaceEID(actor); AddBonesToThings(actor, boneNames); - UpdateConfig(config); + UpdateConfigs(config); return true; } return false; @@ -140,13 +140,14 @@ void SimObj::Update(Actor* actor) } } -bool SimObj::UpdateConfig(config_t& config) +bool SimObj::UpdateConfigs(config_t& config) { logger.Error("%s\n", __func__); - for (auto& thing : things) - { - //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); - thing.second.UpdateConfig(config[std::string(thing.first)]); - } + // TODO does this need parallelization? + concurrency::parallel_for_each(things.begin(), things.end(), [&](auto& thing) + { + //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); + thing.second.UpdateConfig(config[std::string(thing.first)]); + }); return true; } \ No newline at end of file diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index 7b0daa2..ba2bced 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -2,6 +2,9 @@ #include #include +#include "amp.h" +#include + #include "f4se/GameReferences.h" #include "Thing.h" #include "config.h" @@ -17,8 +20,8 @@ class SimObj Unassigned }; - std::unordered_map things; - SimObj(Actor* actor, config_t& config); + concurrency::concurrent_unordered_map things; + SimObj(Actor* actor); SimObj() {} ~SimObj(); bool AddBonesToThings(Actor* actor, std::vector& boneNames); @@ -27,7 +30,7 @@ class SimObj std::string GetRaceEID(); void Reset(); void Update(Actor* actor); - bool UpdateConfig(config_t& config); + bool UpdateConfigs(config_t& config); bool IsBound() { return bound; } private: UInt32 id = 0; diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index b00c3ce..3b10939 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -4,6 +4,10 @@ #include "f4se\NiNodes.h" #include +//## thing_map_lock +// Maps are sorted every edit time, so if it is parallel processing then a high probability of overloading +std::shared_mutex thing_map_lock; + constexpr float DEG_TO_RAD = 3.14159265 / 180; const char* skeletonNif_boneName = "skeleton.nif"; const char* COM_boneName = "COM"; @@ -32,7 +36,8 @@ void Thing::ShowRot(NiMatrix43& r) } Thing::Thing(NiAVObject* obj, BSFixedString& name) - : boneName(name) + : thingObj(obj) + , boneName(name) , velocity(NiPoint3(0, 0, 0)) { @@ -45,6 +50,65 @@ Thing::~Thing() { } +void Thing::StoreOriginalTransforms(Actor* actor) +{ + // Save the bones' original local values, per actor, if they already haven't + auto origLocalPos_iter = origLocalPos.find(boneName.c_str()); + auto origLocalRot_iter = origLocalRot.find(boneName.c_str()); + + auto obj = thingObj; + + // Original Bone Position + if (origLocalPos_iter == origLocalPos.end()) + { + origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; +#ifdef DEBUG + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun pos Set: \n"); + ShowPos(obj->m_localTransform.pos); +#endif + } + else + { + auto actorPosMap = origLocalPos.at(boneName.c_str()); + auto actor_iter = actorPosMap.find(actor->formID); + if (actor_iter == actorPosMap.end()) + { + origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; +#ifdef DEBUG + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun pos Set: \n"); + ShowPos(obj->m_localTransform.pos); +#endif + } + } + + // Original Bone Rotation + if (origLocalRot_iter == origLocalRot.end()) + { + origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; +#ifdef DEBUG + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun rot Set:\n"); + ShowRot(obj->m_localTransform.rot); +#endif + } + else + { + auto actorRotMap = origLocalRot.at(boneName.c_str()); + auto actor_iter = actorRotMap.find(actor->formID); + if (actor_iter == actorRotMap.end()) + { + origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; +#ifdef DEBUG + logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); + logger.Error("firstRun rot Set: \n"); + ShowRot(obj->m_localTransform.rot); +#endif + } + } +} + void Thing::UpdateConfig(configEntry_t& centry) { stiffness = centry["stiffness"]; @@ -167,7 +231,11 @@ void Thing::UpdateThing(Actor* actor) QueryPerformanceFrequency(&frequency); QueryPerformanceCounter(&startingTime);*/ - auto obj = IsThingActorValid(actor); + thingObj = IsThingActorValid(actor); + auto obj = thingObj; + + bool isSkippedmanyFrames = false; + if (!obj) { return; @@ -176,6 +244,8 @@ void Thing::UpdateThing(Actor* actor) auto newTime = clock(); auto deltaT = newTime - time; + isSkippedmanyFrames = deltaT >= 200; // Frame blank for more than 0.2 sec / 5 fps + time = newTime; if (deltaT > 64) deltaT = 64; if (deltaT < 8) deltaT = 8; @@ -206,56 +276,8 @@ void Thing::UpdateThing(Actor* actor) } #endif - // Save the bones' original local values if they already haven't - auto origLocalPos_iter = origLocalPos.find(boneName.c_str()); - auto origLocalRot_iter = origLocalRot.find(boneName.c_str()); + StoreOriginalTransforms(actor); - if (origLocalPos_iter == origLocalPos.end()) - { - origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; -#ifdef DEBUG - logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); - logger.Error("firstRun pos Set: \n"); - ShowPos(obj->m_localTransform.pos); -#endif - } - else - { - auto actorPosMap = origLocalPos.at(boneName.c_str()); - auto actor_iter = actorPosMap.find(actor->formID); - if (actor_iter == actorPosMap.end()) - { - origLocalPos[boneName.c_str()][actor->formID] = obj->m_localTransform.pos; -#ifdef DEBUG - logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); - logger.Error("firstRun pos Set: \n"); - ShowPos(obj->m_localTransform.pos); -#endif - } - } - if (origLocalRot_iter == origLocalRot.end()) - { - origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; -#ifdef DEBUG - logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); - logger.Error("firstRun rot Set:\n"); - ShowRot(obj->m_localTransform.rot); -#endif - } - else - { - auto actorRotMap = origLocalRot.at(boneName.c_str()); - auto actor_iter = actorRotMap.find(actor->formID); - if (actor_iter == actorRotMap.end()) - { - origLocalRot[boneName.c_str()][actor->formID] = obj->m_localTransform.rot; -#ifdef DEBUG - logger.Error("for bone %s, actor %08x: \n", boneName.c_str(), actor->formID); - logger.Error("firstRun rot Set: \n"); - ShowRot(obj->m_localTransform.rot); -#endif - } - } auto skeletonObj = actorUtils::GetBaseSkeleton(actor); if (skeletonObj == NULL) { @@ -263,23 +285,6 @@ void Thing::UpdateThing(Actor* actor) return; } - //auto skeletonObj = obj; - //bool skeletonFound = false; - //while (skeletonObj->m_parent) - //{ - // if (skeletonObj->m_parent->m_name == BSFixedString(skeletonNif_boneName)) { - // skeletonObj = skeletonObj->m_parent; - // skeletonFound = true; - // break; - // } - // skeletonObj = skeletonObj->m_parent; - //} - //if (skeletonFound == false) { - // logger.Error("%s: Couldn't find thing %s's skeleton.nif for actor %08x\n", __func__, boneName.c_str(), actor->formID); - // return; - //} - - #if DEBUG logger.Error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); ShowRot(skeletonObj->m_worldTransform.rot); diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 0ec1c80..7f961e3 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -8,6 +8,24 @@ typedef std::unordered_map> pos_map; typedef std::unordered_map> rot_map; +inline void RefreshNode(NiAVObject* node) +{ + if (node == nullptr || node->m_name == nullptr) + return; + + NiAVObject::NiUpdateData npd; + npd.unk00 = nullptr; + npd.pCamera = nullptr; + npd.flags = 0; + npd.unk14 = 0; + npd.unk18 = 0; + npd.unk20 = 0; + npd.unk28 = 0; + npd.unk30 = 0; + npd.unk38 = 0; + node->UpdateWorldData(&npd); +} + class Thing { BSFixedString boneName; @@ -15,6 +33,7 @@ class Thing float oldRotZ; NiPoint3 velocity; clock_t time; + NiAVObject* thingObj; public: float stiffness = 0.5f; @@ -64,6 +83,7 @@ class Thing NiAVObject* IsThingActorValid(Actor* actor); void Reset(Actor* actor); + void StoreOriginalTransforms(Actor* actor); void UpdateThing(Actor* actor); void UpdateConfig(configEntry_t& centry); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 83fc5c4..475f150 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -34,6 +34,7 @@ std::map, config_t> cachedConfigs; // TODO data structure these whitelist_t whitelist; std::vector raceWhitelist; +void DumpConfigToLog(); UInt32 GetFormIDFromString(std::string const& configString) { @@ -218,6 +219,7 @@ bool LoadConfig() } else if (splitSubAttachStr.first == subAttachStr.end()) { + // Get "XYZ" from Attach.XYZ auto attachSubname = std::string(splitSubAttachStr.second, sectionsIter->end()); UInt32 attachPriority; @@ -241,6 +243,7 @@ bool LoadConfig() priorityNameMappings[attachSubname] = attachPriority; } + // Finally read the "Attach." section auto sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { @@ -251,11 +254,13 @@ bool LoadConfig() if (attachName.empty()) { // "Touch" the map to add empty entry for bone in config_t to signal deletion later when building config from overrides + // This allows for disabling specific disabling attach configs configArmorOverrideMap[attachPriority].config[boneName]; } else if (sections.find(attachName) != sections.end()) { auto attachMapSection = configReader.Section(attachName); + // Find the bone's settings and add them to configArmorOverrideMap for (auto& attachIter : attachMapSection) { auto& keyName = attachIter.first; diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 85ccf1c..e9787c5 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -5,6 +5,9 @@ #include #include +#include +#include + #include "f4se/GameReferences.h" class Configuration @@ -17,8 +20,8 @@ struct whitelistSex bool female; }; -typedef std::unordered_map configEntry_t; -typedef std::unordered_map config_t; +typedef concurrency::concurrent_unordered_map configEntry_t; // Map settings for a particular bone +typedef concurrency::concurrent_unordered_map config_t; // Settings for a set of bones typedef std::unordered_map> whitelist_t; struct armorOverrideData diff --git a/CBPSSE/log.cpp b/CBPSSE/log.cpp index 56a2331..4459cdc 100644 --- a/CBPSSE/log.cpp +++ b/CBPSSE/log.cpp @@ -4,7 +4,7 @@ #pragma warning(disable : 4996) // TODO make better macro -//#define LOG_ON +#define LOG_ON CbpLogger::CbpLogger(const char* fname) { diff --git a/CBPSSE/log.h b/CBPSSE/log.h index b13e337..cd53611 100644 --- a/CBPSSE/log.h +++ b/CBPSSE/log.h @@ -1,5 +1,6 @@ #pragma once #include +#include class CbpLogger { @@ -9,6 +10,8 @@ class CbpLogger void Error(const char* args...); FILE* handle; + + std::shared_mutex log_lock; }; extern CbpLogger logger; \ No newline at end of file diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 1762f06..9e2aa62 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -22,6 +22,10 @@ #include #include +#include +#include +#include + #include "ActorEntry.h" #include "ActorUtils.h" #include "log.h" @@ -48,6 +52,8 @@ using actorUtils::IsActorValid; using actorUtils::BuildConfigForActor; using actorUtils::GetActorRaceEID; +std::atomic currCell = nullptr; + extern F4SETaskInterface* g_task; //void UpdateWorldDataToChild(NiAVObject) @@ -118,21 +124,15 @@ inline void safe_delete(T*& in) -std::unordered_map actors; // Map of Actor (stored as form ID) to its Simulation Object +concurrency::concurrent_unordered_map actors; // Map of Actor (stored as form ID) to its Simulation Object TESObjectCELL* curCell = nullptr; void UpdateActors() { - //LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; - //LARGE_INTEGER frequency; - - //QueryPerformanceFrequency(&frequency); - //QueryPerformanceCounter(&startingTime); - // We scan the cell and build the list every time - only look up things by ID once // we retain all state by actor ID, in a map - it's cleared on cell change - std::vector actorEntries; + concurrency::concurrent_vector actorEntries; //logger.error("scan Cell\n"); auto player = DYNAMIC_CAST(LookupFormByID(0x14), TESForm, Actor); @@ -151,38 +151,38 @@ void UpdateActors() else { // Attempt to get cell's objects - for (int i = 0; i < cell->objectList.count; i++) - { - auto ref = cell->objectList[i]; - if (ref) + concurrency::parallel_for(UInt32(0), cell->objectList.count, [&](UInt32 i) { - // Attempt to get actors - auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); - if (actor && actor->unkF0) + auto ref = cell->objectList[i]; + if (ref) { - // Find if actors is already being tracked - auto soIt = actors.find(actor->formID); - if (soIt == actors.end() && IsActorTrackable(actor)) + // Attempt to get actors + auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); + if (actor && actor->unkF0) { - //logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", - // actor->formID, actor->parentCell->formID, - // actor->race->editorId.c_str(), - // IsActorMale(actor)); - // Make SimObj and place new element in Things - auto obj = SimObj(actor, config); - if (IsActorValid(actor)) + // Find if actors is already being tracked + auto soIt = actors.find(actor->formID); + if (soIt == actors.end() && IsActorTrackable(actor)) { - actors.emplace(actor->formID, obj); - actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); + //logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", + // actor->formID, actor->parentCell->formID, + // actor->race->editorId.c_str(), + // IsActorMale(actor)); + // Make SimObj and place new element in Things + auto obj = SimObj(actor); + if (IsActorValid(actor)) + { + actors.insert(std::make_pair(actor->formID, obj)); + actorEntries.push_back(ActorEntry{ actor->formID, actor }); + } + } + else if (IsActorValid(actor)) + { + actorEntries.push_back(ActorEntry{ actor->formID, actor }); } - } - else if (IsActorValid(actor)) - { - actorEntries.emplace_back(ActorEntry{ actor->formID, actor }); } } - } - } + }); } //static bool done = false; @@ -205,17 +205,18 @@ void UpdateActors() { count = 0; auto reloadActors = LoadConfig(); - for (auto& a : actorEntries) + for each (auto & a in actorEntries) { - auto objIterator = actors.find(a.id); - if (objIterator == actors.end()) + auto actorsIterator = actors.find(a.id); + if (actorsIterator == actors.end()) { //logger.error("Sim Object not found in tracked actors\n"); } else { - objIterator->second.AddBonesToThings(a.actor, boneNames); - objIterator->second.UpdateConfig(config); + auto& simObj = actorsIterator->second; + simObj.AddBonesToThings(a.actor, boneNames); + simObj.UpdateConfigs(config); } } @@ -230,17 +231,16 @@ void UpdateActors() //logger.error("Updating %d entities\n", actorEntries.size()); for (auto& a : actorEntries) { - auto objIterator = actors.find(a.id); - if (objIterator == actors.end()) + auto actorsIterator = actors.find(a.id); + if (actorsIterator == actors.end()) { //logger.error("Sim Object not found in tracked actors\n"); } else { - config_t composedConfig = BuildConfigForActor(a.actor); - - auto& simObj = objIterator->second; + auto& simObj = actorsIterator->second; + auto& composedConfig = BuildConfigForActor(a.actor); SimObj::Gender gender = IsActorMale(a.actor) ? SimObj::Gender::Male : SimObj::Gender::Female; @@ -255,11 +255,6 @@ void UpdateActors() simObj.Reset(); } } - - if (simObj.IsBound()) - { - simObj.Update(a.actor); - } else { simObj.Bind(a.actor, boneNames, composedConfig); @@ -267,13 +262,26 @@ void UpdateActors() } } + concurrency::parallel_for_each(actorEntries.begin(), actorEntries.end(), [&](const auto& a) + { + auto actorsIterator = actors.find(a.id); + if (actorsIterator == actors.end()) + { + //logger.error("Sim Object not found in tracked actors\n"); + } + else + { + auto& simObj = actorsIterator->second; + + if (simObj.IsBound()) + { + simObj.Update(a.actor); + } + } + }); + FAILED: return; - //QueryPerformanceCounter(&endingTime); - //elapsedMicroseconds.QuadPart = endingTime.QuadPart - startingTime.QuadPart; - //elapsedMicroseconds.QuadPart *= 1000000000LL; - //elapsedMicroseconds.QuadPart /= frequency.QuadPart; - //logger.info("Update Time = %lld ns\n", elapsedMicroseconds.QuadPart); } From 7e0b7651a0b37afd3746a55a588d51c756bc4e56 Mon Sep 17 00:00:00 2001 From: ericncream Date: Mon, 12 Sep 2022 02:46:06 -0700 Subject: [PATCH 52/66] Update projects to Visual Studio 2019. --- CBPSSE/CBPSSE.vcxproj | 8 ++++---- common/common_vc14.vcxproj | 4 ++-- f4se/f4se/f4se.vcxproj | 4 ++-- f4se/f4se_common/f4se_common.vcxproj | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index f01bbb7..7d569aa 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -22,7 +22,7 @@ 15.0 {49438A48-1C92-4D07-97C0-BDF0744386F9} CBBPSSE - 10.0.18362.0 + 10.0 OpenCBP_FO4 @@ -48,7 +48,7 @@ DynamicLibrary false - v141 + v142 true MultiByte @@ -78,7 +78,7 @@ .dll cbp false - $(ProjectDir)x64_v141\Release\;$(OutDir);$(LibraryPath) + $(OutDir);$(LibraryPath) $(IncludePath) @@ -143,7 +143,7 @@ Windows true %(AdditionalLibraryDirectories) - $(OutDir)f4se_1_10_163.lib;$(OutDir)f4se_common.lib;$(SolutionDir)x64_v141\Release\common_vc14.lib;%(AdditionalDependencies) + $(OutDir)f4se_1_10_163.lib;$(OutDir)f4se_common.lib;%(AdditionalDependencies) copy "$(TargetPath)" "$(Fallout4Path)\$(TargetFileName)" diff --git a/common/common_vc14.vcxproj b/common/common_vc14.vcxproj index 9b0dad9..bcb786f 100644 --- a/common/common_vc14.vcxproj +++ b/common/common_vc14.vcxproj @@ -46,7 +46,7 @@ {472E19AB-DEF0-42DF-819B-18722E8DC822} Win32Proj common_vc14 - 10.0.18362.0 + 10.0 @@ -58,7 +58,7 @@ StaticLibrary false - v141 + v142 true MultiByte diff --git a/f4se/f4se/f4se.vcxproj b/f4se/f4se/f4se.vcxproj index 56560a3..f7d0457 100644 --- a/f4se/f4se/f4se.vcxproj +++ b/f4se/f4se/f4se.vcxproj @@ -261,7 +261,7 @@ - 10.0.18362.0 + 10.0 @@ -273,7 +273,7 @@ StaticLibrary false - v141 + v142 true MultiByte diff --git a/f4se/f4se_common/f4se_common.vcxproj b/f4se/f4se_common/f4se_common.vcxproj index 867e08a..2ff9122 100644 --- a/f4se/f4se_common/f4se_common.vcxproj +++ b/f4se/f4se_common/f4se_common.vcxproj @@ -43,7 +43,7 @@ - 10.0.18362.0 + 10.0 @@ -55,7 +55,7 @@ StaticLibrary false - v141 + v142 true MultiByte From e8cf4699d3e3f00ce69af7ba86fe36a7168c80b8 Mon Sep 17 00:00:00 2001 From: ericncream Date: Thu, 29 Sep 2022 04:01:29 -0700 Subject: [PATCH 53/66] More accurate motion and updates to gravity motion. Cleanup todo. --- CBPSSE/Thing.cpp | 472 ++++++++++++++++++++++++++++++++++------------- CBPSSE/Thing.h | 34 +++- 2 files changed, 380 insertions(+), 126 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 3b10939..3d93e97 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -4,24 +4,21 @@ #include "f4se\NiNodes.h" #include -//## thing_map_lock -// Maps are sorted every edit time, so if it is parallel processing then a high probability of overloading -std::shared_mutex thing_map_lock; - constexpr float DEG_TO_RAD = 3.14159265 / 180; const char* skeletonNif_boneName = "skeleton.nif"; const char* COM_boneName = "COM"; +const float DIFF_LIMIT = 100.0; // TODO Make these logger macros //#define DEBUG 1 //#define TRANSFORM_DEBUG 1 // > -//pos_map origLocalPos; -//rot_map origLocalRot; - pos_map Thing::origLocalPos; rot_map Thing::origLocalRot; +rot_map Thing::origChestWorldRot; + +shared_mutex Thing::thing_map_lock; void Thing::ShowPos(NiPoint3& p) { @@ -43,7 +40,17 @@ Thing::Thing(NiAVObject* obj, BSFixedString& name) // Set initial positions oldWorldPos = obj->m_worldTransform.pos; + + //logger.Error("obj->m_worldTransform.rot.Transpose():"); + //ShowRot(obj->m_worldTransform.rot.Transpose()); + //logger.Error("obj->m_localTransform.rot:"); + //ShowRot(obj->m_localTransform.rot); + origWorldRot = obj->m_localTransform.rot.Transpose() * obj->m_worldTransform.rot; + time = clock(); + + IsBreastBone = ContainsNoCase(std::string(boneName.c_str()), "Breast"); + firstSkeleton = true; } Thing::~Thing() @@ -58,6 +65,30 @@ void Thing::StoreOriginalTransforms(Actor* actor) auto obj = thingObj; + thing_map_lock.lock(); + if (IsBreastBone) + { + BSFixedString chest_name("Chest"); + NiAVObject* chestObj = actor->unkF0->rootNode->GetObjectByName(&chest_name); + + if (chestObj && chestObj->m_parent) + { + if (origLocalRot_iter == origChestWorldRot.end()) + { + origChestWorldRot[boneName.c_str()][actor->formID] = chestObj->m_parent->m_worldTransform.rot * chestObj->m_localTransform.rot; + } + else + { + auto actorRotMap = origChestWorldRot.at(boneName.c_str()); + auto actor_iter = actorRotMap.find(actor->formID); + if (actor_iter == actorRotMap.end()) + { + origChestWorldRot[boneName.c_str()][actor->formID] = chestObj->m_parent->m_worldTransform.rot * chestObj->m_localTransform.rot; + } + } + } + } + // Original Bone Position if (origLocalPos_iter == origLocalPos.end()) { @@ -107,6 +138,7 @@ void Thing::StoreOriginalTransforms(Actor* actor) #endif } } + thing_map_lock.unlock(); } void Thing::UpdateConfig(configEntry_t& centry) @@ -167,6 +199,11 @@ static float clamp(float val, float min, float max) return val; } +static float remap(float value, float start1, float stop1, float start2, float stop2) +{ + return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)); +} + void Thing::Reset(Actor* actor) { auto loadedState = actor->unkF0; @@ -225,17 +262,11 @@ NiAVObject* Thing::IsThingActorValid(Actor* actor) void Thing::UpdateThing(Actor* actor) { - /*LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; - LARGE_INTEGER frequency; - - QueryPerformanceFrequency(&frequency); - QueryPerformanceCounter(&startingTime);*/ + auto forceMultiplier = 1.0; thingObj = IsThingActorValid(actor); auto obj = thingObj; - bool isSkippedmanyFrames = false; - if (!obj) { return; @@ -244,8 +275,6 @@ void Thing::UpdateThing(Actor* actor) auto newTime = clock(); auto deltaT = newTime - time; - isSkippedmanyFrames = deltaT >= 200; // Frame blank for more than 0.2 sec / 5 fps - time = newTime; if (deltaT > 64) deltaT = 64; if (deltaT < 8) deltaT = 8; @@ -285,182 +314,377 @@ void Thing::UpdateThing(Actor* actor) return; } + if (firstSkeleton) + { + firstSkeleton = false; + firstSkeletonPos = skeletonObj->m_worldTransform.rot * skeletonObj->m_worldTransform.pos; + firstWorldPos = skeletonObj->m_worldTransform.rot * obj->m_worldTransform.pos; + + } + + BSFixedString com_str(COM_boneName); + + NiAVObject* comObj = actor->unkF0->rootNode->GetObjectByName(&com_str); + + bool rightside = (firstWorldPos.x - firstSkeletonPos.x) >= 0.0; + #if DEBUG logger.Error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); ShowRot(skeletonObj->m_worldTransform.rot); //ShowPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif - NiMatrix43 targetRot = skeletonObj->m_localTransform.rot.Transpose(); + + //if (isSkippedmanyFrames) //prevents many bounce when fps gaps + //{ + // oldWorldPos = obj->m_parent->m_worldTransform.pos + obj->m_parent->m_worldTransform.rot.Transpose() * (oldLocalDiff + origLocalPos[boneName.c_str()][actor->formID]); + // return; + //} + + NiMatrix43 skelSpaceInvTransform = skeletonObj->m_localTransform.rot.Transpose(); NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos[boneName.c_str()][actor->formID]) + obj->m_parent->m_worldTransform.pos; + NiPoint3 origWorldPosRot = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos[boneName.c_str()][actor->formID]) + obj->m_parent->m_worldTransform.pos; + //float nodeParentInvScale = 1.0f / obj->m_parent->m_worldTransform.scale; - // Offset to move Center of Mass make rotational motion more significant - NiPoint3 target = (targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; + // Cog offset is offset to move Center of Mass make rotational motion more significant + // First transform the cog offset (which is in skeleton space) to world space + // target is in world space + NiPoint3 target = /*(skelSpaceInvTransform * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) +*/ origWorldPos; #if DEBUG logger.Error("World Position: "); ShowPos(obj->m_worldTransform.pos); //logger.Error("Parent World Position difference: "); //ShowPos(obj->m_worldTransform.pos - obj->m_parent->m_worldTransform.pos); - logger.Error("Target Rotation * cogOffsetY %8.4f: ", cogOffsetY); - ShowPos(targetRot * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)); + ShowPos(skelSpaceInvTransform * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)); //logger.Error("Target Rotation:\n"); - //ShowRot(targetRot); + //ShowRot(skelSpaceInvTransform); logger.Error("cogOffset x Transformation:"); - ShowPos(targetRot * NiPoint3(cogOffsetX, 0, 0)); + ShowPos(skelSpaceInvTransform * NiPoint3(cogOffsetX, 0, 0)); logger.Error("cogOffset y Transformation:"); - ShowPos(targetRot * NiPoint3(0, cogOffsetY, 0)); + ShowPos(skelSpaceInvTransform * NiPoint3(0, cogOffsetY, 0)); logger.Error("cogOffset z Transformation:"); - ShowPos(targetRot * NiPoint3(0, 0, cogOffsetZ)); + ShowPos(skelSpaceInvTransform * NiPoint3(0, 0, cogOffsetZ)); #endif - // diff is Difference in position between old and new world position - NiPoint3 diff = target - oldWorldPos; + auto gravityInvertedCorrectionStart = 0.25; + auto gravityInvertedCorrectionEnd = 0.75; + auto gravityInvertedCorrection = -4.0; - // Move up in for gravity correction - diff += targetRot * NiPoint3(0, 0, gravityCorrection); + // diff is Difference in position between old and new world position + // diff is world space + NiPoint3 diff = (target - oldWorldPos) * forceMultiplier; + NiPoint3 diffRot = (target - oldWorldPosRot) * forceMultiplier; #if DEBUG - logger.Error("Diff after gravity correction %f: ", gravityCorrection); + logger.Error("Diff after gravity correction %f: ", varGravityCorrection); ShowPos(diff); #endif - if (fabs(diff.x) > 100 || fabs(diff.y) > 100 || fabs(diff.z) > 100) + if (fabs(diff.x) > DIFF_LIMIT || fabs(diff.y) > DIFF_LIMIT || fabs(diff.z) > DIFF_LIMIT) { logger.Error("%s: bone %s transform reset for actor %x\n", __func__, boneName.c_str(), actor->formID); obj->m_localTransform.pos = origLocalPos[boneName.c_str()][actor->formID]; + obj->m_localTransform.rot = origLocalRot[boneName.c_str()][actor->formID]; + oldWorldPos = target; + oldWorldPosRot = origWorldPos; + velocity = NiPoint3(0, 0, 0); time = clock(); + return; } - else - { - diff *= timeTick / (float)deltaT; - NiPoint3 posDelta = NiPoint3(0, 0, 0); + auto newRotation = obj->m_parent->m_worldTransform.rot * origWorldRot.Transpose(); + + // move the bones based on the supplied weightings + // Convert the world translations into local coordinates - // Compute the "Spring" Force - NiPoint3 diff2(diff.x * diff.x * sgn(diff.x), diff.y * diff.y * sgn(diff.y), diff.z * diff.z * sgn(diff.z)); - NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - (targetRot * NiPoint3(0, 0, gravityBias)); + NiMatrix43 rotateLinear; + rotateLinear.SetEulerAngles(rotateLinearX * DEG_TO_RAD, + rotateLinearY * DEG_TO_RAD, + rotateLinearZ * DEG_TO_RAD); + + auto timeMultiplier = timeTick / (float)deltaT; + + // Transform to local space + //diff = obj->m_parent->m_worldTransform.rot * (diff * timeMultiplier); + diff = (diff * timeMultiplier); + + NiPoint3 posDelta = NiPoint3(0, 0, 0); + + // Compute the "Spring" Force + + NiPoint3 diff2(diff.x * diff.x * sgn(diff.x), + diff.y * diff.y * sgn(diff.y), + diff.z * diff.z * sgn(diff.z)); + + NiPoint3 force = (diff * stiffness) + (diff2 * stiffness2) - (newRotation * skelSpaceInvTransform * NiPoint3(0, 0, gravityBias)); #if DEBUG - logger.Error("Diff2: "); - ShowPos(diff2); - logger.Error("Force with stiffness %f, stiffness2 %f, gravity bias %f: ", stiffness, stiffness2, gravityBias); - ShowPos(force); + logger.Error("Diff2: "); + ShowPos(diff2); + logger.Error("Force with stiffness %f, stiffness2 %f, gravity bias %f: ", stiffness, stiffness2, gravityBias); + ShowPos(force); #endif - do - { - // Assume mass is 1, so Accelleration is Force, can vary mass by changinf force - //velocity = (velocity + (force * timeStep)) * (1 - (damping * timeStep)); - velocity = (velocity + (force * timeStep)) - (velocity * (damping * timeStep)); + //velocity = obj->m_parent->m_worldTransform.rot * velocity; + + do + { + // Assume mass is 1, so Accelleration is Force, can vary mass by changinf force + //velocity = (velocity + (force * timeStep)) * (1 - (damping * timeStep)); + velocity = (velocity + (force * timeStep)) - (velocity * (damping * timeStep)); - // New position accounting for time - posDelta += (velocity * timeStep); - deltaT -= timeTick; - } while (deltaT >= timeTick); + // New position accounting for time + posDelta += (velocity * timeStep); + deltaT -= timeTick; + } while (deltaT >= timeTick); - NiPoint3 newPos = oldWorldPos + posDelta; + velocity = /*obj->m_parent->m_worldTransform.rot.Transpose() * */velocity; - oldWorldPos = diff + target; + NiPoint3 newPos = oldWorldPos + /*obj->m_parent->m_worldTransform.rot.Transpose() **/ posDelta; + NiPoint3 newPosRot = origWorldPosRot; + + oldWorldPos = diff + target; #if DEBUG - //logger.Error("posDelta: "); - //ShowPos(posDelta); - logger.Error("newPos: "); - ShowPos(newPos); + //logger.Error("posDelta: "); + //ShowPos(posDelta); + logger.Error("newPos: "); + ShowPos(newPos); #endif - // clamp the difference to stop the breast severely lagging at low framerates - diff = newPos - target; + // clamp the difference to stop the breast severely lagging at low framerates + diff = newPos - target; - diff.x = clamp(diff.x, -maxOffsetX, maxOffsetX); - diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); - diff.z = clamp(diff.z - gravityCorrection, -maxOffsetZ, maxOffsetZ) + gravityCorrection; + diff.x = clamp(diff.x, -maxOffsetX, maxOffsetX); + diff.y = clamp(diff.y, -maxOffsetY, maxOffsetY); + diff.z = clamp(diff.z, -maxOffsetZ, maxOffsetZ); #if DEBUG - logger.Error("diff from newPos: "); - ShowPos(diff); - //logger.Error("oldWorldPos: "); - //ShowPos(oldWorldPos); + logger.Error("diff from newPos: "); + ShowPos(diff); + //logger.Error("oldWorldPos: "); + //ShowPos(oldWorldPos); #endif - // move the bones based on the supplied weightings - // Convert the world translations into local coordinates + auto localDiff = diff; - NiMatrix43 rotateLinear; - rotateLinear.SetEulerAngles(rotateLinearX * DEG_TO_RAD, - rotateLinearY * DEG_TO_RAD, - rotateLinearZ * DEG_TO_RAD); + // Transform localDiff (which is in world space) to skeleton space + localDiff = skeletonObj->m_localTransform.rot * localDiff; + + auto scaleMultiplier = 1.0; + localDiff.x *= scaleMultiplier; + localDiff.y *= scaleMultiplier; + localDiff.z *= scaleMultiplier; - NiMatrix43 invRot = rotateLinear * obj->m_parent->m_worldTransform.rot; + auto beforeLocalDiff = localDiff; - auto localDiff = diff; - localDiff = skeletonObj->m_localTransform.rot * localDiff; + // Clamp against settings (which are in skeleton space) + localDiff.x = clamp(localDiff.x, -maxOffsetX, maxOffsetX); + localDiff.y = clamp(localDiff.y, -maxOffsetY, maxOffsetY); + localDiff.z = clamp(localDiff.z, -maxOffsetZ, maxOffsetZ); - localDiff.x = clamp(localDiff.x - posOffsetX, -maxOffsetX, maxOffsetX) + posOffsetX; - localDiff.y = clamp(localDiff.y - posOffsetY, -maxOffsetY, maxOffsetY) + posOffsetY; + beforeLocalDiff = beforeLocalDiff - localDiff; - localDiff.x *= linearX * scaleMultiplierX; - localDiff.y *= linearY * scaleMultiplierY; - localDiff.z *= linearZ * scaleMultiplierZ; + auto linearSpreadForce = 0.75; - auto rotDiff = localDiff; - localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; + localDiff.x += (beforeLocalDiff.y * linearSpreadForce) + (beforeLocalDiff.z * linearSpreadForce); + localDiff.y += (beforeLocalDiff.x * linearSpreadForce) + (beforeLocalDiff.z * linearSpreadForce); + localDiff.z += (beforeLocalDiff.x * linearSpreadForce) + (beforeLocalDiff.y * linearSpreadForce); + + // Clamp against settings (which are in skeleton space) + localDiff.x = clamp(localDiff.x, -maxOffsetX, maxOffsetX); + localDiff.y = clamp(localDiff.y, -maxOffsetY, maxOffsetY); + localDiff.z = clamp(localDiff.z, -maxOffsetZ, maxOffsetZ); + + localDiff.x *= linearX; + localDiff.y *= linearY; + localDiff.z *= linearZ; + + // Store a copy of localDiff for later for transforming rotation motions + auto rotDiff = localDiff; + + + if (IsBreastBone) //other bones don't need to edited gravity by SPINE2 obj + { + //Get the reference bone to know which way the breasts are orientated + //thing_ReadNode_lock.lock(); + BSFixedString chest_str("Chest"); + + NiAVObject* breastGravityReferenceBone = actor->unkF0->rootNode->GetObjectByName(&chest_str); + + //thing_ReadNode_lock.unlock(); + + float gravityRatio = 1.0f; + if (breastGravityReferenceBone != nullptr) + { + //auto breastRot = breastGravityReferenceBone->m_worldTransform.rot; + //Get the orientation (here the Z element of the rotation matrix (1.0 when standing up, -1.0 when upside down)) + auto chestOrientation = breastGravityReferenceBone->m_worldTransform.rot.data[1][2]; + gravityRatio = chestOrientation >= 0.0 ? chestOrientation : 0.0; + } + else + { + gravityRatio = 0.0; + } + + // Calculate the resulting gravity + if (rightside) + { + varGravityCorrection = NiPoint3(gravityRatio * -gravityCorrection * 5.0, 0.0, gravityCorrection * 2); + } + else + { + varGravityCorrection = NiPoint3(-gravityRatio * -gravityCorrection * 5.0, 0.0, gravityCorrection * 2); + } + + //if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || + // ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") + // ) + //{ + // logger.Error("m_worldTransform.rot - %s\n", boneName.c_str()); + // ShowRot(breastGravityReferenceBone->m_worldTransform.rot); + // logger.Error("firstWorldPos - %s\n", boneName.c_str()); + // ShowPos(firstWorldPos); + // logger.Error("firstSkeletonPos - %s\n", boneName.c_str()); + // ShowPos(firstSkeletonPos); + // logger.Error("-------------------------------------------------\n"); + // logger.Error("gravityRatio - %s - %f\n", boneName.c_str(), gravityRatio); + // logger.Error("varGravityCorrection - %s\n", boneName.c_str()); + // ShowPos(varGravityCorrection); + //} + } + + else //other nodes are based on parent obj + { + varGravityCorrection = NiPoint3(0.0, 0.0, gravityCorrection); + } + + //if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") + // ) + //{ + // logger.Error("origWorldRot: "); + // ShowRot((origWorldRot).Transpose()); + // logger.Error("obj->m_parent->m_worldTransform.rot: "); + // ShowRot(obj->m_parent->m_worldTransform.rot); + // logger.Error("Rotation: "); + // ShowRot(newRotation); + // logger.Error("localDiff: "); + // ShowPos(localDiff); + //} + + // Transform localDiff to world coordinates + localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; + + //if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || + // ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") + // ) + //{ + // logger.Error("localDiff - %s:", boneName.c_str() + // ); + // ShowPos(localDiff); + //} + + auto newWorldPos = localDiff; + + newWorldPos.x += varGravityCorrection.x * linearX; + newWorldPos.y += varGravityCorrection.y * linearY; + + oldWorldPos = diff + target; + + // Create the rotated world space transformation matrix + NiMatrix43 rotatedInvWorldTrans = rotateLinear * newRotation.Transpose() * obj->m_parent->m_worldTransform.rot; + + // Transform localDiff to a settings-rotated local space + //newWorldPos = rotatedInvWorldTrans * newWorldPos; + + auto newLocalPos = origLocalPos[boneName.c_str()][actor->formID] + (rotatedInvWorldTrans * newWorldPos); + + newLocalPos += rotateLinear * obj->m_parent->m_worldTransform.rot * NiPoint3(0, 0, varGravityCorrection.z * linearZ); + + //varGravityCorrection = rotatedInvWorldTrans * varGravityCorrection; + + //// gravity correction (after getting rot diff) + //newLocalPos.x += varGravityCorrection.x; + //newLocalPos.y += varGravityCorrection.y; + //newLocalPos.z += varGravityCorrection.z; + + if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || + ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") + ) + { + //logger.Error("skeletonObj->m_localTransform.rot.Transpose()\n"); + //ShowRot(skeletonObj->m_localTransform.rot.Transpose()); + //logger.Error("rotatedInvWorldTrans\n"); + //ShowRot(rotatedInvWorldTrans); + logger.Error("newWorldPos: "); + ShowPos(newWorldPos); + logger.Error("newLocalPos: "); + ShowPos(newLocalPos); + logger.Error("varGravityCorrection: "); + ShowPos(varGravityCorrection); + } + + + //for update oldWorldPos&Rot when frame gap + //oldLocalDiff = localDiff - (rotatedInvWorldTrans * NiPoint3(0, 0, gravityCorrection)); - localDiff = invRot * localDiff; - oldWorldPos = diff + target; #if DEBUG - logger.Error("invRot x=10 Transformation:"); - ShowPos(invRot * NiPoint3(10, 0, 0)); - logger.Error("invRot y=10 Transformation:"); - ShowPos(invRot * NiPoint3(0, 10, 0)); - logger.Error("invRot z=10 Transformation:"); - ShowPos(invRot * NiPoint3(0, 0, 10)); - logger.Error("oldWorldPos: "); - ShowPos(oldWorldPos); - logger.Error("localTransform.pos: "); - ShowPos(obj->m_localTransform.pos); - logger.Error("localDiff: "); - ShowPos(localDiff); - logger.Error("rotDiff: "); - ShowPos(rotDiff); + logger.Error("rotatedInvWorldTrans x=10 Transformation:"); + ShowPos(rotatedInvWorldTrans * NiPoint3(10, 0, 0)); + logger.Error("rotatedInvWorldTrans y=10 Transformation:"); + ShowPos(rotatedInvWorldTrans * NiPoint3(0, 10, 0)); + logger.Error("rotatedInvWorldTrans z=10 Transformation:"); + ShowPos(rotatedInvWorldTrans * NiPoint3(0, 0, 10)); + logger.Error("oldWorldPos: "); + ShowPos(oldWorldPos); + logger.Error("localTransform.pos: "); + ShowPos(obj->m_localTransform.pos); + logger.Error("rotDiff: "); + ShowPos(rotDiff); #endif - // scale positions from config - NiPoint3 newLocalPos = NiPoint3((localDiff.x) + origLocalPos[boneName.c_str()][actor->formID].x, - (localDiff.y) + origLocalPos[boneName.c_str()][actor->formID].y, - (localDiff.z) + origLocalPos[boneName.c_str()][actor->formID].z - ); - obj->m_localTransform.pos = newLocalPos; + + // Calculate the new local pos as an offset from the original local pos + //NiPoint3 newLocalPos = NiPoint3( + // (localDiff.x) + origLocalPos[boneName.c_str()][actor->formID].x, + // (localDiff.y) + origLocalPos[boneName.c_str()][actor->formID].y, + // (localDiff.z) + origLocalPos[boneName.c_str()][actor->formID].z + //); + + obj->m_localTransform.pos = newLocalPos; - if (absRotX) rotDiff.x = fabs(rotDiff.x); + // Calculate rotational motion + if (absRotX) rotDiff.x = fabs(rotDiff.x); - rotDiff.x *= rotationalX; - rotDiff.y *= rotationalY; - rotDiff.z *= rotationalZ; + // Rotational motion scale + rotDiff.x *= rotationalX; + rotDiff.y *= rotationalY; + rotDiff.z *= rotationalZ; #if DEBUG - logger.Error("localTransform.pos after: "); - ShowPos(obj->m_localTransform.pos); - logger.Error("origLocalPos:"); - ShowPos(origLocalPos[boneName.c_str()][actor->formID]); - logger.Error("origLocalRot:"); - ShowRot(origLocalRot[boneName.c_str()][actor->formID]); + logger.Error("localTransform.pos after: "); + ShowPos(obj->m_localTransform.pos); + logger.Error("origLocalPos:"); + ShowPos(origLocalPos[boneName.c_str()][actor->formID]); + logger.Error("origLocalRot:"); + ShowRot(origLocalRot[boneName.c_str()][actor->formID]); #endif - // Do rotation. - NiMatrix43 rotateRotation; - rotateRotation.SetEulerAngles(rotateRotationX * DEG_TO_RAD, - rotateRotationY * DEG_TO_RAD, - rotateRotationZ * DEG_TO_RAD); + // Rotate rotation according to settings. + NiMatrix43 rotateRotation; + rotateRotation.SetEulerAngles(rotateRotationX * DEG_TO_RAD, + rotateRotationY * DEG_TO_RAD, + rotateRotationZ * DEG_TO_RAD); - NiMatrix43 standardRot; + NiMatrix43 standardRot; + + rotDiff = rotateRotation * rotDiff; + standardRot.SetEulerAngles(rotDiff.x, rotDiff.y, rotDiff.z); + // Calculate the new local rot as an offset from the original local rot + obj->m_localTransform.rot = standardRot * origLocalRot[boneName.c_str()][actor->formID]; - rotDiff = rotateRotation * rotDiff; - standardRot.SetEulerAngles(rotDiff.x, rotDiff.y, rotDiff.z); - obj->m_localTransform.rot = standardRot * origLocalRot[boneName.c_str()][actor->formID]; - } #if DEBUG logger.Error("end update()\n"); #endif diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 7f961e3..3b0b472 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -4,9 +4,12 @@ #include #include #include "config.h" +#include -typedef std::unordered_map> pos_map; -typedef std::unordered_map> rot_map; +using std::shared_mutex; + +typedef concurrency::concurrent_unordered_map> pos_map; +typedef concurrency::concurrent_unordered_map> rot_map; inline void RefreshNode(NiAVObject* node) { @@ -30,10 +33,19 @@ class Thing { BSFixedString boneName; NiPoint3 oldWorldPos; + NiPoint3 oldWorldPosRot; + //NiPoint3 oldLocalDiff; float oldRotZ; NiPoint3 velocity; clock_t time; NiAVObject* thingObj; + bool IsBreastBone; + NiPoint3 varGravityCorrection; + NiPoint3 firstWorldPos; + NiPoint3 firstSkeletonPos; + bool firstSkeleton; + NiMatrix43 firstWorldRot; + NiMatrix43 origWorldRot; public: float stiffness = 0.5f; @@ -77,6 +89,10 @@ class Thing static pos_map origLocalPos; static rot_map origLocalRot; + static rot_map origChestWorldRot; + + // Maps are sorted every edit time, so if it is parallel processing then a high probability of overloading + static shared_mutex thing_map_lock; Thing(NiAVObject* obj, BSFixedString& name); ~Thing(); @@ -89,4 +105,18 @@ class Thing void ShowPos(NiPoint3& p); void ShowRot(NiMatrix43& r); + + static inline bool ContainsNoCase(std::string str, std::string ministr) + { + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + std::transform(ministr.begin(), ministr.end(), ministr.begin(), ::tolower); + + if (str.find(ministr) != std::string::npos) + { + return true; + } + else + return false; + } + }; \ No newline at end of file From af23922d3d3eb43c1fad36c330a2ea10638d8b16 Mon Sep 17 00:00:00 2001 From: ericncream Date: Fri, 30 Sep 2022 04:20:56 -0700 Subject: [PATCH 54/66] Some cleanup of Thing. --- CBPSSE/SimObj.cpp | 2 +- CBPSSE/Thing.cpp | 50 +++++++++++++++++------------------------------ CBPSSE/Thing.h | 7 +++---- 3 files changed, 22 insertions(+), 37 deletions(-) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 86ace90..64b553e 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -53,7 +53,7 @@ bool SimObj::AddBonesToThings(Actor* actor, std::vector& boneNames) else if (findBone == things.end()) { //logger.info("Doing Bone %s for actor %08x\n", b, actor->formID); - things.insert(std::make_pair(b, Thing(bone, cs))); + things.insert(std::make_pair(b, Thing(bone, cs, actor))); } } } diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 3d93e97..3b3ada6 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -6,7 +6,6 @@ constexpr float DEG_TO_RAD = 3.14159265 / 180; const char* skeletonNif_boneName = "skeleton.nif"; -const char* COM_boneName = "COM"; const float DIFF_LIMIT = 100.0; // TODO Make these logger macros @@ -32,12 +31,26 @@ void Thing::ShowRot(NiMatrix43& r) logger.Info("%8.4f %8.4f %8.4f %8.4f\n", r.data[2][0], r.data[2][1], r.data[2][2], r.data[2][3]); } -Thing::Thing(NiAVObject* obj, BSFixedString& name) +Thing::Thing(NiAVObject* obj, BSFixedString& name, Actor* actor) : thingObj(obj) , boneName(name) , velocity(NiPoint3(0, 0, 0)) + , m_actor(actor) { + auto skeletonObj = actorUtils::GetBaseSkeleton(actor); + if (skeletonObj == NULL) + { + logger.Error("%s: Didn't find thing %s's base skeleton.nif for actor %08x \n", __func__, boneName.c_str(), actor->formID); + return; + } + + //->m_worldTransform.rot * skeletonObj->m_worldTransform.pos + auto firstWorldPos = skeletonObj->m_worldTransform.rot * obj->m_worldTransform.pos; + auto firstSkeletonPos = skeletonObj->m_worldTransform.rot * skeletonObj->m_worldTransform.pos; + + rightSide = (firstWorldPos.x - firstSkeletonPos.x) >= 0.0; + // Set initial positions oldWorldPos = obj->m_worldTransform.pos; @@ -50,7 +63,6 @@ Thing::Thing(NiAVObject* obj, BSFixedString& name) time = clock(); IsBreastBone = ContainsNoCase(std::string(boneName.c_str()), "Breast"); - firstSkeleton = true; } Thing::~Thing() @@ -314,20 +326,6 @@ void Thing::UpdateThing(Actor* actor) return; } - if (firstSkeleton) - { - firstSkeleton = false; - firstSkeletonPos = skeletonObj->m_worldTransform.rot * skeletonObj->m_worldTransform.pos; - firstWorldPos = skeletonObj->m_worldTransform.rot * obj->m_worldTransform.pos; - - } - - BSFixedString com_str(COM_boneName); - - NiAVObject* comObj = actor->unkF0->rootNode->GetObjectByName(&com_str); - - bool rightside = (firstWorldPos.x - firstSkeletonPos.x) >= 0.0; - #if DEBUG logger.Error("bone %s for actor %08x with parent %s\n", boneName.c_str(), actor->formID, skeletonObj->m_name.c_str()); ShowRot(skeletonObj->m_worldTransform.rot); @@ -348,7 +346,7 @@ void Thing::UpdateThing(Actor* actor) // Cog offset is offset to move Center of Mass make rotational motion more significant // First transform the cog offset (which is in skeleton space) to world space // target is in world space - NiPoint3 target = /*(skelSpaceInvTransform * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) +*/ origWorldPos; + NiPoint3 target = (skelSpaceInvTransform * NiPoint3(cogOffsetX, cogOffsetY, cogOffsetZ)) + origWorldPos; #if DEBUG logger.Error("World Position: "); @@ -366,10 +364,6 @@ void Thing::UpdateThing(Actor* actor) ShowPos(skelSpaceInvTransform * NiPoint3(0, 0, cogOffsetZ)); #endif - auto gravityInvertedCorrectionStart = 0.25; - auto gravityInvertedCorrectionEnd = 0.75; - auto gravityInvertedCorrection = -4.0; - // diff is Difference in position between old and new world position // diff is world space NiPoint3 diff = (target - oldWorldPos) * forceMultiplier; @@ -529,7 +523,7 @@ void Thing::UpdateThing(Actor* actor) } // Calculate the resulting gravity - if (rightside) + if (rightSide) { varGravityCorrection = NiPoint3(gravityRatio * -gravityCorrection * 5.0, 0.0, gravityCorrection * 2); } @@ -600,14 +594,7 @@ void Thing::UpdateThing(Actor* actor) auto newLocalPos = origLocalPos[boneName.c_str()][actor->formID] + (rotatedInvWorldTrans * newWorldPos); - newLocalPos += rotateLinear * obj->m_parent->m_worldTransform.rot * NiPoint3(0, 0, varGravityCorrection.z * linearZ); - - //varGravityCorrection = rotatedInvWorldTrans * varGravityCorrection; - - //// gravity correction (after getting rot diff) - //newLocalPos.x += varGravityCorrection.x; - //newLocalPos.y += varGravityCorrection.y; - //newLocalPos.z += varGravityCorrection.z; + newLocalPos += rotateLinear * obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * NiPoint3(0, 0, varGravityCorrection.z * linearZ); if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") @@ -625,7 +612,6 @@ void Thing::UpdateThing(Actor* actor) ShowPos(varGravityCorrection); } - //for update oldWorldPos&Rot when frame gap //oldLocalDiff = localDiff - (rotatedInvWorldTrans * NiPoint3(0, 0, gravityCorrection)); diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 3b0b472..787c217 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -41,11 +41,10 @@ class Thing NiAVObject* thingObj; bool IsBreastBone; NiPoint3 varGravityCorrection; - NiPoint3 firstWorldPos; - NiPoint3 firstSkeletonPos; - bool firstSkeleton; NiMatrix43 firstWorldRot; NiMatrix43 origWorldRot; + bool rightSide; + Actor* m_actor; public: float stiffness = 0.5f; @@ -94,7 +93,7 @@ class Thing // Maps are sorted every edit time, so if it is parallel processing then a high probability of overloading static shared_mutex thing_map_lock; - Thing(NiAVObject* obj, BSFixedString& name); + Thing(NiAVObject* obj, BSFixedString& name, Actor* actor); ~Thing(); NiAVObject* IsThingActorValid(Actor* actor); From c34bf45a35951e688d3f56a202da38e026d1f942 Mon Sep 17 00:00:00 2001 From: ericncream Date: Sun, 13 Nov 2022 21:00:34 -0800 Subject: [PATCH 55/66] Add gravitySupine and linearSpreadforce. Cleanup. --- CBPSSE/Thing.cpp | 138 +++++++++++++---------------------------------- CBPSSE/Thing.h | 10 +++- 2 files changed, 47 insertions(+), 101 deletions(-) diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 3b3ada6..375a034 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -202,6 +202,14 @@ void Thing::UpdateConfig(configEntry_t& centry) timeTick = 1; absRotX = centry["absRotX"] != 0.0; + + linearSpreadforceX = centry["linearSpreadforceX"]; + linearSpreadforceY = centry["linearSpreadforceY"]; + linearSpreadforceZ = centry["linearSpreadforceZ"]; + + gravitySupineX = centry["gravitySupineX"]; + gravitySupineY = centry["gravitySupineY"]; + gravitySupineZ = centry["gravitySupineZ"]; } static float clamp(float val, float min, float max) @@ -332,12 +340,6 @@ void Thing::UpdateThing(Actor* actor) //ShowPos(obj->m_parent->m_worldTransform.rot.Transpose() * obj->m_localTransform.pos); #endif - //if (isSkippedmanyFrames) //prevents many bounce when fps gaps - //{ - // oldWorldPos = obj->m_parent->m_worldTransform.pos + obj->m_parent->m_worldTransform.rot.Transpose() * (oldLocalDiff + origLocalPos[boneName.c_str()][actor->formID]); - // return; - //} - NiMatrix43 skelSpaceInvTransform = skeletonObj->m_localTransform.rot.Transpose(); NiPoint3 origWorldPos = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos[boneName.c_str()][actor->formID]) + obj->m_parent->m_worldTransform.pos; NiPoint3 origWorldPosRot = (obj->m_parent->m_worldTransform.rot.Transpose() * origLocalPos[boneName.c_str()][actor->formID]) + obj->m_parent->m_worldTransform.pos; @@ -367,7 +369,6 @@ void Thing::UpdateThing(Actor* actor) // diff is Difference in position between old and new world position // diff is world space NiPoint3 diff = (target - oldWorldPos) * forceMultiplier; - NiPoint3 diffRot = (target - oldWorldPosRot) * forceMultiplier; #if DEBUG logger.Error("Diff after gravity correction %f: ", varGravityCorrection); @@ -387,16 +388,15 @@ void Thing::UpdateThing(Actor* actor) time = clock(); return; } - + + // Rotation for transforming gravityBias back to world coordinates auto newRotation = obj->m_parent->m_worldTransform.rot * origWorldRot.Transpose(); // move the bones based on the supplied weightings // Convert the world translations into local coordinates NiMatrix43 rotateLinear; - rotateLinear.SetEulerAngles(rotateLinearX * DEG_TO_RAD, - rotateLinearY * DEG_TO_RAD, - rotateLinearZ * DEG_TO_RAD); + rotateLinear.SetEulerAngles(rotateLinearX * DEG_TO_RAD, rotateLinearY * DEG_TO_RAD, rotateLinearZ * DEG_TO_RAD); auto timeMultiplier = timeTick / (float)deltaT; @@ -421,8 +421,6 @@ void Thing::UpdateThing(Actor* actor) ShowPos(force); #endif - //velocity = obj->m_parent->m_worldTransform.rot * velocity; - do { // Assume mass is 1, so Accelleration is Force, can vary mass by changinf force @@ -482,9 +480,9 @@ void Thing::UpdateThing(Actor* actor) auto linearSpreadForce = 0.75; - localDiff.x += (beforeLocalDiff.y * linearSpreadForce) + (beforeLocalDiff.z * linearSpreadForce); - localDiff.y += (beforeLocalDiff.x * linearSpreadForce) + (beforeLocalDiff.z * linearSpreadForce); - localDiff.z += (beforeLocalDiff.x * linearSpreadForce) + (beforeLocalDiff.y * linearSpreadForce); + localDiff.x += (beforeLocalDiff.y * linearSpreadforceY) + (beforeLocalDiff.z * linearSpreadforceZ); + localDiff.y += (beforeLocalDiff.x * linearSpreadforceX) + (beforeLocalDiff.z * linearSpreadforceZ); + localDiff.z += (beforeLocalDiff.x * linearSpreadforceX) + (beforeLocalDiff.y * linearSpreadforceY); // Clamp against settings (which are in skeleton space) localDiff.x = clamp(localDiff.x, -maxOffsetX, maxOffsetX); @@ -509,80 +507,31 @@ void Thing::UpdateThing(Actor* actor) //thing_ReadNode_lock.unlock(); - float gravityRatio = 1.0f; + float gravityRatio = 0.0f; if (breastGravityReferenceBone != nullptr) { //auto breastRot = breastGravityReferenceBone->m_worldTransform.rot; - //Get the orientation (here the Z element of the rotation matrix (1.0 when standing up, -1.0 when upside down)) + //Get the orientation (here the Z element of the rotation matrix (approx 1.0 when standing up, approx -1.0 when upside down)) auto chestOrientation = breastGravityReferenceBone->m_worldTransform.rot.data[1][2]; gravityRatio = chestOrientation >= 0.0 ? chestOrientation : 0.0; } - else - { - gravityRatio = 0.0; - } - // Calculate the resulting gravity - if (rightSide) - { - varGravityCorrection = NiPoint3(gravityRatio * -gravityCorrection * 5.0, 0.0, gravityCorrection * 2); - } - else - { - varGravityCorrection = NiPoint3(-gravityRatio * -gravityCorrection * 5.0, 0.0, gravityCorrection * 2); - } - - //if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || - // ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") - // ) - //{ - // logger.Error("m_worldTransform.rot - %s\n", boneName.c_str()); - // ShowRot(breastGravityReferenceBone->m_worldTransform.rot); - // logger.Error("firstWorldPos - %s\n", boneName.c_str()); - // ShowPos(firstWorldPos); - // logger.Error("firstSkeletonPos - %s\n", boneName.c_str()); - // ShowPos(firstSkeletonPos); - // logger.Error("-------------------------------------------------\n"); - // logger.Error("gravityRatio - %s - %f\n", boneName.c_str(), gravityRatio); - // logger.Error("varGravityCorrection - %s\n", boneName.c_str()); - // ShowPos(varGravityCorrection); - //} + varGravitySupine = NiPoint3(gravitySupineX, gravitySupineY, gravitySupineZ) * gravityRatio; } - - else //other nodes are based on parent obj + else { - varGravityCorrection = NiPoint3(0.0, 0.0, gravityCorrection); + //other nodes are based on parent obj + varGravitySupine = NiPoint3(0.0, 0.0, 0.0); } - //if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") - // ) - //{ - // logger.Error("origWorldRot: "); - // ShowRot((origWorldRot).Transpose()); - // logger.Error("obj->m_parent->m_worldTransform.rot: "); - // ShowRot(obj->m_parent->m_worldTransform.rot); - // logger.Error("Rotation: "); - // ShowRot(newRotation); - // logger.Error("localDiff: "); - // ShowPos(localDiff); - //} - // Transform localDiff to world coordinates localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; - //if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || - // ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") - // ) - //{ - // logger.Error("localDiff - %s:", boneName.c_str() - // ); - // ShowPos(localDiff); - //} - auto newWorldPos = localDiff; - newWorldPos.x += varGravityCorrection.x * linearX; - newWorldPos.y += varGravityCorrection.y * linearY; + newWorldPos.x += varGravitySupine.x * linearX; + newWorldPos.y += varGravitySupine.y * linearY; + newWorldPos.z += varGravitySupine.z * linearZ; oldWorldPos = diff + target; @@ -594,26 +543,22 @@ void Thing::UpdateThing(Actor* actor) auto newLocalPos = origLocalPos[boneName.c_str()][actor->formID] + (rotatedInvWorldTrans * newWorldPos); - newLocalPos += rotateLinear * obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * NiPoint3(0, 0, varGravityCorrection.z * linearZ); + // Apply gravityCorrection, which will always point downward + newLocalPos += rotateLinear * obj->m_parent->m_worldTransform.rot * skeletonObj->m_localTransform.rot.Transpose() * NiPoint3(0, 0, gravityCorrection * linearZ); - if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || - ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") - ) - { - //logger.Error("skeletonObj->m_localTransform.rot.Transpose()\n"); - //ShowRot(skeletonObj->m_localTransform.rot.Transpose()); - //logger.Error("rotatedInvWorldTrans\n"); - //ShowRot(rotatedInvWorldTrans); - logger.Error("newWorldPos: "); - ShowPos(newWorldPos); - logger.Error("newLocalPos: "); - ShowPos(newLocalPos); - logger.Error("varGravityCorrection: "); - ShowPos(varGravityCorrection); - } - - //for update oldWorldPos&Rot when frame gap - //oldLocalDiff = localDiff - (rotatedInvWorldTrans * NiPoint3(0, 0, gravityCorrection)); + //if (ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_R_02") || + // ContainsNoCase(std::string(boneName.c_str()), "Breast_CBP_L_02") + // ) + //{ + // //logger.Error("skeletonObj->m_localTransform.rot.Transpose()\n"); + // //ShowRot(skeletonObj->m_localTransform.rot.Transpose()); + // //logger.Error("rotatedInvWorldTrans\n"); + // //ShowRot(rotatedInvWorldTrans); + // logger.Error("newWorldPos: "); + // ShowPos(newWorldPos); + // logger.Error("newLocalPos: "); + // ShowPos(newLocalPos); + //} #if DEBUG logger.Error("rotatedInvWorldTrans x=10 Transformation:"); @@ -630,13 +575,6 @@ void Thing::UpdateThing(Actor* actor) ShowPos(rotDiff); #endif - - // Calculate the new local pos as an offset from the original local pos - //NiPoint3 newLocalPos = NiPoint3( - // (localDiff.x) + origLocalPos[boneName.c_str()][actor->formID].x, - // (localDiff.y) + origLocalPos[boneName.c_str()][actor->formID].y, - // (localDiff.z) + origLocalPos[boneName.c_str()][actor->formID].z - //); obj->m_localTransform.pos = newLocalPos; diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 787c217..1b5a673 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -40,7 +40,7 @@ class Thing clock_t time; NiAVObject* thingObj; bool IsBreastBone; - NiPoint3 varGravityCorrection; + NiPoint3 varGravitySupine; NiMatrix43 firstWorldRot; NiMatrix43 origWorldRot; bool rightSide; @@ -86,6 +86,14 @@ class Thing bool absRotX = 0; + float linearSpreadforceX = 0.0f; + float linearSpreadforceY = 0.0f; + float linearSpreadforceZ = 0.0f; + + float gravitySupineX = 0.0f; + float gravitySupineY = 0.0f; + float gravitySupineZ = 0.0f; + static pos_map origLocalPos; static rot_map origLocalRot; static rot_map origChestWorldRot; From 57d0846c3847299e3acafd28120dfe17618002a3 Mon Sep 17 00:00:00 2001 From: ericncream Date: Sat, 26 Nov 2022 05:57:22 -0800 Subject: [PATCH 56/66] Add actor filters. Add more configs: supine and spread force. --- CBPSSE/ActorUtils.cpp | 182 +++++++++++++++++++++++++++++++----------- CBPSSE/ActorUtils.h | 5 ++ CBPSSE/Thing.cpp | 65 ++++++++------- CBPSSE/Thing.h | 2 +- CBPSSE/config.cpp | 65 ++++++++++++++- CBPSSE/config.h | 9 +++ 6 files changed, 247 insertions(+), 81 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 81bc15e..df6468d 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -49,6 +49,22 @@ NiAVObject* actorUtils::GetBaseSkeleton(Actor* actor) return obj; } +bool actorUtils::IsActorPriorityBlacklisted(Actor* actor, UInt32 priority) +{ + bool result = false; + auto actorOverrideConfigEntry = configActorOverrideMap[priority]; + auto isFilterInverted = configActorOverrideMap[priority].isFilterInverted; + auto overrideActors = actorOverrideConfigEntry.actors; + + if (!isFilterInverted) + { + // Result is in the blacklist + result = !(overrideActors.find(actor->formID) == overrideActors.end()); + } + + return result; +} + bool actorUtils::IsActorMale(Actor* actor) { if (!IsActorValid(actor)) @@ -67,6 +83,21 @@ bool actorUtils::IsActorMale(Actor* actor) return false; } +bool actorUtils::IsActorPriorityWhitelisted(Actor* actor, UInt32 priority) +{ + bool result = false; + auto actorOverrideConfigEntry = configActorOverrideMap[priority]; + auto isFilterInverted = configActorOverrideMap[priority].isFilterInverted; + auto overrideActors = actorOverrideConfigEntry.actors; + + if (isFilterInverted) + { + // Result is in the whitelist + result = !(overrideActors.find(actor->formID) == overrideActors.end()); + } + return result; +} + bool actorUtils::IsActorInPowerArmor(Actor* actor) { bool isInPowerArmor = false; @@ -190,6 +221,9 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { std::multiset key; + // key always starts with actors formID (really the refID) + key.emplace((UInt64)actor->formID); + for (auto slot : usedSlots) { // Check if actor has config's slot equipped @@ -221,75 +255,131 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) // Otherwise, build the actor's config config_t baseConfig = config; - for (auto overrideConfigIter = configArmorOverrideMap.rbegin(); overrideConfigIter != configArmorOverrideMap.rend(); ++overrideConfigIter) - { - auto orData = overrideConfigIter->second; - std::vector equippedList; + for (auto priIter = priorities.rbegin(); priIter != priorities.rend(); ++priIter) + { + UInt32 priority = *priIter; - // Make a list of actor's slots that are config's slots - for (auto slot : orData.slots) + // Check if there is no armor slot override entry + if (configArmorOverrideMap.find(priority) == configArmorOverrideMap.end()) { - EquippedArmor equipped = actorUtils::GetActorEquippedArmor(actor, slot); - if (equipped.armor && equipped.model) + auto overrideConfig = configActorOverrideMap[priority].config; + + if (IsActorPriorityWhitelisted(actor, priority)) { - equippedList.push_back(equipped); + for (auto val : overrideConfig) + { + // for each bone, if it is empty, we need to disable it, + // otherwise the configEntry is good. + if (overrideConfig[val.first].empty()) + { + // This is ok because we're doing this to a premade copy sequentially + baseConfig.unsafe_erase(val.first); + } + else + { + baseConfig[val.first] = val.second; + } + } + } + if (!IsActorPriorityBlacklisted(actor, priority)) + { + for (auto val : overrideConfig) + { + // for each bone, if it is empty, we need to disable it, + // otherwise the configEntry is good. + if (overrideConfig[val.first].empty()) + { + // This is ok because we're doing this to a premade copy sequentially + baseConfig.unsafe_erase(val.first); + } + else + { + baseConfig[val.first] = val.second; + } + } } } + else // There is an armor slot override entry + { + // If priority level has an entry in actor override map AND + // actor is not whitelisted or is blacklisted, continue on + if (configActorOverrideMap.find(priority) == configActorOverrideMap.end()) + { + if (!IsActorPriorityWhitelisted(actor, priority) || + IsActorPriorityBlacklisted(actor, priority)) + { + continue; + } + } + auto orData = configArmorOverrideMap[priority]; - auto armorFormID = orData.armors.begin(); - // whitelist filter - if (orData.isFilterInverted) - { - for (; armorFormID != orData.armors.end(); ++armorFormID) + std::vector equippedList; + + // Make a list of actor's slots that are config's slots + for (auto slot : orData.slots) { - bool breakOutside = false; - // Check config's filter IDs against found slot's IDs - for (auto equipped : equippedList) + EquippedArmor equipped = actorUtils::GetActorEquippedArmor(actor, slot); + if (equipped.armor && equipped.model) { - if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) + equippedList.push_back(equipped); + } + } + + auto armorFormID = orData.armors.begin(); + // whitelist filter + if (orData.isFilterInverted) + { + for (; armorFormID != orData.armors.end(); ++armorFormID) + { + bool breakOutside = false; + // Check config's filter IDs against found slot's IDs + for (auto equipped : equippedList) { - for (auto val : orData.config) + if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) { - // for each bone, if it is empty, we need to disable it, - // otherwise the configEntry is good. - if (orData.config[val.first].empty()) - { - // This is ok because we're doing this to a premade copy sequentially - baseConfig.unsafe_erase(val.first); - } - else + for (auto val : orData.config) { - baseConfig[val.first] = val.second; + // for each bone, if it is empty, we need to disable it, + // otherwise the configEntry is good. + if (orData.config[val.first].empty()) + { + // This is ok because we're doing this to a premade copy sequentially + baseConfig.unsafe_erase(val.first); + } + else + { + baseConfig[val.first] = val.second; + } } + + breakOutside = true; + break; } + } - breakOutside = true; + if (breakOutside) + { break; } } - - if (breakOutside) - { - break; - } } - } - // blacklist filter - if (!orData.isFilterInverted && armorFormID == orData.armors.end() && !equippedList.empty()) - { - for (auto val : orData.config) + // blacklist filter + if (!orData.isFilterInverted && armorFormID == orData.armors.end() && !equippedList.empty()) { - if (orData.config[val.first].empty()) - { - // This is ok because we're doing this to a premade copy sequentially - baseConfig.unsafe_erase(val.first); - } - else + for (auto val : orData.config) { - baseConfig[val.first] = val.second; + if (orData.config[val.first].empty()) + { + // This is ok because we're doing this to a premade copy sequentially + baseConfig.unsafe_erase(val.first); + } + else + { + baseConfig[val.first] = val.second; + } } } } diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index 51e776e..415658a 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -12,10 +12,15 @@ namespace actorUtils std::string GetActorRaceEID(Actor* actor); NiAVObject* GetBaseSkeleton(Actor* actor); + bool IsActorPriorityBlacklisted(Actor* actor, UInt32 priority); bool IsActorInPowerArmor(Actor* actor); + bool IsActorPriorityWhitelisted(Actor* actor, UInt32 priority); bool IsActorMale(Actor* actor); bool IsActorTrackable(Actor* actor); bool IsActorValid(Actor* actor); + bool IsArmorPriorityBlacklisted(Actor* actor, UInt32 priority); + bool IsActorInPowerArmor(Actor* actor); + bool IsArmorPriorityWhitelisted(Actor* actor, UInt32 priority); bool IsBoneInWhitelist(Actor* actor, std::string boneName); const EquippedArmor GetActorEquippedArmor(Actor* actor, UInt32 slot); diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 375a034..3aa8f47 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -69,6 +69,39 @@ Thing::~Thing() { } +NiPoint3 Thing::CalculateGravitySupine(Actor* actor) +{ + NiPoint3 varGravitySupine = NiPoint3(0.0, 0.0, 0.0); + + if (!IsBreastBone) //other bones don't need to edited gravity by SPINE2 obj + { + //other nodes are based on parent obj + varGravitySupine = NiPoint3(0.0, 0.0, 0.0); + return varGravitySupine; + } + + //Get the reference bone to know which way the breasts are orientated + //thing_ReadNode_lock.lock(); + BSFixedString chest_str("Chest"); + + NiAVObject* breastGravityReferenceBone = actor->unkF0->rootNode->GetObjectByName(&chest_str); + + //thing_ReadNode_lock.unlock(); + + float gravityRatio = 0.0f; + if (breastGravityReferenceBone != nullptr) + { + //auto breastRot = breastGravityReferenceBone->m_worldTransform.rot; + //Get the orientation (here the Z element of the rotation matrix (approx 1.0 when standing up, approx -1.0 when upside down)) + auto chestOrientation = breastGravityReferenceBone->m_worldTransform.rot.data[1][2]; + gravityRatio = chestOrientation >= 0.0 ? chestOrientation : 0.0; + } + + varGravitySupine = NiPoint3(gravitySupineX, gravitySupineY, gravitySupineZ) * gravityRatio; + + return varGravitySupine; +} + void Thing::StoreOriginalTransforms(Actor* actor) { // Save the bones' original local values, per actor, if they already haven't @@ -388,7 +421,7 @@ void Thing::UpdateThing(Actor* actor) time = clock(); return; } - + // Rotation for transforming gravityBias back to world coordinates auto newRotation = obj->m_parent->m_worldTransform.rot * origWorldRot.Transpose(); @@ -478,8 +511,6 @@ void Thing::UpdateThing(Actor* actor) beforeLocalDiff = beforeLocalDiff - localDiff; - auto linearSpreadForce = 0.75; - localDiff.x += (beforeLocalDiff.y * linearSpreadforceY) + (beforeLocalDiff.z * linearSpreadforceZ); localDiff.y += (beforeLocalDiff.x * linearSpreadforceX) + (beforeLocalDiff.z * linearSpreadforceZ); localDiff.z += (beforeLocalDiff.x * linearSpreadforceX) + (beforeLocalDiff.y * linearSpreadforceY); @@ -496,33 +527,7 @@ void Thing::UpdateThing(Actor* actor) // Store a copy of localDiff for later for transforming rotation motions auto rotDiff = localDiff; - - if (IsBreastBone) //other bones don't need to edited gravity by SPINE2 obj - { - //Get the reference bone to know which way the breasts are orientated - //thing_ReadNode_lock.lock(); - BSFixedString chest_str("Chest"); - - NiAVObject* breastGravityReferenceBone = actor->unkF0->rootNode->GetObjectByName(&chest_str); - - //thing_ReadNode_lock.unlock(); - - float gravityRatio = 0.0f; - if (breastGravityReferenceBone != nullptr) - { - //auto breastRot = breastGravityReferenceBone->m_worldTransform.rot; - //Get the orientation (here the Z element of the rotation matrix (approx 1.0 when standing up, approx -1.0 when upside down)) - auto chestOrientation = breastGravityReferenceBone->m_worldTransform.rot.data[1][2]; - gravityRatio = chestOrientation >= 0.0 ? chestOrientation : 0.0; - } - - varGravitySupine = NiPoint3(gravitySupineX, gravitySupineY, gravitySupineZ) * gravityRatio; - } - else - { - //other nodes are based on parent obj - varGravitySupine = NiPoint3(0.0, 0.0, 0.0); - } + auto varGravitySupine = CalculateGravitySupine(actor); // Transform localDiff to world coordinates localDiff = skeletonObj->m_localTransform.rot.Transpose() * localDiff; diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 1b5a673..82058c8 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -40,7 +40,6 @@ class Thing clock_t time; NiAVObject* thingObj; bool IsBreastBone; - NiPoint3 varGravitySupine; NiMatrix43 firstWorldRot; NiMatrix43 origWorldRot; bool rightSide; @@ -106,6 +105,7 @@ class Thing NiAVObject* IsThingActorValid(Actor* actor); void Reset(Actor* actor); + NiPoint3 CalculateGravitySupine(Actor* actor); void StoreOriginalTransforms(Actor* actor); void UpdateThing(Actor* actor); void UpdateConfig(configEntry_t& centry); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 475f150..99a250a 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -4,6 +4,7 @@ #include "SimObj.h" #include "Thing.h" +#include #include #include #include @@ -29,7 +30,10 @@ bool useWhitelist = false; config_t config; std::map configArmorOverrideMap; std::unordered_set usedSlots; +concurrency::concurrent_unordered_map configActorOverrideMap; std::map, config_t> cachedConfigs; +std::unordered_map priorityNameMappings; +std::set priorities; // TODO data structure these whitelist_t whitelist; @@ -99,7 +103,6 @@ bool LoadConfig() config_t configOverrides; std::map configArmorBoneOverrides; std::set bonesSet; - std::unordered_map priorityNameMappings; bool reloadActors = false; auto playerOnlyOld = playerOnly; @@ -196,6 +199,9 @@ bool LoadConfig() auto armorStr = std::string("Armor."); auto splitArmorStr = std::mismatch(armorStr.begin(), armorStr.end(), sectionsIter->begin()); + auto actorStr = std::string("Actor."); + auto splitActorStr = std::mismatch(actorStr.begin(), actorStr.end(), sectionsIter->begin()); + if (*sectionsIter == std::string("Attach")) { // Get section contents @@ -333,13 +339,59 @@ bool LoadConfig() continue; } - auto formID = GetFormIDFromString(valuesIter.second); - if (formID == -1) + auto formID = std::stoul(valuesIter.second, nullptr, 16); + + configArmorOverrideMap[armorPriority].armors.emplace(formID); + } + } + else if (splitActorStr.first == actorStr.end()) + { + auto actorSubname = std::string(splitActorStr.second, sectionsIter->end()); + + UInt32 actorPriority; + auto mapEntry = priorityNameMappings.find(actorSubname); + if (mapEntry != priorityNameMappings.end()) + { + actorPriority = mapEntry->second; + } + else + { + std::string priorityMapping = configReader.Get("Priority", actorSubname, ""); + try + { + actorPriority = std::stoul(priorityMapping); + } + catch (const std::exception&) { continue; } - configArmorOverrideMap[armorPriority].armors.emplace(formID); + priorityNameMappings[actorSubname] = actorPriority; + } + + configActorOverrideMap[actorPriority].isFilterInverted = configReader.GetBoolean(*sectionsIter, "invertFilter", false); + + // Get section contents + auto sectionMap = configReader.Section(*sectionsIter); + for (auto& valuesIter : sectionMap) + { + auto& key = valuesIter.first; + if (key == "invertFilter") + { + continue; + } + + UInt32 refID; + try + { + refID = std::stoul(valuesIter.second); + } + catch (const std::exception&) + { + continue; + } + + configActorOverrideMap[actorPriority].actors.emplace(refID); } } else if (*sectionsIter == std::string("Whitelist") && useWhitelist) @@ -479,6 +531,11 @@ bool LoadConfig() } } + for (auto map : priorityNameMappings) + { + priorities.insert(map.second); + } + logger.Error("Finished CBP Config\n"); return reloadActors; } diff --git a/CBPSSE/config.h b/CBPSSE/config.h index e9787c5..9d2b1bb 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -32,6 +32,13 @@ struct armorOverrideData config_t config; }; +struct actorOverrideData +{ + bool isFilterInverted; + std::unordered_set actors; + config_t config; +}; + extern bool playerOnly; extern bool femaleOnly; extern bool maleOnly; @@ -45,6 +52,8 @@ extern whitelist_t whitelist; extern std::vector raceWhitelist; extern std::unordered_set usedSlots; extern std::map, config_t> cachedConfigs; +extern std::set priorities; +extern concurrency::concurrent_unordered_map configActorOverrideMap; bool LoadConfig(); void DumpWhitelistToLog(); \ No newline at end of file From a6d707a162d17ea6e75605ef963a5fdb9eadc9e8 Mon Sep 17 00:00:00 2001 From: ericncream Date: Mon, 28 Nov 2022 21:23:24 -0800 Subject: [PATCH 57/66] Config read fixes --- CBPSSE/config.cpp | 57 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 99a250a..9b2a4f1 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -262,6 +262,7 @@ bool LoadConfig() // "Touch" the map to add empty entry for bone in config_t to signal deletion later when building config from overrides // This allows for disabling specific disabling attach configs configArmorOverrideMap[attachPriority].config[boneName]; + configActorOverrideMap[attachPriority].config[boneName]; } else if (sections.find(attachName) != sections.end()) { @@ -271,6 +272,7 @@ bool LoadConfig() { auto& keyName = attachIter.first; configArmorOverrideMap[attachPriority].config[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); + configActorOverrideMap[attachPriority].config[boneName][keyName] = configReader.GetFloat(attachName, keyName, 0.0); } } } @@ -339,7 +341,11 @@ bool LoadConfig() continue; } - auto formID = std::stoul(valuesIter.second, nullptr, 16); + auto formID = GetFormIDFromString(valuesIter.second); + if (formID == -1) + { + continue; + } configArmorOverrideMap[armorPriority].armors.emplace(formID); } @@ -381,12 +387,10 @@ bool LoadConfig() continue; } - UInt32 refID; - try - { - refID = std::stoul(valuesIter.second); - } - catch (const std::exception&) + auto refID = GetFormIDFromString(valuesIter.second); + + logger.Info("refID %s\n", valuesIter.second); + if (refID == -1) { continue; } @@ -512,6 +516,14 @@ bool LoadConfig() configArmorOverrideMap[conf.first].config[boneIter.first][settingIter.first] = settingIter.second; } } + + if (configActorOverrideMap[conf.first].config.count(boneIter.first) > 0) + { + for (auto settingIter : boneIter.second) + { + configActorOverrideMap[conf.first].config[boneIter.first][settingIter.first] = settingIter.second; + } + } } } @@ -528,6 +540,11 @@ bool LoadConfig() { configArmorOverrideMap[0].config[boneName]; } + + if (configActorOverrideMap[0].config.find(boneName) == configActorOverrideMap[0].config.end()) + { + configActorOverrideMap[0].config[boneName]; + } } } @@ -536,6 +553,8 @@ bool LoadConfig() priorities.insert(map.second); } + DumpConfigToLog(); + logger.Error("Finished CBP Config\n"); return reloadActors; } @@ -556,7 +575,7 @@ void DumpConfigToLog() logger.Info("***** ConfigArmorOverride Dump *****\n"); for (auto conf : configArmorOverrideMap) { - logger.Info("** Slot-Armor Map priority %ul **\n", conf.first); + logger.Info("** Slot-Armor Map priority %d **\n", conf.first); logger.Info("[Slots]\n"); for (auto slot : conf.second.slots) { @@ -567,7 +586,27 @@ void DumpConfigToLog() { logger.Info("%ul\n", formID); } - logger.Info("** Config priority %ul **\n", conf.first); + logger.Info("** Config priority %d **\n", conf.first); + for (auto section : conf.second.config) + { + logger.Info("[%s]\n", section.first.c_str()); + for (auto setting : section.second) + { + logger.Info("%s=%f\n", setting.first.c_str(), setting.second); + } + } + } + + logger.Info("***** ConfigActorOverride Dump *****\n"); + for (auto conf : configActorOverrideMap) + { + logger.Info("** Slot-Actor Map priority %d **\n", conf.first); + logger.Info("[Actor]\n"); + for (auto formID : conf.second.actors) + { + logger.Info("%d\n", formID); + } + logger.Info("** Config priority %d **\n", conf.first); for (auto section : conf.second.config) { logger.Info("[%s]\n", section.first.c_str()); From 87956a5df7cf3492140779c4ec3446fb07903bb9 Mon Sep 17 00:00:00 2001 From: ericncream Date: Wed, 30 Nov 2022 04:20:56 -0800 Subject: [PATCH 58/66] Filter fixes. --- CBPSSE/ActorUtils.cpp | 21 ++++++++++++++++----- CBPSSE/config.cpp | 3 ++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index df6468d..e1718ed 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -51,9 +51,9 @@ NiAVObject* actorUtils::GetBaseSkeleton(Actor* actor) bool actorUtils::IsActorPriorityBlacklisted(Actor* actor, UInt32 priority) { - bool result = false; - auto actorOverrideConfigEntry = configActorOverrideMap[priority]; auto isFilterInverted = configActorOverrideMap[priority].isFilterInverted; + bool result = true; + auto actorOverrideConfigEntry = configActorOverrideMap[priority]; auto overrideActors = actorOverrideConfigEntry.actors; if (!isFilterInverted) @@ -261,12 +261,15 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) UInt32 priority = *priIter; // Check if there is no armor slot override entry - if (configArmorOverrideMap.find(priority) == configArmorOverrideMap.end()) + if (configArmorOverrideMap.find(priority) == configArmorOverrideMap.end() + || (configArmorOverrideMap[priority].armors.empty() + && configArmorOverrideMap[priority].slots.empty())) { auto overrideConfig = configActorOverrideMap[priority].config; if (IsActorPriorityWhitelisted(actor, priority)) { + logger.Info("%s: actor %08x is actor whitelisted\n", __func__, actor->formID); for (auto val : overrideConfig) { // for each bone, if it is empty, we need to disable it, @@ -282,8 +285,9 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) } } } - if (!IsActorPriorityBlacklisted(actor, priority)) + else if (!IsActorPriorityBlacklisted(actor, priority)) { + logger.Info("%s: actor %08x is not actor blacklisted\n", __func__, actor->formID); for (auto val : overrideConfig) { // for each bone, if it is empty, we need to disable it, @@ -299,16 +303,21 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) } } } + else + { + logger.Info("%s: actor %08x is not filtered for priority %d\n", __func__, actor->formID, priority); + } } else // There is an armor slot override entry { // If priority level has an entry in actor override map AND // actor is not whitelisted or is blacklisted, continue on - if (configActorOverrideMap.find(priority) == configActorOverrideMap.end()) + if (configActorOverrideMap.find(priority) != configActorOverrideMap.end()) { if (!IsActorPriorityWhitelisted(actor, priority) || IsActorPriorityBlacklisted(actor, priority)) { + logger.Info("%s: actor %08x is filtered from armors\n", __func__, actor->formID); continue; } } @@ -331,6 +340,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) // whitelist filter if (orData.isFilterInverted) { + logger.Info("%s: actor %08x is armor whitelisted\n", __func__, actor->formID); for (; armorFormID != orData.armors.end(); ++armorFormID) { bool breakOutside = false; @@ -369,6 +379,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) // blacklist filter if (!orData.isFilterInverted && armorFormID == orData.armors.end() && !equippedList.empty()) { + logger.Info("%s: actor %08x is armor blacklisted\n", __func__, actor->formID); for (auto val : orData.config) { if (orData.config[val.first].empty()) diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 9b2a4f1..64c4798 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -389,7 +389,6 @@ bool LoadConfig() auto refID = GetFormIDFromString(valuesIter.second); - logger.Info("refID %s\n", valuesIter.second); if (refID == -1) { continue; @@ -553,7 +552,9 @@ bool LoadConfig() priorities.insert(map.second); } +#if DEBUG DumpConfigToLog(); +#endif logger.Error("Finished CBP Config\n"); return reloadActors; From 8ace0dc85f4250191a79bc3e3273dc5ceeec6924 Mon Sep 17 00:00:00 2001 From: ericncream Date: Fri, 2 Dec 2022 04:11:04 -0800 Subject: [PATCH 59/66] Updates to composed configs and disabled things. --- CBPSSE/SimObj.cpp | 16 ++++++++++++++-- CBPSSE/Thing.cpp | 2 ++ CBPSSE/Thing.h | 2 ++ CBPSSE/scan.cpp | 4 +++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 64b553e..3b8775c 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -135,7 +135,10 @@ void SimObj::Update(Actor* actor) !IsActorInPowerArmor(actor) && NULL != GetBaseSkeleton(actor)) { - t.second.UpdateThing(actor); + if (t.second.isEnabled) + { + t.second.UpdateThing(actor); + } } } } @@ -147,7 +150,16 @@ bool SimObj::UpdateConfigs(config_t& config) concurrency::parallel_for_each(things.begin(), things.end(), [&](auto& thing) { //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); - thing.second.UpdateConfig(config[std::string(thing.first)]); + + if (config.count(thing.first) > 0) + { + thing.second.UpdateConfig(config[thing.first]); + thing.second.isEnabled = true; + } + else + { + thing.second.isEnabled = false; + } }); return true; } \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 3aa8f47..48bc9f5 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -38,6 +38,8 @@ Thing::Thing(NiAVObject* obj, BSFixedString& name, Actor* actor) , m_actor(actor) { + isEnabled = true; + auto skeletonObj = actorUtils::GetBaseSkeleton(actor); if (skeletonObj == NULL) { diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index 82058c8..fee93f8 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -46,6 +46,8 @@ class Thing Actor* m_actor; public: + bool isEnabled; + float stiffness = 0.5f; float stiffness2 = 0.0f; float damping = 0.2f; diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 9e2aa62..27fcc86 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -215,8 +215,10 @@ void UpdateActors() else { auto& simObj = actorsIterator->second; + auto& composedConfig = BuildConfigForActor(a.actor); + simObj.AddBonesToThings(a.actor, boneNames); - simObj.UpdateConfigs(config); + simObj.UpdateConfigs(composedConfig); } } From 357577c584a7d6f0be76920e41cdf41d26326419 Mon Sep 17 00:00:00 2001 From: ericncream Date: Sun, 4 Dec 2022 04:55:20 -0800 Subject: [PATCH 60/66] Misc changes --- CBPSSE/ActorUtils.cpp | 8 ++++--- CBPSSE/SimObj.cpp | 1 - CBPSSE/config.cpp | 6 ++--- CBPSSE/config.h | 6 ++--- CBPSSE/scan.cpp | 53 +++++++++++++++++++++++-------------------- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index e1718ed..89b8a31 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -162,9 +162,11 @@ bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) } bool result; auto raceEID = actorUtils::GetActorRaceEID(actor); - if (whitelist.find(boneName) != whitelist.end()) + auto whitelist_bone = whitelist.find(boneName); + if (whitelist_bone != whitelist.end()) { - auto racesMap = whitelist.at(boneName); + auto racesMap = whitelist_bone->second; + if (racesMap.find(raceEID) != racesMap.end()) { if (IsActorMale(actor)) @@ -312,7 +314,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { // If priority level has an entry in actor override map AND // actor is not whitelisted or is blacklisted, continue on - if (configActorOverrideMap.find(priority) != configActorOverrideMap.end()) + if (configActorOverrideMap.count(priority) > 0) { if (!IsActorPriorityWhitelisted(actor, priority) || IsActorPriorityBlacklisted(actor, priority)) diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 3b8775c..17d05d3 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -146,7 +146,6 @@ void SimObj::Update(Actor* actor) bool SimObj::UpdateConfigs(config_t& config) { logger.Error("%s\n", __func__); - // TODO does this need parallelization? concurrency::parallel_for_each(things.begin(), things.end(), [&](auto& thing) { //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 64c4798..99f063e 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -28,11 +28,11 @@ bool npcOnly = false; bool useWhitelist = false; config_t config; -std::map configArmorOverrideMap; -std::unordered_set usedSlots; +concurrency::concurrent_unordered_map configArmorOverrideMap; concurrency::concurrent_unordered_map configActorOverrideMap; -std::map, config_t> cachedConfigs; std::unordered_map priorityNameMappings; +std::unordered_set usedSlots; +std::map, config_t> cachedConfigs; std::set priorities; // TODO data structure these diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 9d2b1bb..817b64d 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -22,7 +22,7 @@ struct whitelistSex typedef concurrency::concurrent_unordered_map configEntry_t; // Map settings for a particular bone typedef concurrency::concurrent_unordered_map config_t; // Settings for a set of bones -typedef std::unordered_map> whitelist_t; +typedef concurrency::concurrent_unordered_map> whitelist_t; struct armorOverrideData { @@ -47,13 +47,13 @@ extern bool useWhitelist; extern int configReloadCount; extern config_t config; -extern std::map configArmorOverrideMap; +extern concurrency::concurrent_unordered_map configArmorOverrideMap; +extern concurrency::concurrent_unordered_map configActorOverrideMap; extern whitelist_t whitelist; extern std::vector raceWhitelist; extern std::unordered_set usedSlots; extern std::map, config_t> cachedConfigs; extern std::set priorities; -extern concurrency::concurrent_unordered_map configActorOverrideMap; bool LoadConfig(); void DumpWhitelistToLog(); \ No newline at end of file diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 27fcc86..6abbdce 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -231,38 +231,38 @@ void UpdateActors() } //logger.error("Updating %d entities\n", actorEntries.size()); - for (auto& a : actorEntries) - { - auto actorsIterator = actors.find(a.id); - if (actorsIterator == actors.end()) - { - //logger.error("Sim Object not found in tracked actors\n"); - } - else + concurrency::parallel_for_each(actorEntries.begin(), actorEntries.end(), [&](const auto& a) { - auto& simObj = actorsIterator->second; + auto actorsIterator = actors.find(a.id); + if (actorsIterator == actors.end()) + { + //logger.error("Sim Object not found in tracked actors\n"); + } + else + { + auto& simObj = actorsIterator->second; - auto& composedConfig = BuildConfigForActor(a.actor); + auto& composedConfig = BuildConfigForActor(a.actor); - SimObj::Gender gender = IsActorMale(a.actor) ? SimObj::Gender::Male : SimObj::Gender::Female; + SimObj::Gender gender = IsActorMale(a.actor) ? SimObj::Gender::Male : SimObj::Gender::Female; - // Pointer comparison is good enough? - // OR check if gender and/or race have changed - if (simObj.IsBound()) - { - if (gender != simObj.GetGender() || - GetActorRaceEID(a.actor) != simObj.GetRaceEID()) + // Pointer comparison is good enough? + // OR check if gender and/or race have changed + if (simObj.IsBound()) { - logger.Info("%s: Reset sim object\n", __func__); - simObj.Reset(); + if (gender != simObj.GetGender() || + GetActorRaceEID(a.actor) != simObj.GetRaceEID()) + { + logger.Info("%s: Reset sim object\n", __func__); + simObj.Reset(); + } + } + else + { + simObj.Bind(a.actor, boneNames, composedConfig); } } - else - { - simObj.Bind(a.actor, boneNames, composedConfig); - } - } - } + }); concurrency::parallel_for_each(actorEntries.begin(), actorEntries.end(), [&](const auto& a) { @@ -277,6 +277,9 @@ void UpdateActors() if (simObj.IsBound()) { + auto& composedConfig = BuildConfigForActor(a.actor); + + simObj.UpdateConfigs(composedConfig); simObj.Update(a.actor); } } From d0a79e934927af78dbde3434beebe260a8e9350b Mon Sep 17 00:00:00 2001 From: ericncream Date: Sat, 24 Dec 2022 05:28:42 -0800 Subject: [PATCH 61/66] WIP speed ups. Switch compilation to C++ 17. --- CBPSSE/ActorUtils.cpp | 69 +- CBPSSE/ActorUtils.h | 5 +- CBPSSE/CBPSSE.vcxproj | 2 + CBPSSE/CBPSSE.vcxproj.filters | 3 + CBPSSE/PapyrusOCBP.cpp | 5 +- CBPSSE/PapyrusOCBP.h | 6 +- CBPSSE/SimObj.cpp | 68 +- CBPSSE/SimObj.h | 4 +- CBPSSE/Thing.cpp | 14 +- CBPSSE/Thing.h | 8 +- CBPSSE/config.cpp | 76 +- CBPSSE/config.h | 16 +- CBPSSE/scan.cpp | 194 ++-- CBPSSE/unordered_dense.h | 1527 ++++++++++++++++++++++++++ common/common_vc14.vcxproj | 1 + f4se/f4se/f4se.vcxproj | 1 + f4se/f4se_common/f4se_common.vcxproj | 1 + 17 files changed, 1823 insertions(+), 177 deletions(-) create mode 100644 CBPSSE/unordered_dense.h diff --git a/CBPSSE/ActorUtils.cpp b/CBPSSE/ActorUtils.cpp index 89b8a31..096b4e4 100644 --- a/CBPSSE/ActorUtils.cpp +++ b/CBPSSE/ActorUtils.cpp @@ -1,7 +1,9 @@ #include +#include #include "ActorUtils.h" #include "log.h" +#include "unordered_dense.h" #include "f4se/GameExtraData.h" #include "f4se/GameObjects.h" @@ -53,8 +55,8 @@ bool actorUtils::IsActorPriorityBlacklisted(Actor* actor, UInt32 priority) { auto isFilterInverted = configActorOverrideMap[priority].isFilterInverted; bool result = true; - auto actorOverrideConfigEntry = configActorOverrideMap[priority]; - auto overrideActors = actorOverrideConfigEntry.actors; + auto & actorOverrideConfigEntry = configActorOverrideMap[priority]; + auto & overrideActors = actorOverrideConfigEntry.actors; if (!isFilterInverted) { @@ -86,9 +88,9 @@ bool actorUtils::IsActorMale(Actor* actor) bool actorUtils::IsActorPriorityWhitelisted(Actor* actor, UInt32 priority) { bool result = false; - auto actorOverrideConfigEntry = configActorOverrideMap[priority]; + auto& actorOverrideConfigEntry = configActorOverrideMap[priority]; auto isFilterInverted = configActorOverrideMap[priority].isFilterInverted; - auto overrideActors = actorOverrideConfigEntry.actors; + auto& overrideActors = actorOverrideConfigEntry.actors; if (isFilterInverted) { @@ -104,12 +106,12 @@ bool actorUtils::IsActorInPowerArmor(Actor* actor) if (!IsActorValid(actor)) { logger.Info("IsActorInPowerArmor: no actor!\n"); - return true; // will force game to not call Update + return true; } if (!actor->extraDataList) { logger.Info("IsActorInPowerArmor: no extraDataList!\n"); - return true; // will force game to not call Update + return true; } isInPowerArmor = actor->extraDataList->HasType(kExtraData_PowerArmor); @@ -145,7 +147,7 @@ bool actorUtils::IsActorValid(Actor* actor) logger.Info("%s: actor %x has deleted flag\n", __func__, actor->formID); return false; } - if (actor && actor->unkF0 && actor->unkF0->rootNode) + if (actor->unkF0 && actor->unkF0->rootNode) { return true; } @@ -165,7 +167,7 @@ bool actorUtils::IsBoneInWhitelist(Actor* actor, std::string boneName) auto whitelist_bone = whitelist.find(boneName); if (whitelist_bone != whitelist.end()) { - auto racesMap = whitelist_bone->second; + auto & racesMap = whitelist_bone->second; if (racesMap.find(raceEID) != racesMap.end()) { @@ -219,12 +221,16 @@ const actorUtils::EquippedArmor actorUtils::GetActorEquippedArmor(Actor* actor, return actorUtils::EquippedArmor{ nullptr, nullptr }; } -config_t actorUtils::BuildConfigForActor(Actor* actor) +UInt64 actorUtils::BuildActorKey(Actor* actor) { - std::multiset key; + std::unordered_map key; + ankerl::unordered_dense::hash hash; + UInt64 hashKey = 0; + UInt64 actorFormID = (UInt64)actor->formID; // key always starts with actors formID (really the refID) - key.emplace((UInt64)actor->formID); + key[actorFormID] = 1; + hashKey = hash((UInt64)actor->formID); for (auto slot : usedSlots) { @@ -243,13 +249,29 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) data |= equipped.model->formID; } if (data) - { - key.emplace(data); + { + auto valIter = key.find(data); + if (valIter != key.end()) + { + key[data] = key[data] + 1; + hashKey += hash(data) * key[data]; + } + else + { + key[data] = 1; + } } } + //logger.Info("%s: actor %08x has hash key 0x%x\n", __func__, actor->formID, hashKey); + + return hashKey; +} + +config_t actorUtils::BuildConfigForActor(Actor* actor, UInt64 hashKey) +{ // Search cached configs for already existing config - auto found = cachedConfigs.find(key); + auto found = cachedConfigs.find(hashKey); if (found != cachedConfigs.end()) { return found->second; @@ -267,12 +289,12 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) || (configArmorOverrideMap[priority].armors.empty() && configArmorOverrideMap[priority].slots.empty())) { - auto overrideConfig = configActorOverrideMap[priority].config; + auto & overrideConfig = configActorOverrideMap[priority].config; if (IsActorPriorityWhitelisted(actor, priority)) { logger.Info("%s: actor %08x is actor whitelisted\n", __func__, actor->formID); - for (auto val : overrideConfig) + for (auto & val : overrideConfig) { // for each bone, if it is empty, we need to disable it, // otherwise the configEntry is good. @@ -290,7 +312,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) else if (!IsActorPriorityBlacklisted(actor, priority)) { logger.Info("%s: actor %08x is not actor blacklisted\n", __func__, actor->formID); - for (auto val : overrideConfig) + for (auto & val : overrideConfig) { // for each bone, if it is empty, we need to disable it, // otherwise the configEntry is good. @@ -324,7 +346,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) } } - auto orData = configArmorOverrideMap[priority]; + auto & orData = configArmorOverrideMap[priority]; std::vector equippedList; @@ -347,11 +369,11 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) { bool breakOutside = false; // Check config's filter IDs against found slot's IDs - for (auto equipped : equippedList) + for (auto & equipped : equippedList) { if (*armorFormID == equipped.armor->formID || *armorFormID == equipped.model->formID) { - for (auto val : orData.config) + for (auto & val : orData.config) { // for each bone, if it is empty, we need to disable it, // otherwise the configEntry is good. @@ -382,7 +404,7 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) if (!orData.isFilterInverted && armorFormID == orData.armors.end() && !equippedList.empty()) { logger.Info("%s: actor %08x is armor blacklisted\n", __func__, actor->formID); - for (auto val : orData.config) + for (auto & val : orData.config) { if (orData.config[val.first].empty()) { @@ -398,6 +420,9 @@ config_t actorUtils::BuildConfigForActor(Actor* actor) } } - cachedConfigs[key] = baseConfig; + //logger.Info("%s: Make new key\n", __func__); + //logger.Info("%s: Caching config\n", __func__); + cachedConfigs.emplace(hashKey, baseConfig); + //logger.Info("%s: exiting\n", __func__); return baseConfig; } \ No newline at end of file diff --git a/CBPSSE/ActorUtils.h b/CBPSSE/ActorUtils.h index 415658a..5fe7a81 100644 --- a/CBPSSE/ActorUtils.h +++ b/CBPSSE/ActorUtils.h @@ -18,11 +18,10 @@ namespace actorUtils bool IsActorMale(Actor* actor); bool IsActorTrackable(Actor* actor); bool IsActorValid(Actor* actor); - bool IsArmorPriorityBlacklisted(Actor* actor, UInt32 priority); bool IsActorInPowerArmor(Actor* actor); - bool IsArmorPriorityWhitelisted(Actor* actor, UInt32 priority); bool IsBoneInWhitelist(Actor* actor, std::string boneName); const EquippedArmor GetActorEquippedArmor(Actor* actor, UInt32 slot); - config_t BuildConfigForActor(Actor* actor); + UInt64 BuildActorKey(Actor* actor); + config_t BuildConfigForActor(Actor* actor, UInt64 hashKey); } \ No newline at end of file diff --git a/CBPSSE/CBPSSE.vcxproj b/CBPSSE/CBPSSE.vcxproj index 7d569aa..deff902 100644 --- a/CBPSSE/CBPSSE.vcxproj +++ b/CBPSSE/CBPSSE.vcxproj @@ -135,6 +135,7 @@ _USRDLL;PLUGIN_EXAMPLE_EXPORTS;RUNTIME;RUNTIME_VERSION=0x010A0A30;%(PreprocessorDefinitions) MultiThreaded common/IPrefix.h;%(ForcedIncludeFiles) + stdcpp17 true @@ -177,6 +178,7 @@ + diff --git a/CBPSSE/CBPSSE.vcxproj.filters b/CBPSSE/CBPSSE.vcxproj.filters index 17564cc..bd9ce33 100644 --- a/CBPSSE/CBPSSE.vcxproj.filters +++ b/CBPSSE/CBPSSE.vcxproj.filters @@ -93,6 +93,9 @@ Header Files\api + + Header Files + diff --git a/CBPSSE/PapyrusOCBP.cpp b/CBPSSE/PapyrusOCBP.cpp index d9acfdb..a7327c7 100644 --- a/CBPSSE/PapyrusOCBP.cpp +++ b/CBPSSE/PapyrusOCBP.cpp @@ -1,3 +1,5 @@ +#pragma warning(disable : 5040) + #include "PapyrusOCBP.h" //#include "f4se/PapyrusVM.h" @@ -17,10 +19,11 @@ #include "SimObj.h" -std::unordered_map> boneIgnores; namespace papyrusOCBP { + concurrency::concurrent_unordered_map> boneIgnores; + void SetBoneToggle(StaticFunctionTag*, Actor* actor, bool toggle, BSFixedString boneName) { boneIgnores[actor->formID][std::string(boneName.c_str())] = toggle; diff --git a/CBPSSE/PapyrusOCBP.h b/CBPSSE/PapyrusOCBP.h index 2ae30f7..1eac986 100644 --- a/CBPSSE/PapyrusOCBP.h +++ b/CBPSSE/PapyrusOCBP.h @@ -2,15 +2,15 @@ #include "f4se/GameTypes.h" #include "f4se/PapyrusVM.h" -#include #include +#include + class VirtualMachine; struct StaticFunctionTag; -extern std::unordered_map> boneIgnores; // probably should be moved somewhere else - namespace papyrusOCBP { + extern concurrency::concurrent_unordered_map> boneIgnores; void RegisterFuncs(VirtualMachine* vm); }; \ No newline at end of file diff --git a/CBPSSE/SimObj.cpp b/CBPSSE/SimObj.cpp index 17d05d3..2288ad9 100644 --- a/CBPSSE/SimObj.cpp +++ b/CBPSSE/SimObj.cpp @@ -1,3 +1,5 @@ +#pragma warning(disable : 5040) + #include "f4se/NiNodes.h" #include "f4se/GameForms.h" #include "f4se/GameRTTI.h" @@ -11,6 +13,7 @@ using actorUtils::GetBaseSkeleton; using actorUtils::IsBoneInWhitelist; using actorUtils::IsActorInPowerArmor; +using papyrusOCBP::boneIgnores; // Note we don't ref count the nodes becasue it's ignored when the Actor is deleted, and calling Release after that can corrupt memory std::vector boneNames; @@ -92,6 +95,11 @@ bool SimObj::Bind(Actor* actor, std::vector& boneNames, config_t& c return false; } +UInt64 SimObj::GetActorKey() +{ + return currentActorKey; +} + SimObj::Gender SimObj::GetGender() { return gender; @@ -108,22 +116,33 @@ void SimObj::Reset() things.clear(); } +void SimObj::SetActorKey(UInt64 key) +{ + currentActorKey = key; +} + void SimObj::Update(Actor* actor) { - if (!bound) + if (!bound || + IsActorInPowerArmor(actor) || + NULL == GetBaseSkeleton(actor)) + { return; + } + //concurrency::parallel_for_each(things.begin(), things.end(), [&](auto& t) + //{ for (auto& t : things) { - //logger.Info("SimObj update: doing thing %s\n", t.first.c_str()); - // Might be a better way to do this - if (boneIgnores.find(actor->formID) != boneIgnores.end()) + auto actorBoneMapIter = boneIgnores.find(actor->formID); + if (actorBoneMapIter != boneIgnores.end()) { - auto actorBoneMap = boneIgnores.at(actor->formID); - if (actorBoneMap.find(t.first) != actorBoneMap.end()) + auto & actorBoneMap = actorBoneMapIter->second; + auto boneDisabledIter = actorBoneMap.find(t.first); + if (boneDisabledIter != actorBoneMap.end()) { - if (actorBoneMap.at(t.first)) + if (true == boneDisabledIter->second) { continue; } @@ -131,9 +150,7 @@ void SimObj::Update(Actor* actor) } if (!useWhitelist || - (IsBoneInWhitelist(actor, t.first) && useWhitelist) && - !IsActorInPowerArmor(actor) && - NULL != GetBaseSkeleton(actor)) + (IsBoneInWhitelist(actor, t.first) && useWhitelist)) { if (t.second.isEnabled) { @@ -141,24 +158,27 @@ void SimObj::Update(Actor* actor) } } } + //}); } bool SimObj::UpdateConfigs(config_t& config) { - logger.Error("%s\n", __func__); - concurrency::parallel_for_each(things.begin(), things.end(), [&](auto& thing) - { - //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); + for (auto & thing : things) + { + //concurrency::parallel_for_each(things.begin(), things.end(), [&](auto& thing) + // { + //logger.Info("%s: Updating config for Thing %s\n", __func__, thing.first.c_str()); - if (config.count(thing.first) > 0) - { - thing.second.UpdateConfig(config[thing.first]); - thing.second.isEnabled = true; - } - else - { - thing.second.isEnabled = false; - } - }); + if (config.count(thing.first) > 0) + { + thing.second.UpdateConfig(config[thing.first]); + thing.second.isEnabled = true; + } + else + { + thing.second.isEnabled = false; + } + //}); + } return true; } \ No newline at end of file diff --git a/CBPSSE/SimObj.h b/CBPSSE/SimObj.h index ba2bced..ef9a5dc 100644 --- a/CBPSSE/SimObj.h +++ b/CBPSSE/SimObj.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include "amp.h" #include @@ -26,9 +25,11 @@ class SimObj ~SimObj(); bool AddBonesToThings(Actor* actor, std::vector& boneNames); bool Bind(Actor* actor, std::vector& boneNames, config_t& config); + UInt64 GetActorKey(); Gender GetGender(); std::string GetRaceEID(); void Reset(); + void SetActorKey(UInt64 key); void Update(Actor* actor); bool UpdateConfigs(config_t& config); bool IsBound() { return bound; } @@ -37,6 +38,7 @@ class SimObj bool bound = false; Gender gender; std::string raceEid; + UInt64 currentActorKey; }; extern std::vector boneNames; \ No newline at end of file diff --git a/CBPSSE/Thing.cpp b/CBPSSE/Thing.cpp index 48bc9f5..894bfd0 100644 --- a/CBPSSE/Thing.cpp +++ b/CBPSSE/Thing.cpp @@ -17,7 +17,7 @@ pos_map Thing::origLocalPos; rot_map Thing::origLocalRot; rot_map Thing::origChestWorldRot; -shared_mutex Thing::thing_map_lock; +//shared_mutex Thing::thing_map_lock; void Thing::ShowPos(NiPoint3& p) { @@ -112,7 +112,7 @@ void Thing::StoreOriginalTransforms(Actor* actor) auto obj = thingObj; - thing_map_lock.lock(); + //thing_map_lock.lock(); if (IsBreastBone) { BSFixedString chest_name("Chest"); @@ -185,7 +185,7 @@ void Thing::StoreOriginalTransforms(Actor* actor) #endif } } - thing_map_lock.unlock(); + //thing_map_lock.unlock(); } void Thing::UpdateConfig(configEntry_t& centry) @@ -289,26 +289,26 @@ NiAVObject* Thing::IsThingActorValid(Actor* actor) { if (!actorUtils::IsActorValid(actor)) { - logger.Error("%s: No valid actor in Thing::Update\n", __func__); + //logger.Error("%s: No valid actor in Thing::Update\n", __func__); return NULL; } auto loadedState = actor->unkF0; if (!loadedState || !loadedState->rootNode) { - logger.Error("%s: No loaded state for actor %08x\n", __func__, actor->formID); + //logger.Error("%s: No loaded state for actor %08x\n", __func__, actor->formID); return NULL; } auto obj = loadedState->rootNode->GetObjectByName(&boneName); if (!obj) { - logger.Error("%s: Couldn't get name for loaded state for actor %08x\n", __func__, actor->formID); + //logger.Error("%s: Couldn't get name for loaded state for actor %08x\n", __func__, actor->formID); return NULL; } if (!obj->m_parent) { - logger.Error("%s: Couldn't get bone %s parent for actor %08x\n", __func__, boneName.c_str(), actor->formID); + //logger.Error("%s: Couldn't get bone %s parent for actor %08x\n", __func__, boneName.c_str(), actor->formID); return NULL; } diff --git a/CBPSSE/Thing.h b/CBPSSE/Thing.h index fee93f8..6f9776c 100644 --- a/CBPSSE/Thing.h +++ b/CBPSSE/Thing.h @@ -6,10 +6,10 @@ #include "config.h" #include -using std::shared_mutex; +//using std::shared_mutex; -typedef concurrency::concurrent_unordered_map> pos_map; -typedef concurrency::concurrent_unordered_map> rot_map; +typedef concurrency::concurrent_unordered_map> pos_map; +typedef concurrency::concurrent_unordered_map> rot_map; inline void RefreshNode(NiAVObject* node) { @@ -100,7 +100,7 @@ class Thing static rot_map origChestWorldRot; // Maps are sorted every edit time, so if it is parallel processing then a high probability of overloading - static shared_mutex thing_map_lock; + //static shared_mutex thing_map_lock; Thing(NiAVObject* obj, BSFixedString& name, Actor* actor); ~Thing(); diff --git a/CBPSSE/config.cpp b/CBPSSE/config.cpp index 99f063e..56bca01 100644 --- a/CBPSSE/config.cpp +++ b/CBPSSE/config.cpp @@ -1,3 +1,5 @@ +#pragma warning(disable : 5040) + #include "config.h" #include "INIReader.h" #include "log.h" @@ -32,7 +34,7 @@ concurrency::concurrent_unordered_map configArmorOver concurrency::concurrent_unordered_map configActorOverrideMap; std::unordered_map priorityNameMappings; std::unordered_set usedSlots; -std::map, config_t> cachedConfigs; +std::map cachedConfigs; std::set priorities; // TODO data structure these @@ -150,7 +152,7 @@ bool LoadConfig() configReloadCount = configReader.GetInteger("Tuning", "rate", 0); // Read sections - auto sections = configReader.Sections(); + auto & sections = configReader.Sections(); auto prioritySection = sections.find("Priority"); bool detectArmorCompat = configReader.GetBoolean("General", "detectArmor", false) && (prioritySection == sections.end() || configReader.Section(*prioritySection).empty()); @@ -158,14 +160,14 @@ bool LoadConfig() if (detectArmorCompat) { priorityNameMappings["A"] = 0; - configArmorOverrideMap[0].slots.emplace(11); + configArmorOverrideMap[0].slots.insert(11); usedSlots.emplace(11); configArmorOverrideMap[0].isFilterInverted = false; //Read armorIgnore auto armorIgnoreStr = configReader.Get("General", "armorIgnore", ""); { - size_t commaPos; + size_t commaPos = 0; do { commaPos = armorIgnoreStr.find_first_of(","); @@ -174,7 +176,7 @@ bool LoadConfig() try { UInt32 formID = std::stoul(token); - configArmorOverrideMap[0].armors.emplace(formID); + configArmorOverrideMap[0].armors.insert(formID); } catch (const std::exception&) {} @@ -205,7 +207,7 @@ bool LoadConfig() if (*sectionsIter == std::string("Attach")) { // Get section contents - auto sectionMap = configReader.Section(*sectionsIter); + auto & sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& boneName = valuesIter.first; @@ -214,7 +216,7 @@ bool LoadConfig() // Find specified bone section and insert map values into config if (sections.find(attachName) != sections.end()) { - auto attachMapSection = configReader.Section(attachName); + auto & attachMapSection = configReader.Section(attachName); for (auto& attachIter : attachMapSection) { auto& keyName = attachIter.first; @@ -250,7 +252,7 @@ bool LoadConfig() } // Finally read the "Attach." section - auto sectionMap = configReader.Section(*sectionsIter); + auto & sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& boneName = valuesIter.first; @@ -266,7 +268,7 @@ bool LoadConfig() } else if (sections.find(attachName) != sections.end()) { - auto attachMapSection = configReader.Section(attachName); + auto & attachMapSection = configReader.Section(attachName); // Find the bone's settings and add them to configArmorOverrideMap for (auto& attachIter : attachMapSection) { @@ -308,7 +310,7 @@ bool LoadConfig() continue; } - size_t commaPos; + size_t commaPos = 0; do { commaPos = slots.find_first_of(","); @@ -325,14 +327,14 @@ bool LoadConfig() continue; } - configArmorOverrideMap[armorPriority].slots.emplace(slot - 30); + configArmorOverrideMap[armorPriority].slots.insert(slot - 30); usedSlots.emplace(slot - 30); } while (commaPos != std::string::npos); configArmorOverrideMap[armorPriority].isFilterInverted = configReader.GetBoolean(*sectionsIter, "invertFilter", false); // Get section contents - auto sectionMap = configReader.Section(*sectionsIter); + auto & sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& key = valuesIter.first; @@ -347,7 +349,7 @@ bool LoadConfig() continue; } - configArmorOverrideMap[armorPriority].armors.emplace(formID); + configArmorOverrideMap[armorPriority].armors.insert(formID); } } else if (splitActorStr.first == actorStr.end()) @@ -378,7 +380,7 @@ bool LoadConfig() configActorOverrideMap[actorPriority].isFilterInverted = configReader.GetBoolean(*sectionsIter, "invertFilter", false); // Get section contents - auto sectionMap = configReader.Section(*sectionsIter); + auto & sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& key = valuesIter.first; @@ -394,7 +396,7 @@ bool LoadConfig() continue; } - configActorOverrideMap[actorPriority].actors.emplace(refID); + configActorOverrideMap[actorPriority].actors.insert(refID); } } else if (*sectionsIter == std::string("Whitelist") && useWhitelist) @@ -403,13 +405,13 @@ bool LoadConfig() raceWhitelist.clear(); // Get section contents - auto sectionMap = configReader.Section(*sectionsIter); + auto & sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIter : sectionMap) { auto& boneName = valuesIter.first; - auto& whitelistName = valuesIter.second; + std::string whitelistName = valuesIter.second; - size_t commaPos; + size_t commaPos = 0; do { commaPos = whitelistName.find_first_of(","); @@ -446,7 +448,7 @@ bool LoadConfig() auto boneName = std::string(splitOverrideStr.second, sectionsIter->end()); // Get section contents - auto sectionMap = configReader.Section(*sectionsIter); + auto & sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIt : sectionMap) { configOverrides[boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); @@ -483,7 +485,7 @@ bool LoadConfig() } // Get section contents - auto sectionMap = configReader.Section(*sectionsIter); + auto & sectionMap = configReader.Section(*sectionsIter); for (auto& valuesIt : sectionMap) { configArmorBoneOverrides[overridePriority][boneName][valuesIt.first] = configReader.GetFloat(*sectionsIter, valuesIt.first, 0.0); @@ -496,7 +498,7 @@ bool LoadConfig() { if (config.count(boneIter.first) > 0) { - for (auto settingIter : boneIter.second) + for (auto & settingIter : boneIter.second) { config[boneIter.first][settingIter.first] = settingIter.second; } @@ -504,13 +506,13 @@ bool LoadConfig() } // replace armor configs with override settings (if any) - for (auto conf : configArmorBoneOverrides) + for (auto & conf : configArmorBoneOverrides) { for (auto& boneIter : conf.second) { if (configArmorOverrideMap[conf.first].config.count(boneIter.first) > 0) { - for (auto settingIter : boneIter.second) + for (auto & settingIter : boneIter.second) { configArmorOverrideMap[conf.first].config[boneIter.first][settingIter.first] = settingIter.second; } @@ -518,7 +520,7 @@ bool LoadConfig() if (configActorOverrideMap[conf.first].config.count(boneIter.first) > 0) { - for (auto settingIter : boneIter.second) + for (auto & settingIter : boneIter.second) { configActorOverrideMap[conf.first].config[boneIter.first][settingIter.first] = settingIter.second; } @@ -533,7 +535,7 @@ bool LoadConfig() // "Delete" bones specified in [Attach] but not [Attach.A] from the latter for compatibility with presets that remove breast bone jiggle when chest armor equipped if (detectArmorCompat) { - for (auto boneName : boneNames) + for (auto & boneName : boneNames) { if (configArmorOverrideMap[0].config.find(boneName) == configArmorOverrideMap[0].config.end()) { @@ -547,7 +549,7 @@ bool LoadConfig() } } - for (auto map : priorityNameMappings) + for (auto & map : priorityNameMappings) { priorities.insert(map.second); } @@ -564,17 +566,17 @@ void DumpConfigToLog() { // Log contents of config logger.Info("***** Config Dump *****\n"); - for (auto section : config) + for (auto & section : config) { logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) + for (auto & setting : section.second) { logger.Info("%s=%f\n", setting.first.c_str(), setting.second); } } logger.Info("***** ConfigArmorOverride Dump *****\n"); - for (auto conf : configArmorOverrideMap) + for (auto & conf : configArmorOverrideMap) { logger.Info("** Slot-Armor Map priority %d **\n", conf.first); logger.Info("[Slots]\n"); @@ -588,10 +590,10 @@ void DumpConfigToLog() logger.Info("%ul\n", formID); } logger.Info("** Config priority %d **\n", conf.first); - for (auto section : conf.second.config) + for (auto & section : conf.second.config) { logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) + for (auto & setting : section.second) { logger.Info("%s=%f\n", setting.first.c_str(), setting.second); } @@ -599,19 +601,19 @@ void DumpConfigToLog() } logger.Info("***** ConfigActorOverride Dump *****\n"); - for (auto conf : configActorOverrideMap) + for (auto & conf : configActorOverrideMap) { logger.Info("** Slot-Actor Map priority %d **\n", conf.first); logger.Info("[Actor]\n"); - for (auto formID : conf.second.actors) + for (auto & formID : conf.second.actors) { logger.Info("%d\n", formID); } logger.Info("** Config priority %d **\n", conf.first); - for (auto section : conf.second.config) + for (auto & section : conf.second.config) { logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) + for (auto & setting : section.second) { logger.Info("%s=%f\n", setting.first.c_str(), setting.second); } @@ -622,10 +624,10 @@ void DumpConfigToLog() void DumpWhitelistToLog() { logger.Info("***** Whitelist Dump *****\n"); - for (auto section : whitelist) + for (auto & section : whitelist) { logger.Info("[%s]\n", section.first.c_str()); - for (auto setting : section.second) + for (auto & setting : section.second) { logger.Info("%s= female: %d, male: %d\n", setting.first.c_str(), setting.second.female, setting.second.male); } diff --git a/CBPSSE/config.h b/CBPSSE/config.h index 817b64d..8949de8 100644 --- a/CBPSSE/config.h +++ b/CBPSSE/config.h @@ -1,5 +1,4 @@ #pragma once -#include #include #include #include @@ -7,8 +6,12 @@ #include #include +#include #include "f4se/GameReferences.h" +#include "unordered_dense.h" + +#pragma warning(disable : 4996) class Configuration { @@ -22,20 +25,21 @@ struct whitelistSex typedef concurrency::concurrent_unordered_map configEntry_t; // Map settings for a particular bone typedef concurrency::concurrent_unordered_map config_t; // Settings for a set of bones -typedef concurrency::concurrent_unordered_map> whitelist_t; +typedef concurrency::concurrent_unordered_map> whitelist_t; + struct armorOverrideData { bool isFilterInverted; - std::unordered_set slots; - std::unordered_set armors; + concurrency::concurrent_unordered_set slots; + concurrency::concurrent_unordered_set armors; config_t config; }; struct actorOverrideData { bool isFilterInverted; - std::unordered_set actors; + concurrency::concurrent_unordered_set actors; config_t config; }; @@ -52,7 +56,7 @@ extern concurrency::concurrent_unordered_map configAc extern whitelist_t whitelist; extern std::vector raceWhitelist; extern std::unordered_set usedSlots; -extern std::map, config_t> cachedConfigs; +extern std::map cachedConfigs; extern std::set priorities; bool LoadConfig(); diff --git a/CBPSSE/scan.cpp b/CBPSSE/scan.cpp index 6abbdce..b43129f 100644 --- a/CBPSSE/scan.cpp +++ b/CBPSSE/scan.cpp @@ -1,6 +1,10 @@ #pragma once +#pragma warning(disable : 5040) + #define DEBUG +//#define SIMPLE_BENCHMARK + #include #include @@ -20,7 +24,6 @@ #include #include -#include #include #include @@ -49,6 +52,7 @@ using actorUtils::IsActorMale; using actorUtils::IsActorTrackable; using actorUtils::IsActorValid; +using actorUtils::BuildActorKey; using actorUtils::BuildConfigForActor; using actorUtils::GetActorRaceEID; @@ -56,6 +60,14 @@ std::atomic currCell = nullptr; extern F4SETaskInterface* g_task; +#ifdef SIMPLE_BENCHMARK +bool firsttimeloginit = true; +LARGE_INTEGER startingTime, endingTime, elapsedMicroseconds; +LARGE_INTEGER frequency; +LARGE_INTEGER totaltime; +int debugtimelog_framecount = 1; +#endif + //void UpdateWorldDataToChild(NiAVObject) void DumpTransform(NiTransform t) { @@ -130,6 +142,18 @@ TESObjectCELL* curCell = nullptr; void UpdateActors() { +#ifdef SIMPLE_BENCHMARK + if (firsttimeloginit) + { + firsttimeloginit = false; + totaltime.QuadPart = 0; + + } + + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&startingTime); +#endif + // We scan the cell and build the list every time - only look up things by ID once // we retain all state by actor ID, in a map - it's cleared on cell change concurrency::concurrent_vector actorEntries; @@ -151,40 +175,38 @@ void UpdateActors() else { // Attempt to get cell's objects - concurrency::parallel_for(UInt32(0), cell->objectList.count, [&](UInt32 i) + for (UInt32 i = 0; i < cell->objectList.count; i++) + { + auto ref = cell->objectList[i]; + if (ref) { - auto ref = cell->objectList[i]; - if (ref) + // Attempt to get actors + auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); + if (actor && actor->unkF0) { - // Attempt to get actors - auto actor = DYNAMIC_CAST(ref, TESObjectREFR, Actor); - if (actor && actor->unkF0) + // Find if actors is already being tracked + auto soIt = actors.find(actor->formID); + if (soIt == actors.end() && IsActorTrackable(actor)) + { + //logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", + // actor->formID, actor->parentCell->formID, + // actor->race->editorId.c_str(), + // IsActorMale(actor)); + // Make SimObj and place new element in Things + auto obj = SimObj(actor); + actors.insert(std::make_pair(actor->formID, obj)); + actorEntries.push_back(ActorEntry{ actor->formID, actor }); + } + else if (IsActorValid(actor)) { - // Find if actors is already being tracked - auto soIt = actors.find(actor->formID); - if (soIt == actors.end() && IsActorTrackable(actor)) - { - //logger.Info("Tracking Actor with form ID %08x in cell %ld, race is %s, gender is %d\n", - // actor->formID, actor->parentCell->formID, - // actor->race->editorId.c_str(), - // IsActorMale(actor)); - // Make SimObj and place new element in Things - auto obj = SimObj(actor); - if (IsActorValid(actor)) - { - actors.insert(std::make_pair(actor->formID, obj)); - actorEntries.push_back(ActorEntry{ actor->formID, actor }); - } - } - else if (IsActorValid(actor)) - { - actorEntries.push_back(ActorEntry{ actor->formID, actor }); - } + actorEntries.push_back(ActorEntry{ actor->formID, actor }); } } - }); + } + } } + //static bool done = false; //if (!done && player->loadedState->node) { // visitObjects(player->loadedState->node, printStuff); @@ -205,7 +227,7 @@ void UpdateActors() { count = 0; auto reloadActors = LoadConfig(); - for each (auto & a in actorEntries) + for (auto & a : actorEntries) { auto actorsIterator = actors.find(a.id); if (actorsIterator == actors.end()) @@ -215,8 +237,10 @@ void UpdateActors() else { auto& simObj = actorsIterator->second; - auto& composedConfig = BuildConfigForActor(a.actor); + auto key = BuildActorKey(a.actor); + auto& composedConfig = BuildConfigForActor(a.actor, key); + simObj.SetActorKey(key); simObj.AddBonesToThings(a.actor, boneNames); simObj.UpdateConfigs(composedConfig); } @@ -230,62 +254,94 @@ void UpdateActors() } } - //logger.error("Updating %d entities\n", actorEntries.size()); - concurrency::parallel_for_each(actorEntries.begin(), actorEntries.end(), [&](const auto& a) + for (auto & a : actorEntries) + { + auto actorsIterator = actors.find(a.id); + if (actorsIterator == actors.end()) { - auto actorsIterator = actors.find(a.id); - if (actorsIterator == actors.end()) + //logger.error("Sim Object not found in tracked actors\n"); + } + else + { + auto& simObj = actorsIterator->second; + + SimObj::Gender gender = IsActorMale(a.actor) ? SimObj::Gender::Male : SimObj::Gender::Female; + + // Check if gender and/or race have changed + if (simObj.IsBound()) { - //logger.error("Sim Object not found in tracked actors\n"); + if (gender != simObj.GetGender() || + GetActorRaceEID(a.actor) != simObj.GetRaceEID()) + { + logger.Info("UpdateActors: Reset sim object\n"); + simObj.Reset(); + } } else { - auto& simObj = actorsIterator->second; + auto key = BuildActorKey(a.actor); + auto& composedConfig = BuildConfigForActor(a.actor, key); - auto& composedConfig = BuildConfigForActor(a.actor); - - SimObj::Gender gender = IsActorMale(a.actor) ? SimObj::Gender::Male : SimObj::Gender::Female; - - // Pointer comparison is good enough? - // OR check if gender and/or race have changed - if (simObj.IsBound()) - { - if (gender != simObj.GetGender() || - GetActorRaceEID(a.actor) != simObj.GetRaceEID()) - { - logger.Info("%s: Reset sim object\n", __func__); - simObj.Reset(); - } - } - else - { - simObj.Bind(a.actor, boneNames, composedConfig); - } + logger.Error("UpdateActors: Setting key...\n"); + simObj.SetActorKey(key); + simObj.Bind(a.actor, boneNames, composedConfig); } - }); + } + } - concurrency::parallel_for_each(actorEntries.begin(), actorEntries.end(), [&](const auto& a) - { - auto actorsIterator = actors.find(a.id); - if (actorsIterator == actors.end()) + + //for (auto & a : actorEntries) + //{ + concurrency::parallel_for_each(actorEntries.begin(), actorEntries.end(), [&](const auto& a) { - //logger.error("Sim Object not found in tracked actors\n"); - } - else + auto actorsIterator = actors.find(a.id); + if (actorsIterator == actors.end()) + { + //logger.error("Sim Object not found in tracked actors\n"); + } + else + { + auto& simObj = actorsIterator->second; + + if (simObj.IsBound()) { - auto& simObj = actorsIterator->second; + UInt64 key = BuildActorKey(a.actor); + UInt64 simObjKey = simObj.GetActorKey(); - if (simObj.IsBound()) + if (key != simObjKey) { - auto& composedConfig = BuildConfigForActor(a.actor); - + logger.Error("UpdateActors: Key change detected for actor %08x.\n", a.actor->formID); + simObj.SetActorKey(key); + auto& composedConfig = BuildConfigForActor(a.actor, key); simObj.UpdateConfigs(composedConfig); - simObj.Update(a.actor); } + simObj.Update(a.actor); } + } }); + //} + + + +#ifdef SIMPLE_BENCHMARK + QueryPerformanceCounter(&endingTime); + elapsedMicroseconds.QuadPart = endingTime.QuadPart - startingTime.QuadPart; + elapsedMicroseconds.QuadPart *= 1000000000LL; + elapsedMicroseconds.QuadPart /= frequency.QuadPart; + //long long avg = elapsedMicroseconds.QuadPart / callCount; + totaltime.QuadPart += elapsedMicroseconds.QuadPart; + //LOG_ERR("Collider Check Call Count: %d - Update Time = %lld ns", callCount, elapsedMicroseconds.QuadPart); + if (debugtimelog_framecount % 1000 == 0) + { + logger.Error("Average Update Time in 1000 frame = %lld ns\n", totaltime.QuadPart / debugtimelog_framecount); + totaltime.QuadPart = 0; + debugtimelog_framecount = 0; + //totalcallcount = 0; + } + debugtimelog_framecount++; +#endif -FAILED: + FAILED: return; } diff --git a/CBPSSE/unordered_dense.h b/CBPSSE/unordered_dense.h new file mode 100644 index 0000000..d04dfaf --- /dev/null +++ b/CBPSSE/unordered_dense.h @@ -0,0 +1,1527 @@ +///////////////////////// ankerl::unordered_dense::{map, set} ///////////////////////// + +// A fast & densely stored hashmap and hashset based on robin-hood backward shift deletion. +// Version 3.0.0 +// https://github.com/martinus/unordered_dense +// +// Licensed under the MIT License . +// SPDX-License-Identifier: MIT +// Copyright (c) 2022 Martin Leitner-Ankerl +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ANKERL_UNORDERED_DENSE_H +#define ANKERL_UNORDERED_DENSE_H + +#pragma warning(disable : 4003) + +// see https://semver.org/spec/v2.0.0.html +#define ANKERL_UNORDERED_DENSE_VERSION_MAJOR 3 // NOLINT(cppcoreguidelines-macro-usage) incompatible API changes +#define ANKERL_UNORDERED_DENSE_VERSION_MINOR 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible functionality +#define ANKERL_UNORDERED_DENSE_VERSION_PATCH 0 // NOLINT(cppcoreguidelines-macro-usage) backwards compatible bug fixes + +// API versioning with inline namespace, see https://www.foonathan.net/2018/11/inline-namespaces/ +#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) v##major##_##minor##_##patch +#define ANKERL_UNORDERED_DENSE_VERSION_CONCAT(major, minor, patch) ANKERL_UNORDERED_DENSE_VERSION_CONCAT1(major, minor, patch) +#define ANKERL_UNORDERED_DENSE_NAMESPACE \ + ANKERL_UNORDERED_DENSE_VERSION_CONCAT( \ + ANKERL_UNORDERED_DENSE_VERSION_MAJOR, ANKERL_UNORDERED_DENSE_VERSION_MINOR, ANKERL_UNORDERED_DENSE_VERSION_PATCH) + +#if defined(_MSVC_LANG) +# define ANKERL_UNORDERED_DENSE_CPP_VERSION _MSVC_LANG +#else +# define ANKERL_UNORDERED_DENSE_CPP_VERSION __cplusplus +#endif + +#if defined(__GNUC__) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_PACK(decl) decl __attribute__((__packed__)) +#elif defined(_MSC_VER) +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_PACK(decl) __pragma(pack(push, 1)) decl __pragma(pack(pop)) +#endif + +#if ANKERL_UNORDERED_DENSE_CPP_VERSION < 201703L +# error ankerl::unordered_dense requires C++17 or higher +#else +# include // for array +# include // for uint64_t, uint32_t, uint8_t, UINT64_C +# include // for size_t, memcpy, memset +# include // for equal_to, hash +# include // for initializer_list +# include // for pair, distance +# include // for numeric_limits +# include // for allocator, allocator_traits, shared_ptr +# include // for out_of_range +# include // for basic_string +# include // for basic_string_view, hash +# include // for forward_as_tuple +# include // for enable_if_t, declval, conditional_t, ena... +# include // for forward, exchange, pair, as_const, piece... +# include // for vector + +# define ANKERL_UNORDERED_DENSE_PMR 0 // NOLINT(cppcoreguidelines-macro-usage) +# if defined(__has_include) +# if __has_include() +# undef ANKERL_UNORDERED_DENSE_PMR +# define ANKERL_UNORDERED_DENSE_PMR 1 // NOLINT(cppcoreguidelines-macro-usage) +# include // for polymorphic_allocator +# endif +# endif + +# if defined(_MSC_VER) && defined(_M_X64) +# include +# pragma intrinsic(_umul128) +# endif + +# if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) +# define ANKERL_UNORDERED_DENSE_LIKELY(x) __builtin_expect(x, 1) // NOLINT(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_UNLIKELY(x) __builtin_expect(x, 0) // NOLINT(cppcoreguidelines-macro-usage) +# else +# define ANKERL_UNORDERED_DENSE_LIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_UNLIKELY(x) (x) // NOLINT(cppcoreguidelines-macro-usage) +# endif + +namespace ankerl::unordered_dense { +inline namespace ANKERL_UNORDERED_DENSE_NAMESPACE { + +// hash /////////////////////////////////////////////////////////////////////// + +// This is a stripped-down implementation of wyhash: https://github.com/wangyi-fudan/wyhash +// No big-endian support (because different values on different machines don't matter), +// hardcodes seed and the secret, reformattes the code, and clang-tidy fixes. +namespace detail::wyhash { + +static inline void mum(uint64_t* a, uint64_t* b) { +# if defined(__SIZEOF_INT128__) + __uint128_t r = *a; + r *= *b; + *a = static_cast(r); + *b = static_cast(r >> 64U); +# elif defined(_MSC_VER) && defined(_M_X64) + *a = _umul128(*a, *b, b); +# else + uint64_t ha = *a >> 32U; + uint64_t hb = *b >> 32U; + uint64_t la = static_cast(*a); + uint64_t lb = static_cast(*b); + uint64_t hi{}; + uint64_t lo{}; + uint64_t rh = ha * hb; + uint64_t rm0 = ha * lb; + uint64_t rm1 = hb * la; + uint64_t rl = la * lb; + uint64_t t = rl + (rm0 << 32U); + auto c = static_cast(t < rl); + lo = t + (rm1 << 32U); + c += static_cast(lo < t); + hi = rh + (rm0 >> 32U) + (rm1 >> 32U) + c; + *a = lo; + *b = hi; +# endif +} + +// multiply and xor mix function, aka MUM +[[nodiscard]] static inline auto mix(uint64_t a, uint64_t b) -> uint64_t { + mum(&a, &b); + return a ^ b; +} + +// read functions. WARNING: we don't care about endianness, so results are different on big endian! +[[nodiscard]] static inline auto r8(const uint8_t* p) -> uint64_t { + uint64_t v{}; + std::memcpy(&v, p, 8U); + return v; +} + +[[nodiscard]] static inline auto r4(const uint8_t* p) -> uint64_t { + uint32_t v{}; + std::memcpy(&v, p, 4); + return v; +} + +// reads 1, 2, or 3 bytes +[[nodiscard]] static inline auto r3(const uint8_t* p, size_t k) -> uint64_t { + return (static_cast(p[0]) << 16U) | (static_cast(p[k >> 1U]) << 8U) | p[k - 1]; +} + +[[maybe_unused]] [[nodiscard]] static inline auto hash(void const* key, size_t len) -> uint64_t { + static constexpr auto secret = std::array{UINT64_C(0xa0761d6478bd642f), + UINT64_C(0xe7037ed1a0b428db), + UINT64_C(0x8ebc6af09c88c6e3), + UINT64_C(0x589965cc75374cc3)}; + + auto const* p = static_cast(key); + uint64_t seed = secret[0]; + uint64_t a{}; + uint64_t b{}; + if (ANKERL_UNORDERED_DENSE_LIKELY(len <= 16)) { + if (ANKERL_UNORDERED_DENSE_LIKELY(len >= 4)) { + a = (r4(p) << 32U) | r4(p + ((len >> 3U) << 2U)); + b = (r4(p + len - 4) << 32U) | r4(p + len - 4 - ((len >> 3U) << 2U)); + } else if (ANKERL_UNORDERED_DENSE_LIKELY(len > 0)) { + a = r3(p, len); + b = 0; + } else { + a = 0; + b = 0; + } + } else { + size_t i = len; + if (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 48)) { + uint64_t see1 = seed; + uint64_t see2 = seed; + do { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1); + see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2); + p += 48; + i -= 48; + } while (ANKERL_UNORDERED_DENSE_LIKELY(i > 48)); + seed ^= see1 ^ see2; + } + while (ANKERL_UNORDERED_DENSE_UNLIKELY(i > 16)) { + seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); + i -= 16; + p += 16; + } + a = r8(p + i - 16); + b = r8(p + i - 8); + } + + return mix(secret[1] ^ len, mix(a ^ secret[1], b ^ seed)); +} + +[[nodiscard]] static inline auto hash(uint64_t x) -> uint64_t { + return detail::wyhash::mix(x, UINT64_C(0x9E3779B97F4A7C15)); +} + +} // namespace detail::wyhash + +template +struct hash { + auto operator()(T const& obj) const noexcept(noexcept(std::declval>().operator()(std::declval()))) + -> uint64_t { + return std::hash{}(obj); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::basic_string const& str) const noexcept -> uint64_t { + return detail::wyhash::hash(str.data(), sizeof(CharT) * str.size()); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::basic_string_view const& sv) const noexcept -> uint64_t { + return detail::wyhash::hash(sv.data(), sizeof(CharT) * sv.size()); + } +}; + +template +struct hash { + using is_avalanching = void; + auto operator()(T* ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast(ptr)); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::unique_ptr const& ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast(ptr.get())); + } +}; + +template +struct hash> { + using is_avalanching = void; + auto operator()(std::shared_ptr const& ptr) const noexcept -> uint64_t { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return detail::wyhash::hash(reinterpret_cast(ptr.get())); + } +}; + +template +struct hash::value>::type> { + using is_avalanching = void; + auto operator()(Enum e) const noexcept -> uint64_t { + using underlying = typename std::underlying_type_t; + return detail::wyhash::hash(static_cast(e)); + } +}; + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +# define ANKERL_UNORDERED_DENSE_HASH_STATICCAST(T) \ + template <> \ + struct hash { \ + using is_avalanching = void; \ + auto operator()(T const& obj) const noexcept -> uint64_t { \ + return detail::wyhash::hash(static_cast(obj)); \ + } \ + } + +# if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +# endif +// see https://en.cppreference.com/w/cpp/utility/hash +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(bool); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(signed char); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned char); +# if ANKERL_UNORDERED_DENSE_CPP_VERSION >= 202002L +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char8_t); +# endif +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char16_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(char32_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(wchar_t); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(short); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned short); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(int); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned int); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(long long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long); +ANKERL_UNORDERED_DENSE_HASH_STATICCAST(unsigned long long); + +# if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +# endif + +// bucket_type ////////////////////////////////////////////////////////// + +namespace bucket_type { + +struct standard { + static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint + static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint + + uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash + uint32_t m_value_idx; // index into the m_values vector. +}; + +ANKERL_UNORDERED_DENSE_PACK(struct big { + static constexpr uint32_t dist_inc = 1U << 8U; // skip 1 byte fingerprint + static constexpr uint32_t fingerprint_mask = dist_inc - 1; // mask for 1 byte of fingerprint + + uint32_t m_dist_and_fingerprint; // upper 3 byte: distance to original bucket. lower byte: fingerprint from hash + size_t m_value_idx; // index into the m_values vector. +}); + +} // namespace bucket_type + +namespace detail { + +struct nonesuch {}; + +template class Op, class... Args> +struct detector { + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> { + using value_t = std::true_type; + using type = Op; +}; + +template